diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 40760d7272d..02c8bcb4c2e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e726eb401c2c1c882aa393071d5bb8bfb45e1dc3 +99bb2ff6a61428036323bb8734e92dad0daf950e diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 9a922769fbc..395418d7e52 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -26,7 +26,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.0.3 + uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.0.3 with: results_file: results.sarif results_format: sarif diff --git a/CODEOWNERS b/CODEOWNERS index cb9966fa3ae..d7c37efa2fb 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -62,8 +62,9 @@ packages/video_player/video_player_web/** @ditman packages/webview_flutter/webview_flutter_web/** @ditman # - Android -packages/camera/camera_android/** @camsim99 -packages/camera/camera_android_camerax/** @camsim99 +# TODO(matanlurey): Remove @matanlurey after https://github.com/flutter/flutter/issues/151018. +packages/camera/camera_android/** @camsim99 @matanlurey +packages/camera/camera_android_camerax/** @camsim99 @matanlurey packages/espresso/** @reidbaker packages/file_selector/file_selector_android/** @gmackall packages/flutter_plugin_android_lifecycle/** @reidbaker @@ -76,7 +77,7 @@ packages/path_provider/path_provider_android/** @camsim99 packages/quick_actions/quick_actions_android/** @camsim99 packages/shared_preferences/shared_preferences_android/** @reidbaker packages/url_launcher/url_launcher_android/** @gmackall -packages/video_player/video_player_android/** @camsim99 +packages/video_player/video_player_android/** @camsim99 @matanlurey # Owned by ecosystem team for now during the wrapper evaluation. packages/webview_flutter/webview_flutter_android/** @bparrishMines diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index 1e46c7c96e6..777f4913eeb 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.9+7 + +* Updates Android Gradle plugin to 8.5.0. + ## 0.10.9+6 * Reverts changes to support Impeller. diff --git a/packages/camera/camera_android/android/build.gradle b/packages/camera/camera_android/android/build.gradle index 2caee412af1..005d5d00985 100644 --- a/packages/camera/camera_android/android/build.gradle +++ b/packages/camera/camera_android/android/build.gradle @@ -9,7 +9,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.3.0' + classpath 'com.android.tools.build:gradle:8.5.0' } } diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 178c15de795..0dc0371c0e4 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -3,7 +3,7 @@ description: Android implementation of the camera plugin. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.9+6 +version: 0.10.9+7 environment: sdk: ^3.4.0 diff --git a/packages/flutter_markdown/CHANGELOG.md b/packages/flutter_markdown/CHANGELOG.md index f787fabbc05..2b42efadc5f 100644 --- a/packages/flutter_markdown/CHANGELOG.md +++ b/packages/flutter_markdown/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.7.3 + +* Adds horizontal scrolling for table when using `tableColumnWidth: FixedColumnWidth(width)`. + ## 0.7.2+1 * Fixes a crash caused by text selection when `selectable` is true and `onSelectionChanged` is null. diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart index 8e7c98abdc2..0706453a01d 100644 --- a/packages/flutter_markdown/lib/src/builder.dart +++ b/packages/flutter_markdown/lib/src/builder.dart @@ -438,12 +438,20 @@ class MarkdownBuilder implements md.NodeVisitor { ); } } else if (tag == 'table') { - child = Table( - defaultColumnWidth: styleSheet.tableColumnWidth!, - defaultVerticalAlignment: styleSheet.tableVerticalAlignment, - border: styleSheet.tableBorder, - children: _tables.removeLast().rows, - ); + if (styleSheet.tableColumnWidth is FixedColumnWidth) { + final ScrollController tableScrollController = ScrollController(); + child = Scrollbar( + controller: tableScrollController, + child: SingleChildScrollView( + controller: tableScrollController, + scrollDirection: Axis.horizontal, + padding: styleSheet.tablePadding, + child: _buildTable(), + ), + ); + } else { + child = _buildTable(); + } } else if (tag == 'blockquote') { _isInBlockquote = false; child = DecoratedBox( @@ -558,6 +566,15 @@ class MarkdownBuilder implements md.NodeVisitor { _lastVisitedTag = tag; } + Table _buildTable() { + return Table( + defaultColumnWidth: styleSheet.tableColumnWidth!, + defaultVerticalAlignment: styleSheet.tableVerticalAlignment, + border: styleSheet.tableBorder, + children: _tables.removeLast().rows, + ); + } + Widget _buildImage(String src, String? title, String? alt) { final List parts = src.split('#'); if (parts.isEmpty) { diff --git a/packages/flutter_markdown/lib/src/style_sheet.dart b/packages/flutter_markdown/lib/src/style_sheet.dart index f3b570aeb91..3923e618ff3 100644 --- a/packages/flutter_markdown/lib/src/style_sheet.dart +++ b/packages/flutter_markdown/lib/src/style_sheet.dart @@ -38,6 +38,7 @@ class MarkdownStyleSheet { this.tableHead, this.tableBody, this.tableHeadAlign, + this.tablePadding, this.tableBorder, this.tableColumnWidth, this.tableCellsPadding, @@ -134,6 +135,7 @@ class MarkdownStyleSheet { tableHead: const TextStyle(fontWeight: FontWeight.w600), tableBody: theme.textTheme.bodyMedium, tableHeadAlign: TextAlign.center, + tablePadding: const EdgeInsets.only(bottom: 4.0), tableBorder: TableBorder.all( color: theme.dividerColor, ), @@ -231,6 +233,7 @@ class MarkdownStyleSheet { ), tableBody: theme.textTheme.textStyle, tableHeadAlign: TextAlign.center, + tablePadding: const EdgeInsets.only(bottom: 8), tableBorder: TableBorder.all(color: CupertinoColors.separator, width: 0), tableColumnWidth: const FlexColumnWidth(), tableCellsPadding: const EdgeInsets.fromLTRB(16, 8, 16, 8), @@ -312,6 +315,7 @@ class MarkdownStyleSheet { tableHead: const TextStyle(fontWeight: FontWeight.w600), tableBody: theme.textTheme.bodyMedium, tableHeadAlign: TextAlign.center, + tablePadding: const EdgeInsets.only(bottom: 4.0), tableBorder: TableBorder.all( color: theme.dividerColor, ), @@ -371,6 +375,7 @@ class MarkdownStyleSheet { TextStyle? tableHead, TextStyle? tableBody, TextAlign? tableHeadAlign, + EdgeInsets? tablePadding, TableBorder? tableBorder, TableColumnWidth? tableColumnWidth, EdgeInsets? tableCellsPadding, @@ -436,6 +441,7 @@ class MarkdownStyleSheet { tableHead: tableHead ?? this.tableHead, tableBody: tableBody ?? this.tableBody, tableHeadAlign: tableHeadAlign ?? this.tableHeadAlign, + tablePadding: tablePadding ?? this.tablePadding, tableBorder: tableBorder ?? this.tableBorder, tableColumnWidth: tableColumnWidth ?? this.tableColumnWidth, tableCellsPadding: tableCellsPadding ?? this.tableCellsPadding, @@ -502,6 +508,7 @@ class MarkdownStyleSheet { tableHead: tableHead!.merge(other.tableHead), tableBody: tableBody!.merge(other.tableBody), tableHeadAlign: other.tableHeadAlign, + tablePadding: other.tablePadding, tableBorder: other.tableBorder, tableColumnWidth: other.tableColumnWidth, tableCellsPadding: other.tableCellsPadding, @@ -620,6 +627,9 @@ class MarkdownStyleSheet { /// The [TextAlign] to use for `th` elements. final TextAlign? tableHeadAlign; + /// The padding to use for `table` elements. + final EdgeInsets? tablePadding; + /// The [TableBorder] to use for `table` elements. final TableBorder? tableBorder; @@ -740,6 +750,7 @@ class MarkdownStyleSheet { other.tableHead == tableHead && other.tableBody == tableBody && other.tableHeadAlign == tableHeadAlign && + other.tablePadding == tablePadding && other.tableBorder == tableBorder && other.tableColumnWidth == tableColumnWidth && other.tableCellsPadding == tableCellsPadding && @@ -798,6 +809,7 @@ class MarkdownStyleSheet { tableHead, tableBody, tableHeadAlign, + tablePadding, tableBorder, tableColumnWidth, tableCellsPadding, diff --git a/packages/flutter_markdown/pubspec.yaml b/packages/flutter_markdown/pubspec.yaml index f7b58ed2888..ecfd21ea000 100644 --- a/packages/flutter_markdown/pubspec.yaml +++ b/packages/flutter_markdown/pubspec.yaml @@ -4,7 +4,7 @@ description: A Markdown renderer for Flutter. Create rich text output, formatted with simple Markdown tags. repository: https://github.com/flutter/packages/tree/main/packages/flutter_markdown issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_markdown%22 -version: 0.7.2+1 +version: 0.7.3 environment: sdk: ^3.3.0 diff --git a/packages/flutter_markdown/test/scrollable_test.dart b/packages/flutter_markdown/test/scrollable_test.dart index 23092328f00..1042a6fa98a 100644 --- a/packages/flutter_markdown/test/scrollable_test.dart +++ b/packages/flutter_markdown/test/scrollable_test.dart @@ -110,5 +110,69 @@ void defineTests() { ]); }, ); + + testWidgets( + 'table', + (WidgetTester tester) async { + const String data = '|Header 1|Header 2|Header 3|' + '\n|-----|-----|-----|' + '\n|Col 1|Col 2|Col 3|'; + await tester.pumpWidget( + boilerplate( + MediaQuery( + data: const MediaQueryData(), + child: MarkdownBody( + data: data, + styleSheet: MarkdownStyleSheet( + tableColumnWidth: const FixedColumnWidth(150), + ), + ), + ), + ), + ); + + final Iterable widgets = tester.allWidgets; + final Iterable scrollViews = + widgets.whereType(); + expect(scrollViews, isNotEmpty); + expect(scrollViews.first.controller, isNotNull); + }, + ); + + testWidgets( + 'two tables use different scroll controllers', + (WidgetTester tester) async { + const String data = '|Header 1|Header 2|Header 3|' + '\n|-----|-----|-----|' + '\n|Col 1|Col 2|Col 3|' + '\n' + '\n|Header 1|Header 2|Header 3|' + '\n|-----|-----|-----|' + '\n|Col 1|Col 2|Col 3|'; + + await tester.pumpWidget( + boilerplate( + MediaQuery( + data: const MediaQueryData(), + child: MarkdownBody( + data: data, + styleSheet: MarkdownStyleSheet( + tableColumnWidth: const FixedColumnWidth(150), + ), + ), + ), + ), + ); + + final Iterable widgets = tester.allWidgets; + final Iterable scrollViews = + widgets.whereType(); + expect(scrollViews, hasLength(2)); + expect(scrollViews.first.controller, isNotNull); + expect(scrollViews.last.controller, isNotNull); + expect(scrollViews.first.controller, + isNot(equals(scrollViews.last.controller))); + }, + ); }); } 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 2291ed28c47..7a1833338bf 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.11.0 + +* Converts additional platform calls to Pigeon. + ## 2.10.0 * Converts some platform calls to Pigeon. diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CirclesController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CirclesController.java index d128d9544b1..2b52641caa0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CirclesController.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CirclesController.java @@ -4,6 +4,7 @@ package io.flutter.plugins.googlemaps; +import androidx.annotation.NonNull; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.model.Circle; import com.google.android.gms.maps.model.CircleOptions; @@ -31,31 +32,28 @@ void setGoogleMap(GoogleMap googleMap) { this.googleMap = googleMap; } - void addCircles(List circlesToAdd) { + void addJsonCircles(List circlesToAdd) { if (circlesToAdd != null) { for (Object circleToAdd : circlesToAdd) { - addCircle(circleToAdd); + addJsonCircle(circleToAdd); } } } - void changeCircles(List circlesToChange) { - if (circlesToChange != null) { - for (Object circleToChange : circlesToChange) { - changeCircle(circleToChange); - } + void addCircles(@NonNull List circlesToAdd) { + for (Messages.PlatformCircle circleToAdd : circlesToAdd) { + addJsonCircle(circleToAdd.getJson()); } } - void removeCircles(List circleIdsToRemove) { - if (circleIdsToRemove == null) { - return; + void changeCircles(@NonNull List circlesToChange) { + for (Object circleToChange : circlesToChange) { + changeCircle(circleToChange); } - for (Object rawCircleId : circleIdsToRemove) { - if (rawCircleId == null) { - continue; - } - String circleId = (String) rawCircleId; + } + + void removeCircles(@NonNull List circleIdsToRemove) { + for (String circleId : circleIdsToRemove) { final CircleController circleController = circleIdToController.remove(circleId); if (circleController != null) { circleController.remove(); @@ -77,7 +75,7 @@ boolean onCircleTap(String googleCircleId) { return false; } - private void addCircle(Object circle) { + private void addJsonCircle(Object circle) { if (circle == null) { return; } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/ClusterManagersController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/ClusterManagersController.java index 09ee5d2416f..16666ac0dc3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/ClusterManagersController.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/ClusterManagersController.java @@ -79,18 +79,25 @@ private void initListenersForClusterManager( } /** Adds new ClusterManagers to the controller. */ - void addClusterManagers(@NonNull List clusterManagersToAdd) { + void addJsonClusterManagers(@NonNull List clusterManagersToAdd) { for (Object clusterToAdd : clusterManagersToAdd) { - addClusterManager(clusterToAdd); + String clusterManagerId = getClusterManagerId(clusterToAdd); + if (clusterManagerId == null) { + throw new IllegalArgumentException("clusterManagerId was null"); + } + addClusterManager(clusterManagerId); } } - /** Adds new ClusterManager to the controller. */ - void addClusterManager(Object clusterManagerData) { - String clusterManagerId = getClusterManagerId(clusterManagerData); - if (clusterManagerId == null) { - throw new IllegalArgumentException("clusterManagerId was null"); + /** Adds new ClusterManagers to the controller. */ + void addClusterManagers(@NonNull List clusterManagersToAdd) { + for (Messages.PlatformClusterManager clusterToAdd : clusterManagersToAdd) { + addClusterManager(clusterToAdd.getIdentifier()); } + } + + /** Adds new ClusterManager to the controller. */ + void addClusterManager(String clusterManagerId) { ClusterManager clusterManager = new ClusterManager(context, googleMap, markerManager); ClusterRenderer clusterRenderer = @@ -101,12 +108,8 @@ void addClusterManager(Object clusterManagerData) { } /** Removes ClusterManagers by given cluster manager IDs from the controller. */ - public void removeClusterManagers(@NonNull List clusterManagerIdsToRemove) { - for (Object rawClusterManagerId : clusterManagerIdsToRemove) { - if (rawClusterManagerId == null) { - continue; - } - String clusterManagerId = (String) rawClusterManagerId; + public void removeClusterManagers(@NonNull List clusterManagerIdsToRemove) { + for (String clusterManagerId : clusterManagerIdsToRemove) { removeClusterManager(clusterManagerId); } } 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 e1817597f16..71455b668e8 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 @@ -9,9 +9,11 @@ import android.graphics.BitmapFactory; import android.graphics.Point; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.google.android.gms.maps.CameraUpdate; import com.google.android.gms.maps.CameraUpdateFactory; +import com.google.android.gms.maps.MapsInitializer; import com.google.android.gms.maps.model.BitmapDescriptor; import com.google.android.gms.maps.model.BitmapDescriptorFactory; import com.google.android.gms.maps.model.ButtCap; @@ -357,6 +359,20 @@ private static int toInt(Object o) { return ((Number) o).intValue(); } + static @Nullable MapsInitializer.Renderer toMapRendererType( + @Nullable Messages.PlatformRendererType type) { + if (type == null) { + return null; + } + switch (type) { + case LATEST: + return MapsInitializer.Renderer.LATEST; + case LEGACY: + return MapsInitializer.Renderer.LEGACY; + } + return null; + } + static Object cameraPositionToJson(CameraPosition position) { if (position == null) { return null; diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java index e3eee3bc1a7..d2b6b082a27 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java @@ -26,7 +26,6 @@ import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; -import com.google.android.gms.maps.CameraUpdate; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.GoogleMapOptions; import com.google.android.gms.maps.MapView; @@ -45,7 +44,6 @@ import com.google.maps.android.collections.MarkerManager; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.platform.PlatformView; import io.flutter.plugins.googlemaps.Messages.FlutterError; @@ -70,7 +68,6 @@ class GoogleMapController GoogleMapOptionsSink, MapsApi, MapsInspectorApi, - MethodChannel.MethodCallHandler, OnMapReadyCallback, PlatformView { @@ -126,7 +123,6 @@ class GoogleMapController this.binaryMessenger = binaryMessenger; methodChannel = new MethodChannel(binaryMessenger, "plugins.flutter.dev/google_maps_android_" + id); - methodChannel.setMethodCallHandler(this); MapsApi.setUp(binaryMessenger, Integer.toString(id), this); MapsInspectorApi.setUp(binaryMessenger, Integer.toString(id), this); AssetManager assetManager = context.getAssets(); @@ -186,14 +182,6 @@ void init() { mapView.getMapAsync(this); } - private void moveCamera(CameraUpdate cameraUpdate) { - googleMap.moveCamera(cameraUpdate); - } - - private void animateCamera(CameraUpdate cameraUpdate) { - googleMap.animateCamera(cameraUpdate); - } - private CameraPosition getCameraPosition() { return trackCameraPosition ? googleMap.getCameraPosition() : null; } @@ -308,104 +296,6 @@ public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) { }); } - @Override - public void onMethodCall(MethodCall call, @NonNull MethodChannel.Result result) { - switch (call.method) { - case "map#update": - { - Convert.interpretGoogleMapOptions(call.argument("options"), this); - result.success(Convert.cameraPositionToJson(getCameraPosition())); - break; - } - case "camera#move": - { - final CameraUpdate cameraUpdate = - Convert.toCameraUpdate(call.argument("cameraUpdate"), density); - moveCamera(cameraUpdate); - result.success(null); - break; - } - case "camera#animate": - { - final CameraUpdate cameraUpdate = - Convert.toCameraUpdate(call.argument("cameraUpdate"), density); - animateCamera(cameraUpdate); - result.success(null); - break; - } - case "markers#update": - { - List markersToAdd = call.argument("markersToAdd"); - markersController.addMarkers(markersToAdd); - List markersToChange = call.argument("markersToChange"); - markersController.changeMarkers(markersToChange); - List markerIdsToRemove = call.argument("markerIdsToRemove"); - markersController.removeMarkers(markerIdsToRemove); - result.success(null); - break; - } - case "clusterManagers#update": - { - List clusterManagersToAdd = call.argument("clusterManagersToAdd"); - if (clusterManagersToAdd != null) { - clusterManagersController.addClusterManagers(clusterManagersToAdd); - } - List clusterManagerIdsToRemove = call.argument("clusterManagerIdsToRemove"); - if (clusterManagerIdsToRemove != null) { - clusterManagersController.removeClusterManagers(clusterManagerIdsToRemove); - } - result.success(null); - break; - } - case "polygons#update": - { - List polygonsToAdd = call.argument("polygonsToAdd"); - polygonsController.addPolygons(polygonsToAdd); - List polygonsToChange = call.argument("polygonsToChange"); - polygonsController.changePolygons(polygonsToChange); - List polygonIdsToRemove = call.argument("polygonIdsToRemove"); - polygonsController.removePolygons(polygonIdsToRemove); - result.success(null); - break; - } - case "polylines#update": - { - List polylinesToAdd = call.argument("polylinesToAdd"); - polylinesController.addPolylines(polylinesToAdd); - List polylinesToChange = call.argument("polylinesToChange"); - polylinesController.changePolylines(polylinesToChange); - List polylineIdsToRemove = call.argument("polylineIdsToRemove"); - polylinesController.removePolylines(polylineIdsToRemove); - result.success(null); - break; - } - case "circles#update": - { - List circlesToAdd = call.argument("circlesToAdd"); - circlesController.addCircles(circlesToAdd); - List circlesToChange = call.argument("circlesToChange"); - circlesController.changeCircles(circlesToChange); - List circleIdsToRemove = call.argument("circleIdsToRemove"); - circlesController.removeCircles(circleIdsToRemove); - result.success(null); - break; - } - case "tileOverlays#update": - { - List> tileOverlaysToAdd = call.argument("tileOverlaysToAdd"); - tileOverlaysController.addTileOverlays(tileOverlaysToAdd); - List> tileOverlaysToChange = call.argument("tileOverlaysToChange"); - tileOverlaysController.changeTileOverlays(tileOverlaysToChange); - List tileOverlaysToRemove = call.argument("tileOverlayIdsToRemove"); - tileOverlaysController.removeTileOverlays(tileOverlaysToRemove); - result.success(null); - break; - } - default: - result.notImplemented(); - } - } - @Override public void onMapClick(@NonNull LatLng latLng) { final Map arguments = new HashMap<>(2); @@ -490,7 +380,6 @@ public void dispose() { return; } disposed = true; - methodChannel.setMethodCallHandler(null); MapsApi.setUp(binaryMessenger, Integer.toString(id), null); MapsInspectorApi.setUp(binaryMessenger, Integer.toString(id), null); setGoogleMapListener(null); @@ -753,7 +642,7 @@ public void setInitialMarkers(Object initialMarkers) { } private void updateInitialMarkers() { - markersController.addMarkers(initialMarkers); + markersController.addJsonMarkers(initialMarkers); } @Override @@ -767,7 +656,7 @@ public void setInitialClusterManagers(Object initialClusterManagers) { private void updateInitialClusterManagers() { if (initialClusterManagers != null) { - clusterManagersController.addClusterManagers(initialClusterManagers); + clusterManagersController.addJsonClusterManagers(initialClusterManagers); } } @@ -781,7 +670,7 @@ public void setInitialPolygons(Object initialPolygons) { } private void updateInitialPolygons() { - polygonsController.addPolygons(initialPolygons); + polygonsController.addJsonPolygons(initialPolygons); } @Override @@ -794,7 +683,7 @@ public void setInitialPolylines(Object initialPolylines) { } private void updateInitialPolylines() { - polylinesController.addPolylines(initialPolylines); + polylinesController.addJsonPolylines(initialPolylines); } @Override @@ -807,7 +696,7 @@ public void setInitialCircles(Object initialCircles) { } private void updateInitialCircles() { - circlesController.addCircles(initialCircles); + circlesController.addJsonCircles(initialCircles); } @Override @@ -819,7 +708,7 @@ public void setInitialTileOverlays(List> initialTileOverlays) { } private void updateInitialTileOverlays() { - tileOverlaysController.addTileOverlays(initialTileOverlays); + tileOverlaysController.addJsonTileOverlays(initialTileOverlays); } @SuppressLint("MissingPermission") @@ -914,6 +803,68 @@ public void waitForMap(@NonNull Messages.VoidResult result) { } } + @Override + public void updateMapConfiguration(@NonNull Messages.PlatformMapConfiguration configuration) { + Convert.interpretGoogleMapOptions(configuration.getJson(), this); + } + + @Override + public void updateCircles( + @NonNull List toAdd, + @NonNull List toChange, + @NonNull List idsToRemove) { + circlesController.addCircles(toAdd); + circlesController.changeCircles(toChange); + circlesController.removeCircles(idsToRemove); + } + + @Override + public void updateClusterManagers( + @NonNull List toAdd, @NonNull List idsToRemove) { + clusterManagersController.addClusterManagers(toAdd); + clusterManagersController.removeClusterManagers(idsToRemove); + } + + @Override + public void updateMarkers( + @NonNull List toAdd, + @NonNull List toChange, + @NonNull List idsToRemove) { + markersController.addMarkers(toAdd); + markersController.changeMarkers(toChange); + markersController.removeMarkers(idsToRemove); + } + + @Override + public void updatePolygons( + @NonNull List toAdd, + @NonNull List toChange, + @NonNull List idsToRemove) { + polygonsController.addPolygons(toAdd); + polygonsController.changePolygons(toChange); + polygonsController.removePolygons(idsToRemove); + } + + @Override + public void updatePolylines( + @NonNull List toAdd, + @NonNull List toChange, + @NonNull List idsToRemove) { + polylinesController.addPolylines(toAdd); + polylinesController.changePolylines(toChange); + polylinesController.removePolylines(idsToRemove); + } + + @Override + public void updateTileOverlays( + @NonNull List toAdd, + @NonNull List toChange, + @NonNull List idsToRemove) { + tileOverlaysController.addTileOverlays(toAdd); + tileOverlaysController.changeTileOverlays(toChange); + tileOverlaysController.removeTileOverlays(idsToRemove); + } + @Override public @NonNull Messages.PlatformPoint getScreenCoordinate( @NonNull Messages.PlatformLatLng latLng) { @@ -950,6 +901,24 @@ public void waitForMap(@NonNull Messages.VoidResult result) { return Convert.latLngBoundsToPigeon(latLngBounds); } + @Override + public void moveCamera(@NonNull Messages.PlatformCameraUpdate cameraUpdate) { + if (googleMap == null) { + throw new FlutterError( + "GoogleMap uninitialized", "moveCamera called prior to map initialization", null); + } + googleMap.moveCamera(Convert.toCameraUpdate(cameraUpdate.getJson(), density)); + } + + @Override + public void animateCamera(@NonNull Messages.PlatformCameraUpdate cameraUpdate) { + if (googleMap == null) { + throw new FlutterError( + "GoogleMap uninitialized", "animateCamera called prior to map initialization", null); + } + googleMap.animateCamera(Convert.toCameraUpdate(cameraUpdate.getJson(), density)); + } + @Override public @NonNull Double getZoomLevel() { if (googleMap == null) { diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapInitializer.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapInitializer.java index a113c0a1c4c..e4bea6b8ffa 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapInitializer.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapInitializer.java @@ -5,74 +5,39 @@ package io.flutter.plugins.googlemaps; import android.content.Context; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.google.android.gms.maps.MapsInitializer; -import com.google.android.gms.maps.MapsInitializer.Renderer; import com.google.android.gms.maps.OnMapsSdkInitializedCallback; import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; /** GoogleMaps initializer used to initialize the Google Maps SDK with preferred settings. */ final class GoogleMapInitializer - implements OnMapsSdkInitializedCallback, MethodChannel.MethodCallHandler { - private final MethodChannel methodChannel; + implements OnMapsSdkInitializedCallback, Messages.MapsInitializerApi { private final Context context; - private static MethodChannel.Result initializationResult; + private static Messages.Result initializationResult; private boolean rendererInitialized = false; GoogleMapInitializer(Context context, BinaryMessenger binaryMessenger) { this.context = context; - methodChannel = - new MethodChannel(binaryMessenger, "plugins.flutter.dev/google_maps_android_initializer"); - methodChannel.setMethodCallHandler(this); + Messages.MapsInitializerApi.setUp(binaryMessenger, this); } @Override - public void onMethodCall(MethodCall call, MethodChannel.Result result) { - switch (call.method) { - case "initializer#preferRenderer": - { - String preferredRenderer = (String) call.argument("value"); - initializeWithPreferredRenderer(preferredRenderer, result); - break; - } - default: - result.notImplemented(); - } - } - - /** - * Initializes map renderer to with preferred renderer type. Renderer can be initialized only once - * per application context. - * - *

Supported renderer types are "latest", "legacy" and "default". - */ - private void initializeWithPreferredRenderer( - String preferredRenderer, MethodChannel.Result result) { + public void initializeWithPreferredRenderer( + @Nullable Messages.PlatformRendererType type, + @NonNull Messages.Result result) { if (rendererInitialized || initializationResult != null) { result.error( - "Renderer already initialized", "Renderer initialization called multiple times", null); + new Messages.FlutterError( + "Renderer already initialized", + "Renderer initialization called multiple times", + null)); } else { initializationResult = result; - switch (preferredRenderer) { - case "latest": - initializeWithRendererRequest(Renderer.LATEST); - break; - case "legacy": - initializeWithRendererRequest(Renderer.LEGACY); - break; - case "default": - initializeWithRendererRequest(null); - break; - default: - initializationResult.error( - "Invalid renderer type", - "Renderer initialization called with invalid renderer type", - null); - initializationResult = null; - } + initializeWithRendererRequest(Convert.toMapRendererType(type)); } } @@ -83,25 +48,28 @@ private void initializeWithPreferredRenderer( * class. */ @VisibleForTesting - public void initializeWithRendererRequest(MapsInitializer.Renderer renderer) { + public void initializeWithRendererRequest(@Nullable MapsInitializer.Renderer renderer) { MapsInitializer.initialize(context, renderer, this); } /** Is called by Google Maps SDK to determine which version of the renderer was initialized. */ @Override - public void onMapsSdkInitialized(MapsInitializer.Renderer renderer) { + public void onMapsSdkInitialized(@NonNull MapsInitializer.Renderer renderer) { rendererInitialized = true; if (initializationResult != null) { switch (renderer) { case LATEST: - initializationResult.success("latest"); + initializationResult.success(Messages.PlatformRendererType.LATEST); break; case LEGACY: - initializationResult.success("legacy"); + initializationResult.success(Messages.PlatformRendererType.LEGACY); break; default: initializationResult.error( - "Unknown renderer type", "Initialized with unknown renderer type", null); + new Messages.FlutterError( + "Unknown renderer type", + "Initialized with unknown renderer type", + renderer.name())); } initializationResult = null; } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkersController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkersController.java index 0f47fecaa50..5d870e88081 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkersController.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkersController.java @@ -5,6 +5,7 @@ package io.flutter.plugins.googlemaps; import android.content.res.AssetManager; +import androidx.annotation.NonNull; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.MarkerOptions; @@ -43,31 +44,28 @@ void setCollection(MarkerManager.Collection markerCollection) { this.markerCollection = markerCollection; } - void addMarkers(List markersToAdd) { + void addJsonMarkers(List markersToAdd) { if (markersToAdd != null) { for (Object markerToAdd : markersToAdd) { - addMarker(markerToAdd); + addJsonMarker(markerToAdd); } } } - void changeMarkers(List markersToChange) { - if (markersToChange != null) { - for (Object markerToChange : markersToChange) { - changeMarker(markerToChange); - } + void addMarkers(@NonNull List markersToAdd) { + for (Messages.PlatformMarker markerToAdd : markersToAdd) { + addJsonMarker(markerToAdd.getJson()); } } - void removeMarkers(List markerIdsToRemove) { - if (markerIdsToRemove == null) { - return; + void changeMarkers(@NonNull List markersToChange) { + for (Messages.PlatformMarker markerToChange : markersToChange) { + changeJsonMarker(markerToChange.getJson()); } - for (Object rawMarkerId : markerIdsToRemove) { - if (rawMarkerId == null) { - continue; - } - String markerId = (String) rawMarkerId; + } + + void removeMarkers(@NonNull List markerIdsToRemove) { + for (String markerId : markerIdsToRemove) { removeMarker(markerId); } } @@ -188,7 +186,7 @@ public void onClusterItemRendered(MarkerBuilder markerBuilder, Marker marker) { } } - private void addMarker(Object marker) { + private void addJsonMarker(Object marker) { if (marker == null) { return; } @@ -234,7 +232,7 @@ private void createControllerForMarker(String markerId, Marker marker, boolean c googleMapsMarkerIdToDartMarkerId.put(marker.getId(), markerId); } - private void changeMarker(Object marker) { + private void changeJsonMarker(Object marker) { if (marker == null) { return; } @@ -252,7 +250,7 @@ private void changeMarker(Object marker) { // be removed and re-added to update its cluster manager state. if (!(Objects.equals(clusterManagerId, oldClusterManagerId))) { removeMarker(markerId); - addMarker(marker); + addJsonMarker(marker); return; } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Messages.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Messages.java index 5c537c9c3bf..ba47fa72d2c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Messages.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Messages.java @@ -64,6 +64,420 @@ protected static ArrayList wrapError(@NonNull Throwable exception) { @Retention(CLASS) @interface CanIgnoreReturnValue {} + public enum PlatformRendererType { + LEGACY(0), + LATEST(1); + + final int index; + + private PlatformRendererType(final int index) { + this.index = index; + } + } + + /** + * Pigeon representation of a CameraUpdate. + * + *

Generated class from Pigeon that represents data sent in messages. + */ + public static final class PlatformCameraUpdate { + /** + * The update data, as JSON. This should only be set from CameraUpdate.toJson, and the native + * code must intepret it according to the internal implementation details of the CameraUpdate + * class. + */ + private @NonNull Object json; + + public @NonNull Object getJson() { + return json; + } + + public void setJson(@NonNull Object setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"json\" is null."); + } + this.json = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + PlatformCameraUpdate() {} + + public static final class Builder { + + private @Nullable Object json; + + @CanIgnoreReturnValue + public @NonNull Builder setJson(@NonNull Object setterArg) { + this.json = setterArg; + return this; + } + + public @NonNull PlatformCameraUpdate build() { + PlatformCameraUpdate pigeonReturn = new PlatformCameraUpdate(); + pigeonReturn.setJson(json); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(json); + return toListResult; + } + + static @NonNull PlatformCameraUpdate fromList(@NonNull ArrayList __pigeon_list) { + PlatformCameraUpdate pigeonResult = new PlatformCameraUpdate(); + Object json = __pigeon_list.get(0); + pigeonResult.setJson(json); + return pigeonResult; + } + } + + /** + * Pigeon equivalent of the Circle class. + * + *

Generated class from Pigeon that represents data sent in messages. + */ + public static final class PlatformCircle { + /** + * The circle data, as JSON. This should only be set from Circle.toJson, and the native code + * must intepret it according to the internal implementation details of that method. + */ + private @NonNull Object json; + + public @NonNull Object getJson() { + return json; + } + + public void setJson(@NonNull Object setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"json\" is null."); + } + this.json = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + PlatformCircle() {} + + public static final class Builder { + + private @Nullable Object json; + + @CanIgnoreReturnValue + public @NonNull Builder setJson(@NonNull Object setterArg) { + this.json = setterArg; + return this; + } + + public @NonNull PlatformCircle build() { + PlatformCircle pigeonReturn = new PlatformCircle(); + pigeonReturn.setJson(json); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(json); + return toListResult; + } + + static @NonNull PlatformCircle fromList(@NonNull ArrayList __pigeon_list) { + PlatformCircle pigeonResult = new PlatformCircle(); + Object json = __pigeon_list.get(0); + pigeonResult.setJson(json); + return pigeonResult; + } + } + + /** + * Pigeon equivalent of the ClusterManager class. + * + *

Generated class from Pigeon that represents data sent in messages. + */ + public static final class PlatformClusterManager { + private @NonNull String identifier; + + public @NonNull String getIdentifier() { + return identifier; + } + + public void setIdentifier(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"identifier\" is null."); + } + this.identifier = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + PlatformClusterManager() {} + + public static final class Builder { + + private @Nullable String identifier; + + @CanIgnoreReturnValue + public @NonNull Builder setIdentifier(@NonNull String setterArg) { + this.identifier = setterArg; + return this; + } + + public @NonNull PlatformClusterManager build() { + PlatformClusterManager pigeonReturn = new PlatformClusterManager(); + pigeonReturn.setIdentifier(identifier); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(identifier); + return toListResult; + } + + static @NonNull PlatformClusterManager fromList(@NonNull ArrayList __pigeon_list) { + PlatformClusterManager pigeonResult = new PlatformClusterManager(); + Object identifier = __pigeon_list.get(0); + pigeonResult.setIdentifier((String) identifier); + return pigeonResult; + } + } + + /** + * Pigeon equivalent of the Marker class. + * + *

Generated class from Pigeon that represents data sent in messages. + */ + public static final class PlatformMarker { + /** + * The marker data, as JSON. This should only be set from Marker.toJson, and the native code + * must intepret it according to the internal implementation details of that method. + */ + private @NonNull Object json; + + public @NonNull Object getJson() { + return json; + } + + public void setJson(@NonNull Object setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"json\" is null."); + } + this.json = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + PlatformMarker() {} + + public static final class Builder { + + private @Nullable Object json; + + @CanIgnoreReturnValue + public @NonNull Builder setJson(@NonNull Object setterArg) { + this.json = setterArg; + return this; + } + + public @NonNull PlatformMarker build() { + PlatformMarker pigeonReturn = new PlatformMarker(); + pigeonReturn.setJson(json); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(json); + return toListResult; + } + + static @NonNull PlatformMarker fromList(@NonNull ArrayList __pigeon_list) { + PlatformMarker pigeonResult = new PlatformMarker(); + Object json = __pigeon_list.get(0); + pigeonResult.setJson(json); + return pigeonResult; + } + } + + /** + * Pigeon equivalent of the Polygon class. + * + *

Generated class from Pigeon that represents data sent in messages. + */ + public static final class PlatformPolygon { + /** + * The polygon data, as JSON. This should only be set from Polygon.toJson, and the native code + * must intepret it according to the internal implementation details of that method. + */ + private @NonNull Object json; + + public @NonNull Object getJson() { + return json; + } + + public void setJson(@NonNull Object setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"json\" is null."); + } + this.json = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + PlatformPolygon() {} + + public static final class Builder { + + private @Nullable Object json; + + @CanIgnoreReturnValue + public @NonNull Builder setJson(@NonNull Object setterArg) { + this.json = setterArg; + return this; + } + + public @NonNull PlatformPolygon build() { + PlatformPolygon pigeonReturn = new PlatformPolygon(); + pigeonReturn.setJson(json); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(json); + return toListResult; + } + + static @NonNull PlatformPolygon fromList(@NonNull ArrayList __pigeon_list) { + PlatformPolygon pigeonResult = new PlatformPolygon(); + Object json = __pigeon_list.get(0); + pigeonResult.setJson(json); + return pigeonResult; + } + } + + /** + * Pigeon equivalent of the Polyline class. + * + *

Generated class from Pigeon that represents data sent in messages. + */ + public static final class PlatformPolyline { + /** + * The polyline data, as JSON. This should only be set from Polyline.toJson, and the native code + * must intepret it according to the internal implementation details of that method. + */ + private @NonNull Object json; + + public @NonNull Object getJson() { + return json; + } + + public void setJson(@NonNull Object setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"json\" is null."); + } + this.json = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + PlatformPolyline() {} + + public static final class Builder { + + private @Nullable Object json; + + @CanIgnoreReturnValue + public @NonNull Builder setJson(@NonNull Object setterArg) { + this.json = setterArg; + return this; + } + + public @NonNull PlatformPolyline build() { + PlatformPolyline pigeonReturn = new PlatformPolyline(); + pigeonReturn.setJson(json); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(json); + return toListResult; + } + + static @NonNull PlatformPolyline fromList(@NonNull ArrayList __pigeon_list) { + PlatformPolyline pigeonResult = new PlatformPolyline(); + Object json = __pigeon_list.get(0); + pigeonResult.setJson(json); + return pigeonResult; + } + } + + /** + * Pigeon equivalent of the TileOverlay class. + * + *

Generated class from Pigeon that represents data sent in messages. + */ + public static final class PlatformTileOverlay { + /** + * The tile overlay data, as JSON. This should only be set from TileOverlay.toJson, and the + * native code must intepret it according to the internal implementation details of that method. + */ + private @NonNull Object json; + + public @NonNull Object getJson() { + return json; + } + + public void setJson(@NonNull Object setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"json\" is null."); + } + this.json = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + PlatformTileOverlay() {} + + public static final class Builder { + + private @Nullable Object json; + + @CanIgnoreReturnValue + public @NonNull Builder setJson(@NonNull Object setterArg) { + this.json = setterArg; + return this; + } + + public @NonNull PlatformTileOverlay build() { + PlatformTileOverlay pigeonReturn = new PlatformTileOverlay(); + pigeonReturn.setJson(json); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(json); + return toListResult; + } + + static @NonNull PlatformTileOverlay fromList(@NonNull ArrayList __pigeon_list) { + PlatformTileOverlay pigeonResult = new PlatformTileOverlay(); + Object json = __pigeon_list.get(0); + pigeonResult.setJson(json); + return pigeonResult; + } + } + /** * Pigeon equivalent of LatLng. * @@ -351,6 +765,65 @@ ArrayList toList() { } } + /** + * Pigeon equivalent of MapConfiguration. + * + *

Generated class from Pigeon that represents data sent in messages. + */ + public static final class PlatformMapConfiguration { + /** + * The configuration options, as JSON. This should only be set from _jsonForMapConfiguration, + * and the native code must intepret it according to the internal implementation details of that + * method. + */ + private @NonNull Object json; + + public @NonNull Object getJson() { + return json; + } + + public void setJson(@NonNull Object setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"json\" is null."); + } + this.json = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + PlatformMapConfiguration() {} + + public static final class Builder { + + private @Nullable Object json; + + @CanIgnoreReturnValue + public @NonNull Builder setJson(@NonNull Object setterArg) { + this.json = setterArg; + return this; + } + + public @NonNull PlatformMapConfiguration build() { + PlatformMapConfiguration pigeonReturn = new PlatformMapConfiguration(); + pigeonReturn.setJson(json); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(json); + return toListResult; + } + + static @NonNull PlatformMapConfiguration fromList(@NonNull ArrayList __pigeon_list) { + PlatformMapConfiguration pigeonResult = new PlatformMapConfiguration(); + Object json = __pigeon_list.get(0); + pigeonResult.setJson(json); + return pigeonResult; + } + } + /** * Pigeon representation of an x,y coordinate. * @@ -647,17 +1120,36 @@ private PigeonCodec() {} protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { switch (type) { case (byte) 129: - return PlatformLatLng.fromList((ArrayList) readValue(buffer)); + return PlatformCameraUpdate.fromList((ArrayList) readValue(buffer)); case (byte) 130: - return PlatformLatLngBounds.fromList((ArrayList) readValue(buffer)); + return PlatformCircle.fromList((ArrayList) readValue(buffer)); case (byte) 131: - return PlatformCluster.fromList((ArrayList) readValue(buffer)); + return PlatformClusterManager.fromList((ArrayList) readValue(buffer)); case (byte) 132: - return PlatformPoint.fromList((ArrayList) readValue(buffer)); + return PlatformMarker.fromList((ArrayList) readValue(buffer)); case (byte) 133: - return PlatformTileLayer.fromList((ArrayList) readValue(buffer)); + return PlatformPolygon.fromList((ArrayList) readValue(buffer)); case (byte) 134: + return PlatformPolyline.fromList((ArrayList) readValue(buffer)); + case (byte) 135: + return PlatformTileOverlay.fromList((ArrayList) readValue(buffer)); + case (byte) 136: + return PlatformLatLng.fromList((ArrayList) readValue(buffer)); + case (byte) 137: + return PlatformLatLngBounds.fromList((ArrayList) readValue(buffer)); + case (byte) 138: + return PlatformCluster.fromList((ArrayList) readValue(buffer)); + case (byte) 139: + return PlatformMapConfiguration.fromList((ArrayList) readValue(buffer)); + case (byte) 140: + return PlatformPoint.fromList((ArrayList) readValue(buffer)); + case (byte) 141: + return PlatformTileLayer.fromList((ArrayList) readValue(buffer)); + case (byte) 142: return PlatformZoomRange.fromList((ArrayList) readValue(buffer)); + case (byte) 143: + Object value = readValue(buffer); + return value == null ? null : PlatformRendererType.values()[(int) value]; default: return super.readValueOfType(type, buffer); } @@ -665,24 +1157,51 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { @Override protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { - if (value instanceof PlatformLatLng) { + if (value instanceof PlatformCameraUpdate) { stream.write(129); + writeValue(stream, ((PlatformCameraUpdate) value).toList()); + } else if (value instanceof PlatformCircle) { + stream.write(130); + writeValue(stream, ((PlatformCircle) value).toList()); + } else if (value instanceof PlatformClusterManager) { + stream.write(131); + writeValue(stream, ((PlatformClusterManager) value).toList()); + } else if (value instanceof PlatformMarker) { + stream.write(132); + writeValue(stream, ((PlatformMarker) value).toList()); + } else if (value instanceof PlatformPolygon) { + stream.write(133); + writeValue(stream, ((PlatformPolygon) value).toList()); + } else if (value instanceof PlatformPolyline) { + stream.write(134); + writeValue(stream, ((PlatformPolyline) value).toList()); + } else if (value instanceof PlatformTileOverlay) { + stream.write(135); + writeValue(stream, ((PlatformTileOverlay) value).toList()); + } else if (value instanceof PlatformLatLng) { + stream.write(136); writeValue(stream, ((PlatformLatLng) value).toList()); } else if (value instanceof PlatformLatLngBounds) { - stream.write(130); + stream.write(137); writeValue(stream, ((PlatformLatLngBounds) value).toList()); } else if (value instanceof PlatformCluster) { - stream.write(131); + stream.write(138); writeValue(stream, ((PlatformCluster) value).toList()); + } else if (value instanceof PlatformMapConfiguration) { + stream.write(139); + writeValue(stream, ((PlatformMapConfiguration) value).toList()); } else if (value instanceof PlatformPoint) { - stream.write(132); + stream.write(140); writeValue(stream, ((PlatformPoint) value).toList()); } else if (value instanceof PlatformTileLayer) { - stream.write(133); + stream.write(141); writeValue(stream, ((PlatformTileLayer) value).toList()); } else if (value instanceof PlatformZoomRange) { - stream.write(134); + stream.write(142); writeValue(stream, ((PlatformZoomRange) value).toList()); + } else if (value instanceof PlatformRendererType) { + stream.write(143); + writeValue(stream, value == null ? null : ((PlatformRendererType) value).index); } else { super.writeValue(stream, value); } @@ -723,6 +1242,41 @@ public interface VoidResult { public interface MapsApi { /** Returns once the map instance is available. */ void waitForMap(@NonNull VoidResult result); + /** + * Updates the map's configuration options. + * + *

Only non-null configuration values will result in updates; options with null values will + * remain unchanged. + */ + void updateMapConfiguration(@NonNull PlatformMapConfiguration configuration); + /** Updates the set of circles on the map. */ + void updateCircles( + @NonNull List toAdd, + @NonNull List toChange, + @NonNull List idsToRemove); + /** Updates the set of custer managers for clusters on the map. */ + void updateClusterManagers( + @NonNull List toAdd, @NonNull List idsToRemove); + /** Updates the set of markers on the map. */ + void updateMarkers( + @NonNull List toAdd, + @NonNull List toChange, + @NonNull List idsToRemove); + /** Updates the set of polygonss on the map. */ + void updatePolygons( + @NonNull List toAdd, + @NonNull List toChange, + @NonNull List idsToRemove); + /** Updates the set of polylines on the map. */ + void updatePolylines( + @NonNull List toAdd, + @NonNull List toChange, + @NonNull List idsToRemove); + /** Updates the set of tile overlays on the map. */ + void updateTileOverlays( + @NonNull List toAdd, + @NonNull List toChange, + @NonNull List idsToRemove); /** Gets the screen coordinate for the given map location. */ @NonNull PlatformPoint getScreenCoordinate(@NonNull PlatformLatLng latLng); @@ -732,6 +1286,10 @@ public interface MapsApi { /** Gets the map region currently displayed on the map. */ @NonNull PlatformLatLngBounds getVisibleRegion(); + /** Moves the camera according to [cameraUpdate] immediately, with no animation. */ + void moveCamera(@NonNull PlatformCameraUpdate cameraUpdate); + /** Moves the camera according to [cameraUpdate], animating the update. */ + void animateCamera(@NonNull PlatformCameraUpdate cameraUpdate); /** Gets the current map zoom level. */ @NonNull Double getZoomLevel(); @@ -808,6 +1366,199 @@ public void error(Throwable error) { channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.google_maps_flutter_android.MapsApi.updateMapConfiguration" + + messageChannelSuffix, + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + PlatformMapConfiguration configurationArg = (PlatformMapConfiguration) args.get(0); + try { + api.updateMapConfiguration(configurationArg); + wrapped.add(0, null); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.google_maps_flutter_android.MapsApi.updateCircles" + + messageChannelSuffix, + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + List toAddArg = (List) args.get(0); + List toChangeArg = (List) args.get(1); + List idsToRemoveArg = (List) args.get(2); + try { + api.updateCircles(toAddArg, toChangeArg, idsToRemoveArg); + wrapped.add(0, null); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.google_maps_flutter_android.MapsApi.updateClusterManagers" + + messageChannelSuffix, + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + List toAddArg = (List) args.get(0); + List idsToRemoveArg = (List) args.get(1); + try { + api.updateClusterManagers(toAddArg, idsToRemoveArg); + wrapped.add(0, null); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.google_maps_flutter_android.MapsApi.updateMarkers" + + messageChannelSuffix, + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + List toAddArg = (List) args.get(0); + List toChangeArg = (List) args.get(1); + List idsToRemoveArg = (List) args.get(2); + try { + api.updateMarkers(toAddArg, toChangeArg, idsToRemoveArg); + wrapped.add(0, null); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.google_maps_flutter_android.MapsApi.updatePolygons" + + messageChannelSuffix, + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + List toAddArg = (List) args.get(0); + List toChangeArg = (List) args.get(1); + List idsToRemoveArg = (List) args.get(2); + try { + api.updatePolygons(toAddArg, toChangeArg, idsToRemoveArg); + wrapped.add(0, null); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.google_maps_flutter_android.MapsApi.updatePolylines" + + messageChannelSuffix, + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + List toAddArg = (List) args.get(0); + List toChangeArg = (List) args.get(1); + List idsToRemoveArg = (List) args.get(2); + try { + api.updatePolylines(toAddArg, toChangeArg, idsToRemoveArg); + wrapped.add(0, null); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.google_maps_flutter_android.MapsApi.updateTileOverlays" + + messageChannelSuffix, + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + List toAddArg = (List) args.get(0); + List toChangeArg = (List) args.get(1); + List idsToRemoveArg = (List) args.get(2); + try { + api.updateTileOverlays(toAddArg, toChangeArg, idsToRemoveArg); + wrapped.add(0, null); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } { BasicMessageChannel channel = new BasicMessageChannel<>( @@ -884,6 +1635,58 @@ public void error(Throwable error) { channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.google_maps_flutter_android.MapsApi.moveCamera" + + messageChannelSuffix, + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + PlatformCameraUpdate cameraUpdateArg = (PlatformCameraUpdate) args.get(0); + try { + api.moveCamera(cameraUpdateArg); + wrapped.add(0, null); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.google_maps_flutter_android.MapsApi.animateCamera" + + messageChannelSuffix, + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + PlatformCameraUpdate cameraUpdateArg = (PlatformCameraUpdate) args.get(0); + try { + api.animateCamera(cameraUpdateArg); + wrapped.add(0, null); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } { BasicMessageChannel channel = new BasicMessageChannel<>( @@ -1094,6 +1897,72 @@ public void error(Throwable error) { } } } + /** + * Interface for global SDK initialization. + * + *

Generated interface from Pigeon that represents a handler of messages from Flutter. + */ + public interface MapsInitializerApi { + /** + * Initializes the Google Maps SDK with the given renderer preference. + * + *

A null renderer preference will result in the default renderer. + * + *

Calling this more than once in the lifetime of an application will result in an error. + */ + void initializeWithPreferredRenderer( + @Nullable PlatformRendererType type, @NonNull Result result); + + /** The codec used by MapsInitializerApi. */ + static @NonNull MessageCodec getCodec() { + return PigeonCodec.INSTANCE; + } + /** + * Sets up an instance of `MapsInitializerApi` to handle messages through the `binaryMessenger`. + */ + static void setUp(@NonNull BinaryMessenger binaryMessenger, @Nullable MapsInitializerApi api) { + setUp(binaryMessenger, "", api); + } + + static void setUp( + @NonNull BinaryMessenger binaryMessenger, + @NonNull String messageChannelSuffix, + @Nullable MapsInitializerApi api) { + messageChannelSuffix = messageChannelSuffix.isEmpty() ? "" : "." + messageChannelSuffix; + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.google_maps_flutter_android.MapsInitializerApi.initializeWithPreferredRenderer" + + messageChannelSuffix, + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + PlatformRendererType typeArg = (PlatformRendererType) args.get(0); + Result resultCallback = + new Result() { + public void success(PlatformRendererType result) { + wrapped.add(0, result); + reply.reply(wrapped); + } + + public void error(Throwable error) { + ArrayList wrappedError = wrapError(error); + reply.reply(wrappedError); + } + }; + + api.initializeWithPreferredRenderer(typeArg, resultCallback); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } /** * Inspector API only intended for use in integration tests. * diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonsController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonsController.java index 6f855db0799..a68e3e89ea8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonsController.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonsController.java @@ -4,6 +4,7 @@ package io.flutter.plugins.googlemaps; +import androidx.annotation.NonNull; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.model.Polygon; import com.google.android.gms.maps.model.PolygonOptions; @@ -31,31 +32,28 @@ void setGoogleMap(GoogleMap googleMap) { this.googleMap = googleMap; } - void addPolygons(List polygonsToAdd) { + void addJsonPolygons(List polygonsToAdd) { if (polygonsToAdd != null) { for (Object polygonToAdd : polygonsToAdd) { - addPolygon(polygonToAdd); + addJsonPolygon(polygonToAdd); } } } - void changePolygons(List polygonsToChange) { - if (polygonsToChange != null) { - for (Object polygonToChange : polygonsToChange) { - changePolygon(polygonToChange); - } + void addPolygons(@NonNull List polygonsToAdd) { + for (Messages.PlatformPolygon polygonToAdd : polygonsToAdd) { + addJsonPolygon(polygonToAdd.getJson()); } } - void removePolygons(List polygonIdsToRemove) { - if (polygonIdsToRemove == null) { - return; + void changePolygons(@NonNull List polygonsToChange) { + for (Messages.PlatformPolygon polygonToChange : polygonsToChange) { + changeJsonPolygon(polygonToChange.getJson()); } - for (Object rawPolygonId : polygonIdsToRemove) { - if (rawPolygonId == null) { - continue; - } - String polygonId = (String) rawPolygonId; + } + + void removePolygons(@NonNull List polygonIdsToRemove) { + for (String polygonId : polygonIdsToRemove) { final PolygonController polygonController = polygonIdToController.remove(polygonId); if (polygonController != null) { polygonController.remove(); @@ -77,7 +75,7 @@ boolean onPolygonTap(String googlePolygonId) { return false; } - private void addPolygon(Object polygon) { + private void addJsonPolygon(Object polygon) { if (polygon == null) { return; } @@ -95,7 +93,7 @@ private void addPolygon( googleMapsPolygonIdToDartPolygonId.put(polygon.getId(), polygonId); } - private void changePolygon(Object polygon) { + private void changeJsonPolygon(Object polygon) { if (polygon == null) { return; } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylinesController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylinesController.java index 2dbad98fcfe..043474d3dc3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylinesController.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylinesController.java @@ -5,6 +5,7 @@ package io.flutter.plugins.googlemaps; import android.content.res.AssetManager; +import androidx.annotation.NonNull; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.model.Polyline; import com.google.android.gms.maps.model.PolylineOptions; @@ -34,31 +35,28 @@ void setGoogleMap(GoogleMap googleMap) { this.googleMap = googleMap; } - void addPolylines(List polylinesToAdd) { + void addJsonPolylines(List polylinesToAdd) { if (polylinesToAdd != null) { for (Object polylineToAdd : polylinesToAdd) { - addPolyline(polylineToAdd); + addJsonPolyline(polylineToAdd); } } } - void changePolylines(List polylinesToChange) { - if (polylinesToChange != null) { - for (Object polylineToChange : polylinesToChange) { - changePolyline(polylineToChange); - } + void addPolylines(@NonNull List polylinesToAdd) { + for (Messages.PlatformPolyline polylineToAdd : polylinesToAdd) { + addJsonPolyline(polylineToAdd.getJson()); } } - void removePolylines(List polylineIdsToRemove) { - if (polylineIdsToRemove == null) { - return; + void changePolylines(@NonNull List polylinesToChange) { + for (Messages.PlatformPolyline polylineToChange : polylinesToChange) { + changeJsonPolyline(polylineToChange.getJson()); } - for (Object rawPolylineId : polylineIdsToRemove) { - if (rawPolylineId == null) { - continue; - } - String polylineId = (String) rawPolylineId; + } + + void removePolylines(@NonNull List polylineIdsToRemove) { + for (String polylineId : polylineIdsToRemove) { final PolylineController polylineController = polylineIdToController.remove(polylineId); if (polylineController != null) { polylineController.remove(); @@ -80,7 +78,7 @@ boolean onPolylineTap(String googlePolylineId) { return false; } - private void addPolyline(Object polyline) { + private void addJsonPolyline(Object polyline) { if (polyline == null) { return; } @@ -99,7 +97,7 @@ private void addPolyline( googleMapsPolylineIdToDartPolylineId.put(polyline.getId(), polylineId); } - private void changePolyline(Object polyline) { + private void changeJsonPolyline(Object polyline) { if (polyline == null) { return; } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaysController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaysController.java index 52b9d1e6bc9..5dfbe2e8ee1 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaysController.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaysController.java @@ -4,6 +4,7 @@ package io.flutter.plugins.googlemaps; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.model.TileOverlay; @@ -28,21 +29,28 @@ void setGoogleMap(GoogleMap googleMap) { this.googleMap = googleMap; } - void addTileOverlays(List> tileOverlaysToAdd) { + void addJsonTileOverlays(List> tileOverlaysToAdd) { if (tileOverlaysToAdd == null) { return; } for (Map tileOverlayToAdd : tileOverlaysToAdd) { - addTileOverlay(tileOverlayToAdd); + addJsonTileOverlay(tileOverlayToAdd); } } - void changeTileOverlays(List> tileOverlaysToChange) { - if (tileOverlaysToChange == null) { - return; + void addTileOverlays(@NonNull List tileOverlaysToAdd) { + for (Messages.PlatformTileOverlay tileOverlayToAdd : tileOverlaysToAdd) { + @SuppressWarnings("unchecked") + final Map overlayJson = (Map) tileOverlayToAdd.getJson(); + addJsonTileOverlay(overlayJson); } - for (Map tileOverlayToChange : tileOverlaysToChange) { - changeTileOverlay(tileOverlayToChange); + } + + void changeTileOverlays(@NonNull List tileOverlaysToChange) { + for (Messages.PlatformTileOverlay tileOverlayToChange : tileOverlaysToChange) { + @SuppressWarnings("unchecked") + final Map overlayJson = (Map) tileOverlayToChange.getJson(); + changeJsonTileOverlay(overlayJson); } } @@ -80,7 +88,7 @@ TileOverlay getTileOverlay(String tileOverlayId) { return tileOverlayController.getTileOverlay(); } - private void addTileOverlay(Map tileOverlayOptions) { + private void addJsonTileOverlay(Map tileOverlayOptions) { if (tileOverlayOptions == null) { return; } @@ -96,7 +104,7 @@ private void addTileOverlay(Map tileOverlayOptions) { tileOverlayIdToController.put(tileOverlayId, tileOverlayController); } - private void changeTileOverlay(Map tileOverlayOptions) { + private void changeJsonTileOverlay(Map tileOverlayOptions) { if (tileOverlayOptions == null) { return; } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/ClusterManagersControllerTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/ClusterManagersControllerTest.java index bc80627c35f..aaa50542e7f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/ClusterManagersControllerTest.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/ClusterManagersControllerTest.java @@ -68,7 +68,7 @@ public void setUp() { @Test @SuppressWarnings("unchecked") - public void AddClusterManagersAndMarkers() throws InterruptedException { + public void AddJsonClusterManagersAndMarkers() throws InterruptedException { final String clusterManagerId = "cm_1"; final String markerId1 = "mid_1"; final String markerId2 = "mid_2"; @@ -90,6 +90,61 @@ public void AddClusterManagersAndMarkers() throws InterruptedException { initialClusterManager.put("clusterManagerId", clusterManagerId); List clusterManagersToAdd = new ArrayList<>(); clusterManagersToAdd.add(initialClusterManager); + controller.addJsonClusterManagers(clusterManagersToAdd); + + MarkerBuilder markerBuilder1 = new MarkerBuilder(markerId1, clusterManagerId); + MarkerBuilder markerBuilder2 = new MarkerBuilder(markerId2, clusterManagerId); + + final Map markerData1 = + createMarkerData(markerId1, location1, clusterManagerId); + final Map markerData2 = + createMarkerData(markerId2, location2, clusterManagerId); + + Convert.interpretMarkerOptions(markerData1, markerBuilder1, assetManager, density); + Convert.interpretMarkerOptions(markerData2, markerBuilder2, assetManager, density); + + controller.addItem(markerBuilder1); + controller.addItem(markerBuilder2); + + Set> clusters = + controller.getClustersWithClusterManagerId(clusterManagerId); + assertEquals("Amount of clusters should be 1", 1, clusters.size()); + + Cluster cluster = clusters.iterator().next(); + assertNotNull("Cluster position should not be null", cluster.getPosition()); + Set markerIds = new HashSet<>(); + for (MarkerBuilder marker : cluster.getItems()) { + markerIds.add(marker.markerId()); + } + assertTrue("Marker IDs should contain markerId1", markerIds.contains(markerId1)); + assertTrue("Marker IDs should contain markerId2", markerIds.contains(markerId2)); + assertEquals("Cluster should contain exactly 2 markers", 2, cluster.getSize()); + } + + @Test + @SuppressWarnings("unchecked") + public void AddClusterManagersAndMarkers() throws InterruptedException { + final String clusterManagerId = "cm_1"; + final String markerId1 = "mid_1"; + final String markerId2 = "mid_2"; + + final LatLng latLng1 = new LatLng(1.1, 2.2); + final LatLng latLng2 = new LatLng(3.3, 4.4); + + final List location1 = new ArrayList<>(); + location1.add(latLng1.latitude); + location1.add(latLng1.longitude); + + final List location2 = new ArrayList<>(); + location2.add(latLng2.latitude); + location2.add(latLng2.longitude); + + when(googleMap.getCameraPosition()) + .thenReturn(CameraPosition.builder().target(new LatLng(0, 0)).build()); + Messages.PlatformClusterManager initialClusterManager = + new Messages.PlatformClusterManager.Builder().setIdentifier(clusterManagerId).build(); + List clusterManagersToAdd = new ArrayList<>(); + clusterManagersToAdd.add(initialClusterManager); controller.addClusterManagers(clusterManagersToAdd); MarkerBuilder markerBuilder1 = new MarkerBuilder(markerId1, clusterManagerId); @@ -150,9 +205,9 @@ public void RemoveClusterManagers() { when(googleMap.getCameraPosition()) .thenReturn(CameraPosition.builder().target(new LatLng(0, 0)).build()); - Map initialClusterManager = new HashMap<>(); - initialClusterManager.put("clusterManagerId", clusterManagerId); - List clusterManagersToAdd = new ArrayList<>(); + Messages.PlatformClusterManager initialClusterManager = + new Messages.PlatformClusterManager.Builder().setIdentifier(clusterManagerId).build(); + List clusterManagersToAdd = new ArrayList<>(); clusterManagersToAdd.add(initialClusterManager); controller.addClusterManagers(clusterManagersToAdd); diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java index 3459b9b3139..f68ab346077 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java @@ -203,7 +203,7 @@ public void SetInitialClusterManagers() { googleMapController.onMapReady(mockGoogleMap); // Verify if the ClusterManagersController.addClusterManagers method is called with initial cluster managers. - verify(mockClusterManagersController, times(1)).addClusterManagers(any()); + verify(mockClusterManagersController, times(1)).addJsonClusterManagers(any()); } @Test diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapInitializerTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapInitializerTest.java index 2f9f5e5619f..374964cbad6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapInitializerTest.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapInitializerTest.java @@ -6,7 +6,6 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -18,9 +17,6 @@ import androidx.test.core.app.ApplicationProvider; import com.google.android.gms.maps.MapsInitializer.Renderer; import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; -import java.util.HashMap; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -46,53 +42,33 @@ public void before() { @Test public void initializer_OnMapsSdkInitializedWithLatestRenderer() { doNothing().when(googleMapInitializer).initializeWithRendererRequest(Renderer.LATEST); - MethodChannel.Result result = mock(MethodChannel.Result.class); - googleMapInitializer.onMethodCall( - new MethodCall( - "initializer#preferRenderer", - new HashMap() { - { - put("value", "latest"); - } - }), - result); + @SuppressWarnings("unchecked") + Messages.Result result = mock(Messages.Result.class); + googleMapInitializer.initializeWithPreferredRenderer( + Messages.PlatformRendererType.LATEST, result); googleMapInitializer.onMapsSdkInitialized(Renderer.LATEST); - verify(result, times(1)).success("latest"); - verify(result, never()).error(any(), any(), any()); + verify(result, times(1)).success(Messages.PlatformRendererType.LATEST); + verify(result, never()).error(any()); } @Test public void initializer_OnMapsSdkInitializedWithLegacyRenderer() { doNothing().when(googleMapInitializer).initializeWithRendererRequest(Renderer.LEGACY); - MethodChannel.Result result = mock(MethodChannel.Result.class); - googleMapInitializer.onMethodCall( - new MethodCall( - "initializer#preferRenderer", - new HashMap() { - { - put("value", "legacy"); - } - }), - result); + @SuppressWarnings("unchecked") + Messages.Result result = mock(Messages.Result.class); + googleMapInitializer.initializeWithPreferredRenderer( + Messages.PlatformRendererType.LEGACY, result); googleMapInitializer.onMapsSdkInitialized(Renderer.LEGACY); - verify(result, times(1)).success("legacy"); - verify(result, never()).error(any(), any(), any()); + verify(result, times(1)).success(Messages.PlatformRendererType.LEGACY); + verify(result, never()).error(any()); } @Test - public void initializer_onMethodCallWithUnknownRenderer() { - doNothing().when(googleMapInitializer).initializeWithRendererRequest(Renderer.LEGACY); - MethodChannel.Result result = mock(MethodChannel.Result.class); - googleMapInitializer.onMethodCall( - new MethodCall( - "initializer#preferRenderer", - new HashMap() { - { - put("value", "wrong_renderer"); - } - }), - result); - verify(result, never()).success(any()); - verify(result, times(1)).error(eq("Invalid renderer type"), any(), any()); + public void initializer_onMethodCallWithNoRendererPreference() { + doNothing().when(googleMapInitializer).initializeWithRendererRequest(null); + @SuppressWarnings("unchecked") + Messages.Result result = mock(Messages.Result.class); + googleMapInitializer.initializeWithPreferredRenderer(null, result); + verify(result, never()).error(any()); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/MarkersControllerTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/MarkersControllerTest.java index 116f8381bce..41444d9f290 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/MarkersControllerTest.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/MarkersControllerTest.java @@ -25,6 +25,7 @@ import io.flutter.plugin.common.MethodCodec; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -81,7 +82,7 @@ public void controller_OnMarkerDragStart() { markerOptions.put("markerId", googleMarkerId); final List markers = Arrays.asList(markerOptions); - controller.addMarkers(markers); + controller.addJsonMarkers(markers); controller.onMarkerDragStart(googleMarkerId, latLng); final List points = new ArrayList<>(); @@ -108,7 +109,7 @@ public void controller_OnMarkerDragEnd() { markerOptions.put("markerId", googleMarkerId); final List markers = Arrays.asList(markerOptions); - controller.addMarkers(markers); + controller.addJsonMarkers(markers); controller.onMarkerDragEnd(googleMarkerId, latLng); final List points = new ArrayList<>(); @@ -135,7 +136,7 @@ public void controller_OnMarkerDrag() { markerOptions.put("markerId", googleMarkerId); final List markers = Arrays.asList(markerOptions); - controller.addMarkers(markers); + controller.addJsonMarkers(markers); controller.onMarkerDrag(googleMarkerId, latLng); final List points = new ArrayList<>(); @@ -154,7 +155,7 @@ public void controller_AddMarkerThrowsErrorIfMarkerIdIsNull() { final List markers = Arrays.asList(markerOptions); try { - controller.addMarkers(markers); + controller.addJsonMarkers(markers); } catch (IllegalArgumentException e) { assertEquals("markerId was null", e.getMessage()); throw e; @@ -183,7 +184,7 @@ public void controller_AddChangeAndRemoveMarkerWithClusterManagerId() { final List markers = Arrays.asList(markerOptions1); // Add marker and capture the markerBuilder - controller.addMarkers(markers); + controller.addJsonMarkers(markers); ArgumentCaptor captor = ArgumentCaptor.forClass(MarkerBuilder.class); Mockito.verify(clusterManagersController, times(1)).addItem(captor.capture()); MarkerBuilder capturedMarkerBuilder = captor.getValue(); @@ -202,7 +203,9 @@ public void controller_AddChangeAndRemoveMarkerWithClusterManagerId() { markerOptions2.put("markerId", googleMarkerId); markerOptions2.put("position", location2); markerOptions2.put("clusterManagerId", clusterManagerId); - final List updatedMarkers = Arrays.asList(markerOptions2); + final List updatedMarkers = + Collections.singletonList( + new Messages.PlatformMarker.Builder().setJson(markerOptions2).build()); controller.changeMarkers(updatedMarkers); Mockito.verify(marker, times(1)).setPosition(latLng2); @@ -232,7 +235,7 @@ public void controller_AddChangeAndRemoveMarkerWithoutClusterManagerId() { markerOptions1.put("markerId", googleMarkerId); final List markers = Arrays.asList(markerOptions1); - controller.addMarkers(markers); + controller.addJsonMarkers(markers); // clusterManagersController should not be called when adding the marker Mockito.verify(clusterManagersController, times(0)).addItem(any()); @@ -244,7 +247,9 @@ public void controller_AddChangeAndRemoveMarkerWithoutClusterManagerId() { markerOptions2.put("markerId", googleMarkerId); markerOptions2.put("alpha", alpha); - final List markerUpdates = Arrays.asList(markerOptions2); + final List markerUpdates = + Collections.singletonList( + new Messages.PlatformMarker.Builder().setJson(markerOptions2).build()); controller.changeMarkers(markerUpdates); Mockito.verify(marker, times(1)).setAlpha(alpha); diff --git a/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart index 0664ae3accd..3b77fa91480 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart @@ -70,10 +70,6 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { GoogleMapsFlutterPlatform.instance = GoogleMapsFlutterAndroid(); } - /// The method channel used to initialize the native Google Maps SDK. - final MethodChannel _initializerChannel = const MethodChannel( - 'plugins.flutter.dev/google_maps_android_initializer'); - // Keep a collection of id -> channel // Every method call passes the int mapId final Map _channels = {}; @@ -83,15 +79,6 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { // A method to create MapsApi instances, which can be overridden for testing. final MapsApi Function(int mapId) _apiProvider; - /// Accesses the MethodChannel associated to the passed mapId. - MethodChannel _channel(int mapId) { - final MethodChannel? channel = _channels[mapId]; - if (channel == null) { - throw UnknownMapIDError(mapId); - } - return channel; - } - /// Accesses the MapsApi associated to the passed mapId. MapsApi _hostApi(int mapId) { final MapsApi? api = _hostMaps[mapId]; @@ -341,17 +328,22 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { return (call.arguments as Map).cast(); } + @override + Future updateMapConfiguration( + MapConfiguration configuration, { + required int mapId, + }) { + return updateMapOptions(_jsonForMapConfiguration(configuration), + mapId: mapId); + } + @override Future updateMapOptions( Map optionsUpdate, { required int mapId, }) { - return _channel(mapId).invokeMethod( - 'map#update', - { - 'options': optionsUpdate, - }, - ); + return _hostApi(mapId) + .updateMapConfiguration(PlatformMapConfiguration(json: optionsUpdate)); } @override @@ -359,9 +351,10 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { MarkerUpdates markerUpdates, { required int mapId, }) { - return _channel(mapId).invokeMethod( - 'markers#update', - markerUpdates.toJson(), + return _hostApi(mapId).updateMarkers( + markerUpdates.markersToAdd.map(_platformMarkerFromMarker).toList(), + markerUpdates.markersToChange.map(_platformMarkerFromMarker).toList(), + markerUpdates.markerIdsToRemove.map((MarkerId id) => id.value).toList(), ); } @@ -370,9 +363,12 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { PolygonUpdates polygonUpdates, { required int mapId, }) { - return _channel(mapId).invokeMethod( - 'polygons#update', - polygonUpdates.toJson(), + return _hostApi(mapId).updatePolygons( + polygonUpdates.polygonsToAdd.map(_platformPolygonFromPolygon).toList(), + polygonUpdates.polygonsToChange.map(_platformPolygonFromPolygon).toList(), + polygonUpdates.polygonIdsToRemove + .map((PolygonId id) => id.value) + .toList(), ); } @@ -381,9 +377,16 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { PolylineUpdates polylineUpdates, { required int mapId, }) { - return _channel(mapId).invokeMethod( - 'polylines#update', - polylineUpdates.toJson(), + return _hostApi(mapId).updatePolylines( + polylineUpdates.polylinesToAdd + .map(_platformPolylineFromPolyline) + .toList(), + polylineUpdates.polylinesToChange + .map(_platformPolylineFromPolyline) + .toList(), + polylineUpdates.polylineIdsToRemove + .map((PolylineId id) => id.value) + .toList(), ); } @@ -392,9 +395,10 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { CircleUpdates circleUpdates, { required int mapId, }) { - return _channel(mapId).invokeMethod( - 'circles#update', - circleUpdates.toJson(), + return _hostApi(mapId).updateCircles( + circleUpdates.circlesToAdd.map(_platformCircleFromCircle).toList(), + circleUpdates.circlesToChange.map(_platformCircleFromCircle).toList(), + circleUpdates.circleIdsToRemove.map((CircleId id) => id.value).toList(), ); } @@ -411,9 +415,16 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { final _TileOverlayUpdates updates = _TileOverlayUpdates.from(previousSet, newTileOverlays); _tileOverlays[mapId] = keyTileOverlayId(newTileOverlays); - return _channel(mapId).invokeMethod( - 'tileOverlays#update', - updates.toJson(), + return _hostApi(mapId).updateTileOverlays( + updates.tileOverlaysToAdd + .map(_platformTileOverlayFromTileOverlay) + .toList(), + updates.tileOverlaysToChange + .map(_platformTileOverlayFromTileOverlay) + .toList(), + updates.tileOverlayIdsToRemove + .map((TileOverlayId id) => id.value) + .toList(), ); } @@ -422,9 +433,13 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { ClusterManagerUpdates clusterManagerUpdates, { required int mapId, }) { - return _channel(mapId).invokeMethod( - 'clusterManagers#update', - serializeClusterManagerUpdates(clusterManagerUpdates), + return _hostApi(mapId).updateClusterManagers( + clusterManagerUpdates.clusterManagersToAdd + .map(_platformClusterManagerFromClusterManager) + .toList(), + clusterManagerUpdates.clusterManagerIdsToRemove + .map((ClusterManagerId id) => id.value) + .toList(), ); } @@ -441,10 +456,8 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { CameraUpdate cameraUpdate, { required int mapId, }) { - return _channel(mapId) - .invokeMethod('camera#animate', { - 'cameraUpdate': cameraUpdate.toJson(), - }); + return _hostApi(mapId) + .animateCamera(PlatformCameraUpdate(json: cameraUpdate.toJson())); } @override @@ -452,9 +465,8 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { CameraUpdate cameraUpdate, { required int mapId, }) { - return _channel(mapId).invokeMethod('camera#move', { - 'cameraUpdate': cameraUpdate.toJson(), - }); + return _hostApi(mapId) + .moveCamera(PlatformCameraUpdate(json: cameraUpdate.toJson())); } @override @@ -561,35 +573,25 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { /// Initialized [AndroidMapRenderer] type is returned. Future initializeWithRenderer( AndroidMapRenderer? rendererType) async { - String preferredRenderer; + PlatformRendererType? preferredRenderer; switch (rendererType) { case AndroidMapRenderer.latest: - preferredRenderer = 'latest'; + preferredRenderer = PlatformRendererType.latest; case AndroidMapRenderer.legacy: - preferredRenderer = 'legacy'; + preferredRenderer = PlatformRendererType.legacy; case AndroidMapRenderer.platformDefault: case null: - preferredRenderer = 'default'; + preferredRenderer = null; } - final String? initializedRenderer = await _initializerChannel - .invokeMethod('initializer#preferRenderer', - {'value': preferredRenderer}); - - if (initializedRenderer == null) { - throw AndroidMapRendererException('Failed to initialize map renderer.'); - } + final MapsInitializerApi hostApi = MapsInitializerApi(); + final PlatformRendererType initializedRenderer = + await hostApi.initializeWithPreferredRenderer(preferredRenderer); - // Returns mapped [AndroidMapRenderer] enum type. - switch (initializedRenderer) { - case 'latest': - return AndroidMapRenderer.latest; - case 'legacy': - return AndroidMapRenderer.legacy; - default: - throw AndroidMapRendererException( - 'Failed to initialize latest or legacy renderer, got $initializedRenderer.'); - } + return switch (initializedRenderer) { + PlatformRendererType.latest => AndroidMapRenderer.latest, + PlatformRendererType.legacy => AndroidMapRenderer.legacy, + }; } Widget _buildView( @@ -599,6 +601,8 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { MapObjects mapObjects = const MapObjects(), Map mapOptions = const {}, }) { + // TODO(stuartmorgan): Convert this to Pigeon-generated structures once + // https://github.com/flutter/flutter/issues/150631 is fixed. final Map creationParams = { 'initialCameraPosition': widgetConfiguration.initialCameraPosition.toMap(), @@ -815,6 +819,33 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { southwest: _latLngFromPlatformLatLng(bounds.southwest), northeast: _latLngFromPlatformLatLng(bounds.northeast)); } + + static PlatformCircle _platformCircleFromCircle(Circle circle) { + return PlatformCircle(json: circle.toJson()); + } + + static PlatformClusterManager _platformClusterManagerFromClusterManager( + ClusterManager clusterManager) { + return PlatformClusterManager( + identifier: clusterManager.clusterManagerId.value); + } + + static PlatformMarker _platformMarkerFromMarker(Marker marker) { + return PlatformMarker(json: marker.toJson()); + } + + static PlatformPolygon _platformPolygonFromPolygon(Polygon polygon) { + return PlatformPolygon(json: polygon.toJson()); + } + + static PlatformPolyline _platformPolylineFromPolyline(Polyline polyline) { + return PlatformPolyline(json: polyline.toJson()); + } + + static PlatformTileOverlay _platformTileOverlayFromTileOverlay( + TileOverlay tileOverlay) { + return PlatformTileOverlay(json: tileOverlay.toJson()); + } } Map _jsonForMapConfiguration(MapConfiguration config) { diff --git a/packages/google_maps_flutter/google_maps_flutter_android/lib/src/messages.g.dart b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/messages.g.dart index fa7445f748f..23f7aa45c0a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/lib/src/messages.g.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/messages.g.dart @@ -18,6 +18,183 @@ PlatformException _createConnectionError(String channelName) { ); } +enum PlatformRendererType { + legacy, + latest, +} + +/// Pigeon representation of a CameraUpdate. +class PlatformCameraUpdate { + PlatformCameraUpdate({ + required this.json, + }); + + /// The update data, as JSON. This should only be set from + /// CameraUpdate.toJson, and the native code must intepret it according to the + /// internal implementation details of the CameraUpdate class. + Object json; + + Object encode() { + return [ + json, + ]; + } + + static PlatformCameraUpdate decode(Object result) { + result as List; + return PlatformCameraUpdate( + json: result[0]!, + ); + } +} + +/// Pigeon equivalent of the Circle class. +class PlatformCircle { + PlatformCircle({ + required this.json, + }); + + /// The circle data, as JSON. This should only be set from + /// Circle.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + Object json; + + Object encode() { + return [ + json, + ]; + } + + static PlatformCircle decode(Object result) { + result as List; + return PlatformCircle( + json: result[0]!, + ); + } +} + +/// Pigeon equivalent of the ClusterManager class. +class PlatformClusterManager { + PlatformClusterManager({ + required this.identifier, + }); + + String identifier; + + Object encode() { + return [ + identifier, + ]; + } + + static PlatformClusterManager decode(Object result) { + result as List; + return PlatformClusterManager( + identifier: result[0]! as String, + ); + } +} + +/// Pigeon equivalent of the Marker class. +class PlatformMarker { + PlatformMarker({ + required this.json, + }); + + /// The marker data, as JSON. This should only be set from + /// Marker.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + Object json; + + Object encode() { + return [ + json, + ]; + } + + static PlatformMarker decode(Object result) { + result as List; + return PlatformMarker( + json: result[0]!, + ); + } +} + +/// Pigeon equivalent of the Polygon class. +class PlatformPolygon { + PlatformPolygon({ + required this.json, + }); + + /// The polygon data, as JSON. This should only be set from + /// Polygon.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + Object json; + + Object encode() { + return [ + json, + ]; + } + + static PlatformPolygon decode(Object result) { + result as List; + return PlatformPolygon( + json: result[0]!, + ); + } +} + +/// Pigeon equivalent of the Polyline class. +class PlatformPolyline { + PlatformPolyline({ + required this.json, + }); + + /// The polyline data, as JSON. This should only be set from + /// Polyline.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + Object json; + + Object encode() { + return [ + json, + ]; + } + + static PlatformPolyline decode(Object result) { + result as List; + return PlatformPolyline( + json: result[0]!, + ); + } +} + +/// Pigeon equivalent of the TileOverlay class. +class PlatformTileOverlay { + PlatformTileOverlay({ + required this.json, + }); + + /// The tile overlay data, as JSON. This should only be set from + /// TileOverlay.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + Object json; + + Object encode() { + return [ + json, + ]; + } + + static PlatformTileOverlay decode(Object result) { + result as List; + return PlatformTileOverlay( + json: result[0]!, + ); + } +} + /// Pigeon equivalent of LatLng. class PlatformLatLng { PlatformLatLng({ @@ -109,6 +286,31 @@ class PlatformCluster { } } +/// Pigeon equivalent of MapConfiguration. +class PlatformMapConfiguration { + PlatformMapConfiguration({ + required this.json, + }); + + /// The configuration options, as JSON. This should only be set from + /// _jsonForMapConfiguration, and the native code must intepret it according + /// to the internal implementation details of that method. + Object json; + + Object encode() { + return [ + json, + ]; + } + + static PlatformMapConfiguration decode(Object result) { + result as List; + return PlatformMapConfiguration( + json: result[0]!, + ); + } +} + /// Pigeon representation of an x,y coordinate. class PlatformPoint { PlatformPoint({ @@ -204,24 +406,51 @@ class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is PlatformLatLng) { + if (value is PlatformCameraUpdate) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is PlatformLatLngBounds) { + } else if (value is PlatformCircle) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is PlatformCluster) { + } else if (value is PlatformClusterManager) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is PlatformPoint) { + } else if (value is PlatformMarker) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is PlatformTileLayer) { + } else if (value is PlatformPolygon) { buffer.putUint8(133); writeValue(buffer, value.encode()); - } else if (value is PlatformZoomRange) { + } else if (value is PlatformPolyline) { buffer.putUint8(134); writeValue(buffer, value.encode()); + } else if (value is PlatformTileOverlay) { + buffer.putUint8(135); + writeValue(buffer, value.encode()); + } else if (value is PlatformLatLng) { + buffer.putUint8(136); + writeValue(buffer, value.encode()); + } else if (value is PlatformLatLngBounds) { + buffer.putUint8(137); + writeValue(buffer, value.encode()); + } else if (value is PlatformCluster) { + buffer.putUint8(138); + writeValue(buffer, value.encode()); + } else if (value is PlatformMapConfiguration) { + buffer.putUint8(139); + writeValue(buffer, value.encode()); + } else if (value is PlatformPoint) { + buffer.putUint8(140); + writeValue(buffer, value.encode()); + } else if (value is PlatformTileLayer) { + buffer.putUint8(141); + writeValue(buffer, value.encode()); + } else if (value is PlatformZoomRange) { + buffer.putUint8(142); + writeValue(buffer, value.encode()); + } else if (value is PlatformRendererType) { + buffer.putUint8(143); + writeValue(buffer, value.index); } else { super.writeValue(buffer, value); } @@ -231,17 +460,36 @@ class _PigeonCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 129: - return PlatformLatLng.decode(readValue(buffer)!); + return PlatformCameraUpdate.decode(readValue(buffer)!); case 130: - return PlatformLatLngBounds.decode(readValue(buffer)!); + return PlatformCircle.decode(readValue(buffer)!); case 131: - return PlatformCluster.decode(readValue(buffer)!); + return PlatformClusterManager.decode(readValue(buffer)!); case 132: - return PlatformPoint.decode(readValue(buffer)!); + return PlatformMarker.decode(readValue(buffer)!); case 133: - return PlatformTileLayer.decode(readValue(buffer)!); + return PlatformPolygon.decode(readValue(buffer)!); case 134: + return PlatformPolyline.decode(readValue(buffer)!); + case 135: + return PlatformTileOverlay.decode(readValue(buffer)!); + case 136: + return PlatformLatLng.decode(readValue(buffer)!); + case 137: + return PlatformLatLngBounds.decode(readValue(buffer)!); + case 138: + return PlatformCluster.decode(readValue(buffer)!); + case 139: + return PlatformMapConfiguration.decode(readValue(buffer)!); + case 140: + return PlatformPoint.decode(readValue(buffer)!); + case 141: + return PlatformTileLayer.decode(readValue(buffer)!); + case 142: return PlatformZoomRange.decode(readValue(buffer)!); + case 143: + final int? value = readValue(buffer) as int?; + return value == null ? null : PlatformRendererType.values[value]; default: return super.readValueOfType(type, buffer); } @@ -290,6 +538,191 @@ class MapsApi { } } + /// Updates the map's configuration options. + /// + /// Only non-null configuration values will result in updates; options with + /// null values will remain unchanged. + Future updateMapConfiguration( + PlatformMapConfiguration configuration) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_android.MapsApi.updateMapConfiguration$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([configuration]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Updates the set of circles on the map. + Future updateCircles(List toAdd, + List toChange, List idsToRemove) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_android.MapsApi.updateCircles$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([toAdd, toChange, idsToRemove]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Updates the set of custer managers for clusters on the map. + Future updateClusterManagers( + List toAdd, List idsToRemove) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_android.MapsApi.updateClusterManagers$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([toAdd, idsToRemove]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Updates the set of markers on the map. + Future updateMarkers(List toAdd, + List toChange, List idsToRemove) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_android.MapsApi.updateMarkers$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([toAdd, toChange, idsToRemove]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Updates the set of polygonss on the map. + Future updatePolygons(List toAdd, + List toChange, List idsToRemove) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_android.MapsApi.updatePolygons$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([toAdd, toChange, idsToRemove]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Updates the set of polylines on the map. + Future updatePolylines(List toAdd, + List toChange, List idsToRemove) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_android.MapsApi.updatePolylines$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([toAdd, toChange, idsToRemove]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Updates the set of tile overlays on the map. + Future updateTileOverlays(List toAdd, + List toChange, List idsToRemove) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_android.MapsApi.updateTileOverlays$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([toAdd, toChange, idsToRemove]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + /// Gets the screen coordinate for the given map location. Future getScreenCoordinate(PlatformLatLng latLng) async { final String __pigeon_channelName = @@ -380,6 +813,57 @@ class MapsApi { } } + /// Moves the camera according to [cameraUpdate] immediately, with no + /// animation. + Future moveCamera(PlatformCameraUpdate cameraUpdate) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_android.MapsApi.moveCamera$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([cameraUpdate]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Moves the camera according to [cameraUpdate], animating the update. + Future animateCamera(PlatformCameraUpdate cameraUpdate) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_android.MapsApi.animateCamera$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([cameraUpdate]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + /// Gets the current map zoom level. Future getZoomLevel() async { final String __pigeon_channelName = @@ -615,6 +1099,59 @@ class MapsApi { } } +/// Interface for global SDK initialization. +class MapsInitializerApi { + /// Constructor for [MapsInitializerApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + MapsInitializerApi( + {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : __pigeon_binaryMessenger = binaryMessenger, + __pigeon_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? __pigeon_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String __pigeon_messageChannelSuffix; + + /// Initializes the Google Maps SDK with the given renderer preference. + /// + /// A null renderer preference will result in the default renderer. + /// + /// Calling this more than once in the lifetime of an application will result + /// in an error. + Future initializeWithPreferredRenderer( + PlatformRendererType? type) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_android.MapsInitializerApi.initializeWithPreferredRenderer$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([type]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as PlatformRendererType?)!; + } + } +} + /// Inspector API only intended for use in integration tests. class MapsInspectorApi { /// Constructor for [MapsInspectorApi]. The [binaryMessenger] named argument is diff --git a/packages/google_maps_flutter/google_maps_flutter_android/lib/src/utils/cluster_manager_utils.dart b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/utils/cluster_manager_utils.dart index cdb1d2c9244..1aab89354a0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/lib/src/utils/cluster_manager_utils.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/utils/cluster_manager_utils.dart @@ -17,18 +17,3 @@ Object _serializeClusterManager(ClusterManager clusterManager) { json['clusterManagerId'] = clusterManager.clusterManagerId.value; return json; } - -/// Converts a Cluster Manager updates into object serializable in JSON. -Object serializeClusterManagerUpdates( - ClusterManagerUpdates clusterManagerUpdates) { - final Map updateMap = {}; - - updateMap['clusterManagersToAdd'] = - serializeClusterManagerSet(clusterManagerUpdates.objectsToAdd); - updateMap['clusterManagerIdsToRemove'] = clusterManagerUpdates - .objectIdsToRemove - .map((MapsObjectId id) => id.value) - .toList(); - - return updateMap; -} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/pigeons/messages.dart b/packages/google_maps_flutter/google_maps_flutter_android/pigeons/messages.dart index 1a50204c580..3b78a5b180b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/pigeons/messages.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/pigeons/messages.dart @@ -11,6 +11,91 @@ import 'package:pigeon/pigeon.dart'; copyrightHeader: 'pigeons/copyright.txt', )) +// Pigeon equivalent of the Java MapsInitializer.Renderer. +enum PlatformRendererType { legacy, latest } + +/// Pigeon representation of a CameraUpdate. +class PlatformCameraUpdate { + PlatformCameraUpdate(this.json); + + /// The update data, as JSON. This should only be set from + /// CameraUpdate.toJson, and the native code must intepret it according to the + /// internal implementation details of the CameraUpdate class. + // TODO(stuartmorgan): Update the google_maps_platform_interface CameraUpdate + // class to provide a structured representation of an update. Currently it + // uses JSON as its only state, so there is no way to preserve structure. + // This wrapper class exists as a placeholder for now to at least provide + // type safety in the top-level call's arguments. + final Object json; +} + +/// Pigeon equivalent of the Circle class. +class PlatformCircle { + PlatformCircle(this.json); + + /// The circle data, as JSON. This should only be set from + /// Circle.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + // TODO(stuartmorgan): Replace this with structured data. This exists only to + // allow incremental migration to Pigeon. + final Object json; +} + +/// Pigeon equivalent of the ClusterManager class. +class PlatformClusterManager { + PlatformClusterManager({required this.identifier}); + + final String identifier; +} + +/// Pigeon equivalent of the Marker class. +class PlatformMarker { + PlatformMarker(this.json); + + /// The marker data, as JSON. This should only be set from + /// Marker.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + // TODO(stuartmorgan): Replace this with structured data. This exists only to + // allow incremental migration to Pigeon. + final Object json; +} + +/// Pigeon equivalent of the Polygon class. +class PlatformPolygon { + PlatformPolygon(this.json); + + /// The polygon data, as JSON. This should only be set from + /// Polygon.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + // TODO(stuartmorgan): Replace this with structured data. This exists only to + // allow incremental migration to Pigeon. + final Object json; +} + +/// Pigeon equivalent of the Polyline class. +class PlatformPolyline { + PlatformPolyline(this.json); + + /// The polyline data, as JSON. This should only be set from + /// Polyline.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + // TODO(stuartmorgan): Replace this with structured data. This exists only to + // allow incremental migration to Pigeon. + final Object json; +} + +/// Pigeon equivalent of the TileOverlay class. +class PlatformTileOverlay { + PlatformTileOverlay(this.json); + + /// The tile overlay data, as JSON. This should only be set from + /// TileOverlay.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + // TODO(stuartmorgan): Replace this with structured data. This exists only to + // allow incremental migration to Pigeon. + final Object json; +} + /// Pigeon equivalent of LatLng. class PlatformLatLng { PlatformLatLng({required this.latitude, required this.longitude}); @@ -45,6 +130,18 @@ class PlatformCluster { final List markerIds; } +/// Pigeon equivalent of MapConfiguration. +class PlatformMapConfiguration { + PlatformMapConfiguration({required this.json}); + + /// The configuration options, as JSON. This should only be set from + /// _jsonForMapConfiguration, and the native code must intepret it according + /// to the internal implementation details of that method. + // TODO(stuartmorgan): Replace this with structured data. This exists only to + // allow incremental migration to Pigeon. + final Object json; +} + /// Pigeon representation of an x,y coordinate. class PlatformPoint { PlatformPoint({required this.x, required this.y}); @@ -85,6 +182,54 @@ abstract class MapsApi { @async void waitForMap(); + /// Updates the map's configuration options. + /// + /// Only non-null configuration values will result in updates; options with + /// null values will remain unchanged. + void updateMapConfiguration(PlatformMapConfiguration configuration); + + /// Updates the set of circles on the map. + // TODO(stuartmorgan): Make the generic type non-nullable once supported. + // https://github.com/flutter/flutter/issues/97848 + // The consuming code treats the entries as non-nullable. + void updateCircles(List toAdd, + List toChange, List idsToRemove); + + /// Updates the set of custer managers for clusters on the map. + // TODO(stuartmorgan): Make the generic type non-nullable once supported. + // https://github.com/flutter/flutter/issues/97848 + // The consuming code treats the entries as non-nullable. + void updateClusterManagers( + List toAdd, List idsToRemove); + + /// Updates the set of markers on the map. + // TODO(stuartmorgan): Make the generic type non-nullable once supported. + // https://github.com/flutter/flutter/issues/97848 + // The consuming code treats the entries as non-nullable. + void updateMarkers(List toAdd, + List toChange, List idsToRemove); + + /// Updates the set of polygonss on the map. + // TODO(stuartmorgan): Make the generic type non-nullable once supported. + // https://github.com/flutter/flutter/issues/97848 + // The consuming code treats the entries as non-nullable. + void updatePolygons(List toAdd, + List toChange, List idsToRemove); + + /// Updates the set of polylines on the map. + // TODO(stuartmorgan): Make the generic type non-nullable once supported. + // https://github.com/flutter/flutter/issues/97848 + // The consuming code treats the entries as non-nullable. + void updatePolylines(List toAdd, + List toChange, List idsToRemove); + + /// Updates the set of tile overlays on the map. + // TODO(stuartmorgan): Make the generic type non-nullable once supported. + // https://github.com/flutter/flutter/issues/97848 + // The consuming code treats the entries as non-nullable. + void updateTileOverlays(List toAdd, + List toChange, List idsToRemove); + /// Gets the screen coordinate for the given map location. PlatformPoint getScreenCoordinate(PlatformLatLng latLng); @@ -94,6 +239,13 @@ abstract class MapsApi { /// Gets the map region currently displayed on the map. PlatformLatLngBounds getVisibleRegion(); + /// Moves the camera according to [cameraUpdate] immediately, with no + /// animation. + void moveCamera(PlatformCameraUpdate cameraUpdate); + + /// Moves the camera according to [cameraUpdate], animating the update. + void animateCamera(PlatformCameraUpdate cameraUpdate); + /// Gets the current map zoom level. double getZoomLevel(); @@ -129,6 +281,20 @@ abstract class MapsApi { Uint8List takeSnapshot(); } +/// Interface for global SDK initialization. +@HostApi() +abstract class MapsInitializerApi { + /// Initializes the Google Maps SDK with the given renderer preference. + /// + /// A null renderer preference will result in the default renderer. + /// + /// Calling this more than once in the lifetime of an application will result + /// in an error. + @async + PlatformRendererType initializeWithPreferredRenderer( + PlatformRendererType? type); +} + /// Inspector API only intended for use in integration tests. @HostApi() abstract class MapsInspectorApi { 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 087914e6b3e..e55b16a6b94 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.10.0 +version: 2.11.0 environment: sdk: ^3.4.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_android/test/cluster_manager_utils_test.dart b/packages/google_maps_flutter/google_maps_flutter_android/test/cluster_manager_utils_test.dart deleted file mode 100644 index 56963ddbdd9..00000000000 --- a/packages/google_maps_flutter/google_maps_flutter_android/test/cluster_manager_utils_test.dart +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -import 'package:flutter_test/flutter_test.dart'; -import 'package:google_maps_flutter_android/src/utils/cluster_manager_utils.dart'; -import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; - -void main() { - test('serializeClusterManagerUpdates', () async { - const ClusterManagerId clusterManagerId1 = ClusterManagerId('cm1'); - const ClusterManagerId clusterManagerId2 = ClusterManagerId('cm2'); - - const ClusterManager clusterManager1 = ClusterManager( - clusterManagerId: clusterManagerId1, - ); - const ClusterManager clusterManager2 = ClusterManager( - clusterManagerId: clusterManagerId2, - ); - - final Set clusterManagersSet1 = {}; - final Set clusterManagersSet2 = { - clusterManager1, - clusterManager2 - }; - final Set clusterManagersSet3 = { - clusterManager1 - }; - - final ClusterManagerUpdates clusterManagerUpdates1 = - ClusterManagerUpdates.from(clusterManagersSet1, clusterManagersSet2); - final Map serializedData1 = - serializeClusterManagerUpdates(clusterManagerUpdates1) - as Map; - expect(serializedData1['clusterManagersToAdd'], isNotNull); - final List clusterManagersToAdd1 = - serializedData1['clusterManagersToAdd']! as List; - expect(clusterManagersToAdd1.length, 2); - expect(serializedData1['clusterManagerIdsToRemove'], isNotNull); - final List clusterManagersToRemove1 = - serializedData1['clusterManagerIdsToRemove']! as List; - expect(clusterManagersToRemove1.length, 0); - - final ClusterManagerUpdates clusterManagerUpdates2 = - ClusterManagerUpdates.from(clusterManagersSet2, clusterManagersSet3); - serializeClusterManagerUpdates(clusterManagerUpdates2); - final Map serializedData2 = - serializeClusterManagerUpdates(clusterManagerUpdates2) - as Map; - expect(serializedData2['clusterManagersToAdd'], isNotNull); - final List clusterManagersToAdd2 = - serializedData2['clusterManagersToAdd']! as List; - expect(clusterManagersToAdd2.length, 0); - expect(serializedData1['clusterManagerIdsToRemove'], isNotNull); - final List clusterManagersToRemove2 = - serializedData2['clusterManagerIdsToRemove']! as List; - expect(clusterManagersToRemove2.length, 1); - expect(clusterManagersToRemove2.first as String, equals('cm2')); - }); -} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart b/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart index 109b9411a32..cfe6b976695 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart @@ -113,6 +113,35 @@ void main() { expect(bounds, expectedBounds); }); + test('moveCamera calls through', () async { + const int mapId = 1; + final (GoogleMapsFlutterAndroid maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + final CameraUpdate update = CameraUpdate.scrollBy(10, 20); + await maps.moveCamera(update, mapId: mapId); + + final VerificationResult verification = verify(api.moveCamera(captureAny)); + final PlatformCameraUpdate passedUpdate = + verification.captured[0] as PlatformCameraUpdate; + expect(passedUpdate.json, update.toJson()); + }); + + test('animateCamera calls through', () async { + const int mapId = 1; + final (GoogleMapsFlutterAndroid maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + final CameraUpdate update = CameraUpdate.scrollBy(10, 20); + await maps.animateCamera(update, mapId: mapId); + + final VerificationResult verification = + verify(api.animateCamera(captureAny)); + final PlatformCameraUpdate passedUpdate = + verification.captured[0] as PlatformCameraUpdate; + expect(passedUpdate.json, update.toJson()); + }); + test('getZoomLevel passes values correctly', () async { const int mapId = 1; final (GoogleMapsFlutterAndroid maps, MockMapsApi api) = @@ -183,6 +212,258 @@ void main() { verify(api.clearTileCache(tileOverlayId)); }); + test('updateMapConfiguration passes expected arguments', () async { + const int mapId = 1; + final (GoogleMapsFlutterAndroid maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + // Set some arbitrary options. + const MapConfiguration config = MapConfiguration( + compassEnabled: true, + liteModeEnabled: false, + mapType: MapType.terrain, + ); + await maps.updateMapConfiguration(config, mapId: mapId); + + final VerificationResult verification = + verify(api.updateMapConfiguration(captureAny)); + final PlatformMapConfiguration passedConfig = + verification.captured[0] as PlatformMapConfiguration; + final Map passedConfigJson = + passedConfig.json as Map; + // Each set option should be present. + expect(passedConfigJson['compassEnabled'], true); + expect(passedConfigJson['liteModeEnabled'], false); + expect(passedConfigJson['mapType'], MapType.terrain.index); + // Spot-check that unset options are not be present. + expect(passedConfigJson['myLocationEnabled'], isNull); + expect(passedConfigJson['cameraTargetBounds'], isNull); + expect(passedConfigJson['padding'], isNull); + }); + + test('updateMapOptions passes expected arguments', () async { + const int mapId = 1; + final (GoogleMapsFlutterAndroid maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + // Set some arbitrary options. + final Map config = { + 'compassEnabled': true, + 'liteModeEnabled': false, + 'mapType': MapType.terrain.index, + }; + await maps.updateMapOptions(config, mapId: mapId); + + final VerificationResult verification = + verify(api.updateMapConfiguration(captureAny)); + final PlatformMapConfiguration passedConfig = + verification.captured[0] as PlatformMapConfiguration; + final Map passedConfigJson = + passedConfig.json as Map; + // Each set option should be present. + expect(passedConfigJson['compassEnabled'], true); + expect(passedConfigJson['liteModeEnabled'], false); + expect(passedConfigJson['mapType'], MapType.terrain.index); + // Spot-check that unset options are not be present. + expect(passedConfigJson['myLocationEnabled'], isNull); + expect(passedConfigJson['cameraTargetBounds'], isNull); + expect(passedConfigJson['padding'], isNull); + }); + + test('updateCircles passes expected arguments', () async { + const int mapId = 1; + final (GoogleMapsFlutterAndroid maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const Circle object1 = Circle(circleId: CircleId('1')); + const Circle object2old = Circle(circleId: CircleId('2')); + final Circle object2new = object2old.copyWith(radiusParam: 42); + const Circle object3 = Circle(circleId: CircleId('3')); + await maps.updateCircles( + CircleUpdates.from( + {object1, object2old}, {object2new, object3}), + mapId: mapId); + + final VerificationResult verification = + verify(api.updateCircles(captureAny, captureAny, captureAny)); + final List toAdd = + verification.captured[0] as List; + final List toChange = + verification.captured[1] as List; + final List toRemove = verification.captured[2] as List; + // Object one should be removed. + expect(toRemove.length, 1); + expect(toRemove.first, object1.circleId.value); + // Object two should be changed. + expect(toChange.length, 1); + expect(toChange.first?.json, object2new.toJson()); + // Object 3 should be added. + expect(toAdd.length, 1); + expect(toAdd.first?.json, object3.toJson()); + }); + + test('updateClusterManagers passes expected arguments', () async { + const int mapId = 1; + final (GoogleMapsFlutterAndroid maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const ClusterManager object1 = + ClusterManager(clusterManagerId: ClusterManagerId('1')); + const ClusterManager object3 = + ClusterManager(clusterManagerId: ClusterManagerId('3')); + await maps.updateClusterManagers( + ClusterManagerUpdates.from( + {object1}, {object3}), + mapId: mapId); + + final VerificationResult verification = + verify(api.updateClusterManagers(captureAny, captureAny)); + final List toAdd = + verification.captured[0] as List; + final List toRemove = verification.captured[1] as List; + // Object one should be removed. + expect(toRemove.length, 1); + expect(toRemove.first, object1.clusterManagerId.value); + // Unlike other map object types, changes are not possible for cluster + // managers, since they have no non-ID properties. + // Object 3 should be added. + expect(toAdd.length, 1); + expect(toAdd.first?.identifier, object3.clusterManagerId.value); + }); + + test('updateMarkers passes expected arguments', () async { + const int mapId = 1; + final (GoogleMapsFlutterAndroid maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const Marker object1 = Marker(markerId: MarkerId('1')); + const Marker object2old = Marker(markerId: MarkerId('2')); + final Marker object2new = object2old.copyWith(rotationParam: 42); + const Marker object3 = Marker(markerId: MarkerId('3')); + await maps.updateMarkers( + MarkerUpdates.from( + {object1, object2old}, {object2new, object3}), + mapId: mapId); + + final VerificationResult verification = + verify(api.updateMarkers(captureAny, captureAny, captureAny)); + final List toAdd = + verification.captured[0] as List; + final List toChange = + verification.captured[1] as List; + final List toRemove = verification.captured[2] as List; + // Object one should be removed. + expect(toRemove.length, 1); + expect(toRemove.first, object1.markerId.value); + // Object two should be changed. + expect(toChange.length, 1); + expect(toChange.first?.json, object2new.toJson()); + // Object 3 should be added. + expect(toAdd.length, 1); + expect(toAdd.first?.json, object3.toJson()); + }); + + test('updatePolygons passes expected arguments', () async { + const int mapId = 1; + final (GoogleMapsFlutterAndroid maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const Polygon object1 = Polygon(polygonId: PolygonId('1')); + const Polygon object2old = Polygon(polygonId: PolygonId('2')); + final Polygon object2new = object2old.copyWith(strokeWidthParam: 42); + const Polygon object3 = Polygon(polygonId: PolygonId('3')); + await maps.updatePolygons( + PolygonUpdates.from( + {object1, object2old}, {object2new, object3}), + mapId: mapId); + + final VerificationResult verification = + verify(api.updatePolygons(captureAny, captureAny, captureAny)); + final List toAdd = + verification.captured[0] as List; + final List toChange = + verification.captured[1] as List; + final List toRemove = verification.captured[2] as List; + // Object one should be removed. + expect(toRemove.length, 1); + expect(toRemove.first, object1.polygonId.value); + // Object two should be changed. + expect(toChange.length, 1); + expect(toChange.first?.json, object2new.toJson()); + // Object 3 should be added. + expect(toAdd.length, 1); + expect(toAdd.first?.json, object3.toJson()); + }); + + test('updatePolylines passes expected arguments', () async { + const int mapId = 1; + final (GoogleMapsFlutterAndroid maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const Polyline object1 = Polyline(polylineId: PolylineId('1')); + const Polyline object2old = Polyline(polylineId: PolylineId('2')); + final Polyline object2new = object2old.copyWith(widthParam: 42); + const Polyline object3 = Polyline(polylineId: PolylineId('3')); + await maps.updatePolylines( + PolylineUpdates.from( + {object1, object2old}, {object2new, object3}), + mapId: mapId); + + final VerificationResult verification = + verify(api.updatePolylines(captureAny, captureAny, captureAny)); + final List toAdd = + verification.captured[0] as List; + final List toChange = + verification.captured[1] as List; + final List toRemove = verification.captured[2] as List; + // Object one should be removed. + expect(toRemove.length, 1); + expect(toRemove.first, object1.polylineId.value); + // Object two should be changed. + expect(toChange.length, 1); + expect(toChange.first?.json, object2new.toJson()); + // Object 3 should be added. + expect(toAdd.length, 1); + expect(toAdd.first?.json, object3.toJson()); + }); + + test('updateTileOverlays passes expected arguments', () async { + const int mapId = 1; + final (GoogleMapsFlutterAndroid maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const TileOverlay object1 = TileOverlay(tileOverlayId: TileOverlayId('1')); + const TileOverlay object2old = + TileOverlay(tileOverlayId: TileOverlayId('2')); + final TileOverlay object2new = object2old.copyWith(zIndexParam: 42); + const TileOverlay object3 = TileOverlay(tileOverlayId: TileOverlayId('3')); + // Pre-set the initial state, since this update method doesn't take the old + // state. + await maps.updateTileOverlays( + newTileOverlays: {object1, object2old}, mapId: mapId); + clearInteractions(api); + + await maps.updateTileOverlays( + newTileOverlays: {object2new, object3}, mapId: mapId); + + final VerificationResult verification = + verify(api.updateTileOverlays(captureAny, captureAny, captureAny)); + final List toAdd = + verification.captured[0] as List; + final List toChange = + verification.captured[1] as List; + final List toRemove = verification.captured[2] as List; + // Object one should be removed. + expect(toRemove.length, 1); + expect(toRemove.first, object1.tileOverlayId.value); + // Object two should be changed. + expect(toChange.length, 1); + expect(toChange.first?.json, object2new.toJson()); + // Object 3 should be added. + expect(toAdd.length, 1); + expect(toAdd.first?.json, object3.toJson()); + }); + test('markers send drag event to correct streams', () async { const int mapId = 1; final Map jsonMarkerDragStartEvent = { diff --git a/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.mocks.dart index f819660e213..c49d6268926 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.mocks.dart @@ -68,6 +68,130 @@ class MockMapsApi extends _i1.Mock implements _i2.MapsApi { returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); + @override + _i3.Future updateMapConfiguration( + _i2.PlatformMapConfiguration? configuration) => + (super.noSuchMethod( + Invocation.method( + #updateMapConfiguration, + [configuration], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future updateCircles( + List<_i2.PlatformCircle?>? toAdd, + List<_i2.PlatformCircle?>? toChange, + List? idsToRemove, + ) => + (super.noSuchMethod( + Invocation.method( + #updateCircles, + [ + toAdd, + toChange, + idsToRemove, + ], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future updateClusterManagers( + List<_i2.PlatformClusterManager?>? toAdd, + List? idsToRemove, + ) => + (super.noSuchMethod( + Invocation.method( + #updateClusterManagers, + [ + toAdd, + idsToRemove, + ], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future updateMarkers( + List<_i2.PlatformMarker?>? toAdd, + List<_i2.PlatformMarker?>? toChange, + List? idsToRemove, + ) => + (super.noSuchMethod( + Invocation.method( + #updateMarkers, + [ + toAdd, + toChange, + idsToRemove, + ], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future updatePolygons( + List<_i2.PlatformPolygon?>? toAdd, + List<_i2.PlatformPolygon?>? toChange, + List? idsToRemove, + ) => + (super.noSuchMethod( + Invocation.method( + #updatePolygons, + [ + toAdd, + toChange, + idsToRemove, + ], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future updatePolylines( + List<_i2.PlatformPolyline?>? toAdd, + List<_i2.PlatformPolyline?>? toChange, + List? idsToRemove, + ) => + (super.noSuchMethod( + Invocation.method( + #updatePolylines, + [ + toAdd, + toChange, + idsToRemove, + ], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future updateTileOverlays( + List<_i2.PlatformTileOverlay?>? toAdd, + List<_i2.PlatformTileOverlay?>? toChange, + List? idsToRemove, + ) => + (super.noSuchMethod( + Invocation.method( + #updateTileOverlays, + [ + toAdd, + toChange, + idsToRemove, + ], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + @override _i3.Future<_i2.PlatformPoint> getScreenCoordinate( _i2.PlatformLatLng? latLng) => @@ -143,6 +267,28 @@ class MockMapsApi extends _i1.Mock implements _i2.MapsApi { )), ) as _i3.Future<_i2.PlatformLatLngBounds>); + @override + _i3.Future moveCamera(_i2.PlatformCameraUpdate? cameraUpdate) => + (super.noSuchMethod( + Invocation.method( + #moveCamera, + [cameraUpdate], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future animateCamera(_i2.PlatformCameraUpdate? cameraUpdate) => + (super.noSuchMethod( + Invocation.method( + #animateCamera, + [cameraUpdate], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + @override _i3.Future getZoomLevel() => (super.noSuchMethod( Invocation.method( diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md index 6fc61e8eefc..4802eb9d144 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.1 + +* Improves Objective-C type handling. + ## 2.8.0 * Adds compatibility with SDK version 9.x for apps targetting iOS 15+. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m index cc654606d8c..e75fc046912 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m @@ -16,6 +16,28 @@ @interface FLTGoogleMapJSONConversionsTests : XCTestCase @implementation FLTGoogleMapJSONConversionsTests +- (void)testGetValueOrNilWithValue { + NSString *key = @"key"; + NSString *value = @"value"; + NSDictionary *dict = @{key : value}; + + XCTAssertEqual(FGMGetValueOrNilFromDict(dict, key), value); +} + +- (void)testGetValueOrNilWithNoEntry { + NSString *key = @"key"; + NSDictionary *dict = @{}; + + XCTAssertNil(FGMGetValueOrNilFromDict(dict, key)); +} + +- (void)testGetValueOrNilWithNSNull { + NSString *key = @"key"; + NSDictionary *dict = @{key : [NSNull null]}; + + XCTAssertNil(FGMGetValueOrNilFromDict(dict, key)); +} + - (void)testLocationFromLatLong { NSArray *latlong = @[ @1, @2 ]; CLLocationCoordinate2D location = [FLTGoogleMapJSONConversions locationFromLatLong:latlong]; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h index 1fc8d003d91..c6f9fc76eba 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h @@ -7,6 +7,9 @@ NS_ASSUME_NONNULL_BEGIN +/// Returns dict[key], or nil if dict[key] is NSNull. +extern id _Nullable FGMGetValueOrNilFromDict(NSDictionary *dict, NSString *key); + @interface FLTGoogleMapJSONConversions : NSObject + (CLLocationCoordinate2D)locationFromLatLong:(NSArray *)latlong; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m index 28307182f2c..f6ea7690222 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m @@ -4,6 +4,12 @@ #import "FLTGoogleMapJSONConversions.h" +/// Returns dict[key], or nil if dict[key] is NSNull. +id FGMGetValueOrNilFromDict(NSDictionary *dict, NSString *key) { + id value = dict[key]; + return value == [NSNull null] ? nil : value; +} + @implementation FLTGoogleMapJSONConversions + (CLLocationCoordinate2D)locationFromLatLong:(NSArray *)latlong { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m index 73eab6c1ead..bc56671f5cd 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m @@ -70,28 +70,28 @@ - (void)interpretTileOverlayOptions:(NSDictionary *)data { if (!data) { return; } - NSNumber *visible = data[@"visible"]; - if (visible != nil && visible != (id)[NSNull null]) { + NSNumber *visible = FGMGetValueOrNilFromDict(data, @"visible"); + if (visible) { [self setVisible:visible.boolValue]; } - NSNumber *transparency = data[@"transparency"]; - if (transparency != nil && transparency != (id)[NSNull null]) { + NSNumber *transparency = FGMGetValueOrNilFromDict(data, @"transparency"); + if (transparency) { [self setTransparency:transparency.floatValue]; } - NSNumber *zIndex = data[@"zIndex"]; - if (zIndex != nil && zIndex != (id)[NSNull null]) { + NSNumber *zIndex = FGMGetValueOrNilFromDict(data, @"zIndex"); + if (zIndex) { [self setZIndex:zIndex.intValue]; } - NSNumber *fadeIn = data[@"fadeIn"]; - if (fadeIn != nil && fadeIn != (id)[NSNull null]) { + NSNumber *fadeIn = FGMGetValueOrNilFromDict(data, @"fadeIn"); + if (fadeIn) { [self setFadeIn:fadeIn.boolValue]; } - NSNumber *tileSize = data[@"tileSize"]; - if (tileSize != nil && tileSize != (id)[NSNull null]) { + NSNumber *tileSize = FGMGetValueOrNilFromDict(data, @"tileSize"); + if (tileSize) { [self setTileSize:tileSize.integerValue]; } } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m index 53bf69075c9..1eb74a19559 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m @@ -60,43 +60,43 @@ - (void)setFillColor:(UIColor *)color { } - (void)interpretCircleOptions:(NSDictionary *)data { - NSNumber *consumeTapEvents = data[@"consumeTapEvents"]; - if (consumeTapEvents && consumeTapEvents != (id)[NSNull null]) { + NSNumber *consumeTapEvents = FGMGetValueOrNilFromDict(data, @"consumeTapEvents"); + if (consumeTapEvents) { [self setConsumeTapEvents:consumeTapEvents.boolValue]; } - NSNumber *visible = data[@"visible"]; - if (visible && visible != (id)[NSNull null]) { + NSNumber *visible = FGMGetValueOrNilFromDict(data, @"visible"); + if (visible) { [self setVisible:[visible boolValue]]; } - NSNumber *zIndex = data[@"zIndex"]; - if (zIndex && zIndex != (id)[NSNull null]) { + NSNumber *zIndex = FGMGetValueOrNilFromDict(data, @"zIndex"); + if (zIndex) { [self setZIndex:[zIndex intValue]]; } - NSArray *center = data[@"center"]; - if (center && center != (id)[NSNull null]) { + NSArray *center = FGMGetValueOrNilFromDict(data, @"center"); + if (center) { [self setCenter:[FLTGoogleMapJSONConversions locationFromLatLong:center]]; } - NSNumber *radius = data[@"radius"]; - if (radius && radius != (id)[NSNull null]) { + NSNumber *radius = FGMGetValueOrNilFromDict(data, @"radius"); + if (radius) { [self setRadius:[radius floatValue]]; } - NSNumber *strokeColor = data[@"strokeColor"]; - if (strokeColor && strokeColor != (id)[NSNull null]) { + NSNumber *strokeColor = FGMGetValueOrNilFromDict(data, @"strokeColor"); + if (strokeColor) { [self setStrokeColor:[FLTGoogleMapJSONConversions colorFromRGBA:strokeColor]]; } - NSNumber *strokeWidth = data[@"strokeWidth"]; - if (strokeWidth && strokeWidth != (id)[NSNull null]) { + NSNumber *strokeWidth = FGMGetValueOrNilFromDict(data, @"strokeWidth"); + if (strokeWidth) { [self setStrokeWidth:[strokeWidth intValue]]; } - NSNumber *fillColor = data[@"fillColor"]; - if (fillColor && fillColor != (id)[NSNull null]) { + NSNumber *fillColor = FGMGetValueOrNilFromDict(data, @"fillColor"); + if (fillColor) { [self setFillColor:[FLTGoogleMapJSONConversions colorFromRGBA:fillColor]]; } } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m index 4c747f3eeba..08fdf8cc7f4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m @@ -581,41 +581,41 @@ - (void)mapView:(GMSMapView *)mapView didLongPressAtCoordinate:(CLLocationCoordi } - (void)interpretMapOptions:(NSDictionary *)data { - NSArray *cameraTargetBounds = data[@"cameraTargetBounds"]; - if (cameraTargetBounds && cameraTargetBounds != (id)[NSNull null]) { + NSArray *cameraTargetBounds = FGMGetValueOrNilFromDict(data, @"cameraTargetBounds"); + if (cameraTargetBounds) { [self setCameraTargetBounds:cameraTargetBounds.count > 0 && cameraTargetBounds[0] != [NSNull null] ? [FLTGoogleMapJSONConversions coordinateBoundsFromLatLongs:cameraTargetBounds.firstObject] : nil]; } - NSNumber *compassEnabled = data[@"compassEnabled"]; - if (compassEnabled && compassEnabled != (id)[NSNull null]) { + NSNumber *compassEnabled = FGMGetValueOrNilFromDict(data, @"compassEnabled"); + if (compassEnabled) { [self setCompassEnabled:[compassEnabled boolValue]]; } - id indoorEnabled = data[@"indoorEnabled"]; - if (indoorEnabled && indoorEnabled != [NSNull null]) { + id indoorEnabled = FGMGetValueOrNilFromDict(data, @"indoorEnabled"); + if (indoorEnabled) { [self setIndoorEnabled:[indoorEnabled boolValue]]; } - id trafficEnabled = data[@"trafficEnabled"]; - if (trafficEnabled && trafficEnabled != [NSNull null]) { + id trafficEnabled = FGMGetValueOrNilFromDict(data, @"trafficEnabled"); + if (trafficEnabled) { [self setTrafficEnabled:[trafficEnabled boolValue]]; } - id buildingsEnabled = data[@"buildingsEnabled"]; - if (buildingsEnabled && buildingsEnabled != [NSNull null]) { + id buildingsEnabled = FGMGetValueOrNilFromDict(data, @"buildingsEnabled"); + if (buildingsEnabled) { [self setBuildingsEnabled:[buildingsEnabled boolValue]]; } - id mapType = data[@"mapType"]; - if (mapType && mapType != [NSNull null]) { + id mapType = FGMGetValueOrNilFromDict(data, @"mapType"); + if (mapType) { [self setMapType:[FLTGoogleMapJSONConversions mapViewTypeFromTypeValue:mapType]]; } - NSArray *zoomData = data[@"minMaxZoomPreference"]; - if (zoomData && zoomData != (id)[NSNull null]) { + NSArray *zoomData = FGMGetValueOrNilFromDict(data, @"minMaxZoomPreference"); + if (zoomData) { float minZoom = (zoomData[0] == [NSNull null]) ? kGMSMinZoomLevel : [zoomData[0] floatValue]; float maxZoom = (zoomData[1] == [NSNull null]) ? kGMSMaxZoomLevel : [zoomData[1] floatValue]; [self setMinZoom:minZoom maxZoom:maxZoom]; } - NSArray *paddingData = data[@"padding"]; + NSArray *paddingData = FGMGetValueOrNilFromDict(data, @"padding"); if (paddingData) { float top = (paddingData[0] == [NSNull null]) ? 0 : [paddingData[0] floatValue]; float left = (paddingData[1] == [NSNull null]) ? 0 : [paddingData[1] floatValue]; @@ -624,35 +624,35 @@ - (void)interpretMapOptions:(NSDictionary *)data { [self setPaddingTop:top left:left bottom:bottom right:right]; } - NSNumber *rotateGesturesEnabled = data[@"rotateGesturesEnabled"]; - if (rotateGesturesEnabled && rotateGesturesEnabled != (id)[NSNull null]) { + NSNumber *rotateGesturesEnabled = FGMGetValueOrNilFromDict(data, @"rotateGesturesEnabled"); + if (rotateGesturesEnabled) { [self setRotateGesturesEnabled:[rotateGesturesEnabled boolValue]]; } - NSNumber *scrollGesturesEnabled = data[@"scrollGesturesEnabled"]; - if (scrollGesturesEnabled && scrollGesturesEnabled != (id)[NSNull null]) { + NSNumber *scrollGesturesEnabled = FGMGetValueOrNilFromDict(data, @"scrollGesturesEnabled"); + if (scrollGesturesEnabled) { [self setScrollGesturesEnabled:[scrollGesturesEnabled boolValue]]; } - NSNumber *tiltGesturesEnabled = data[@"tiltGesturesEnabled"]; - if (tiltGesturesEnabled && tiltGesturesEnabled != (id)[NSNull null]) { + NSNumber *tiltGesturesEnabled = FGMGetValueOrNilFromDict(data, @"tiltGesturesEnabled"); + if (tiltGesturesEnabled) { [self setTiltGesturesEnabled:[tiltGesturesEnabled boolValue]]; } - NSNumber *trackCameraPosition = data[@"trackCameraPosition"]; - if (trackCameraPosition && trackCameraPosition != (id)[NSNull null]) { + NSNumber *trackCameraPosition = FGMGetValueOrNilFromDict(data, @"trackCameraPosition"); + if (trackCameraPosition) { [self setTrackCameraPosition:[trackCameraPosition boolValue]]; } - NSNumber *zoomGesturesEnabled = data[@"zoomGesturesEnabled"]; - if (zoomGesturesEnabled && zoomGesturesEnabled != (id)[NSNull null]) { + NSNumber *zoomGesturesEnabled = FGMGetValueOrNilFromDict(data, @"zoomGesturesEnabled"); + if (zoomGesturesEnabled) { [self setZoomGesturesEnabled:[zoomGesturesEnabled boolValue]]; } - NSNumber *myLocationEnabled = data[@"myLocationEnabled"]; - if (myLocationEnabled && myLocationEnabled != (id)[NSNull null]) { + NSNumber *myLocationEnabled = FGMGetValueOrNilFromDict(data, @"myLocationEnabled"); + if (myLocationEnabled) { [self setMyLocationEnabled:[myLocationEnabled boolValue]]; } - NSNumber *myLocationButtonEnabled = data[@"myLocationButtonEnabled"]; - if (myLocationButtonEnabled && myLocationButtonEnabled != (id)[NSNull null]) { + NSNumber *myLocationButtonEnabled = FGMGetValueOrNilFromDict(data, @"myLocationButtonEnabled"); + if (myLocationButtonEnabled) { [self setMyLocationButtonEnabled:[myLocationButtonEnabled boolValue]]; } - NSString *style = data[@"style"]; + NSString *style = FGMGetValueOrNilFromDict(data, @"style"); if (style) { self.styleError = [self setMapStyle:style]; } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m index b816e7ee914..1bc7d2a8db8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m @@ -93,60 +93,60 @@ - (void)setZIndex:(int)zIndex { - (void)interpretMarkerOptions:(NSDictionary *)data registrar:(NSObject *)registrar screenScale:(CGFloat)screenScale { - NSNumber *alpha = data[@"alpha"]; - if (alpha && alpha != (id)[NSNull null]) { + NSNumber *alpha = FGMGetValueOrNilFromDict(data, @"alpha"); + if (alpha) { [self setAlpha:[alpha floatValue]]; } - NSArray *anchor = data[@"anchor"]; - if (anchor && anchor != (id)[NSNull null]) { + NSArray *anchor = FGMGetValueOrNilFromDict(data, @"anchor"); + if (anchor) { [self setAnchor:[FLTGoogleMapJSONConversions pointFromArray:anchor]]; } - NSNumber *draggable = data[@"draggable"]; - if (draggable && draggable != (id)[NSNull null]) { + NSNumber *draggable = FGMGetValueOrNilFromDict(data, @"draggable"); + if (draggable) { [self setDraggable:[draggable boolValue]]; } - NSArray *icon = data[@"icon"]; - if (icon && icon != (id)[NSNull null]) { + NSArray *icon = FGMGetValueOrNilFromDict(data, @"icon"); + if (icon) { UIImage *image = [self extractIconFromData:icon registrar:registrar screenScale:screenScale]; [self setIcon:image]; } - NSNumber *flat = data[@"flat"]; - if (flat && flat != (id)[NSNull null]) { + NSNumber *flat = FGMGetValueOrNilFromDict(data, @"flat"); + if (flat) { [self setFlat:[flat boolValue]]; } - NSNumber *consumeTapEvents = data[@"consumeTapEvents"]; - if (consumeTapEvents && consumeTapEvents != (id)[NSNull null]) { + NSNumber *consumeTapEvents = FGMGetValueOrNilFromDict(data, @"consumeTapEvents"); + if (consumeTapEvents) { [self setConsumeTapEvents:[consumeTapEvents boolValue]]; } [self interpretInfoWindow:data]; - NSArray *position = data[@"position"]; - if (position && position != (id)[NSNull null]) { + NSArray *position = FGMGetValueOrNilFromDict(data, @"position"); + if (position) { [self setPosition:[FLTGoogleMapJSONConversions locationFromLatLong:position]]; } - NSNumber *rotation = data[@"rotation"]; - if (rotation && rotation != (id)[NSNull null]) { + NSNumber *rotation = FGMGetValueOrNilFromDict(data, @"rotation"); + if (rotation) { [self setRotation:[rotation doubleValue]]; } - NSNumber *visible = data[@"visible"]; - if (visible && visible != (id)[NSNull null]) { + NSNumber *visible = FGMGetValueOrNilFromDict(data, @"visible"); + if (visible) { [self setVisible:[visible boolValue]]; } - NSNumber *zIndex = data[@"zIndex"]; - if (zIndex && zIndex != (id)[NSNull null]) { + NSNumber *zIndex = FGMGetValueOrNilFromDict(data, @"zIndex"); + if (zIndex) { [self setZIndex:[zIndex intValue]]; } } - (void)interpretInfoWindow:(NSDictionary *)data { - NSDictionary *infoWindow = data[@"infoWindow"]; - if (infoWindow && infoWindow != (id)[NSNull null]) { - NSString *title = infoWindow[@"title"]; - NSString *snippet = infoWindow[@"snippet"]; - if (title && title != (id)[NSNull null]) { + NSDictionary *infoWindow = FGMGetValueOrNilFromDict(data, @"infoWindow"); + if (infoWindow) { + NSString *title = FGMGetValueOrNilFromDict(infoWindow, @"title"); + NSString *snippet = FGMGetValueOrNilFromDict(infoWindow, @"snippet"); + if (title) { [self setInfoWindowTitle:title snippet:snippet]; } NSArray *infoWindowAnchor = infoWindow[@"infoWindowAnchor"]; - if (infoWindowAnchor && infoWindowAnchor != (id)[NSNull null]) { + if (infoWindowAnchor) { [self setInfoWindowAnchor:[FLTGoogleMapJSONConversions pointFromArray:infoWindowAnchor]]; } } @@ -221,15 +221,16 @@ - (UIImage *)extractIconFromData:(NSArray *)iconData @throw exception; } - NSString *assetName = assetData[@"assetName"]; - NSString *scalingMode = assetData[@"bitmapScaling"]; + NSString *assetName = FGMGetValueOrNilFromDict(assetData, @"assetName"); + NSString *scalingMode = FGMGetValueOrNilFromDict(assetData, @"bitmapScaling"); image = [UIImage imageNamed:[registrar lookupKeyForAsset:assetName]]; if ([scalingMode isEqualToString:@"auto"]) { - NSNumber *width = assetData[@"width"]; - NSNumber *height = assetData[@"height"]; - CGFloat imagePixelRatio = [assetData[@"imagePixelRatio"] doubleValue]; + NSNumber *width = FGMGetValueOrNilFromDict(assetData, @"width"); + NSNumber *height = FGMGetValueOrNilFromDict(assetData, @"height"); + CGFloat imagePixelRatio = + [FGMGetValueOrNilFromDict(assetData, @"imagePixelRatio") doubleValue]; if (width || height) { image = [FLTGoogleMapMarkerController scaledImage:image withScale:screenScale]; @@ -252,15 +253,16 @@ - (UIImage *)extractIconFromData:(NSArray *)iconData @throw exception; } - FlutterStandardTypedData *bytes = byteData[@"byteData"]; - NSString *scalingMode = byteData[@"bitmapScaling"]; + FlutterStandardTypedData *bytes = FGMGetValueOrNilFromDict(byteData, @"byteData"); + NSString *scalingMode = FGMGetValueOrNilFromDict(byteData, @"bitmapScaling"); @try { image = [UIImage imageWithData:[bytes data] scale:screenScale]; if ([scalingMode isEqualToString:@"auto"]) { - NSNumber *width = byteData[@"width"]; - NSNumber *height = byteData[@"height"]; - CGFloat imagePixelRatio = [byteData[@"imagePixelRatio"] doubleValue]; + NSNumber *width = FGMGetValueOrNilFromDict(byteData, @"width"); + NSNumber *height = FGMGetValueOrNilFromDict(byteData, @"height"); + CGFloat imagePixelRatio = + [FGMGetValueOrNilFromDict(byteData, @"imagePixelRatio") doubleValue]; if (width || height) { // Before scaling the image, image must be in screenScale diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m index 398adfcacec..d8dc47def60 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m @@ -73,43 +73,43 @@ - (void)setStrokeWidth:(CGFloat)width { - (void)interpretPolygonOptions:(NSDictionary *)data registrar:(NSObject *)registrar { - NSNumber *consumeTapEvents = data[@"consumeTapEvents"]; - if (consumeTapEvents && consumeTapEvents != (id)[NSNull null]) { + NSNumber *consumeTapEvents = FGMGetValueOrNilFromDict(data, @"consumeTapEvents"); + if (consumeTapEvents) { [self setConsumeTapEvents:[consumeTapEvents boolValue]]; } - NSNumber *visible = data[@"visible"]; - if (visible && visible != (id)[NSNull null]) { + NSNumber *visible = FGMGetValueOrNilFromDict(data, @"visible"); + if (visible) { [self setVisible:[visible boolValue]]; } - NSNumber *zIndex = data[@"zIndex"]; - if (zIndex && zIndex != (id)[NSNull null]) { + NSNumber *zIndex = FGMGetValueOrNilFromDict(data, @"zIndex"); + if (zIndex) { [self setZIndex:[zIndex intValue]]; } - NSArray *points = data[@"points"]; - if (points && points != (id)[NSNull null]) { + NSArray *points = FGMGetValueOrNilFromDict(data, @"points"); + if (points) { [self setPoints:[FLTGoogleMapJSONConversions pointsFromLatLongs:points]]; } - NSArray *holes = data[@"holes"]; - if (holes && holes != (id)[NSNull null]) { + NSArray *holes = FGMGetValueOrNilFromDict(data, @"holes"); + if (holes) { [self setHoles:[FLTGoogleMapJSONConversions holesFromPointsArray:holes]]; } - NSNumber *fillColor = data[@"fillColor"]; - if (fillColor && fillColor != (id)[NSNull null]) { + NSNumber *fillColor = FGMGetValueOrNilFromDict(data, @"fillColor"); + if (fillColor) { [self setFillColor:[FLTGoogleMapJSONConversions colorFromRGBA:fillColor]]; } - NSNumber *strokeColor = data[@"strokeColor"]; - if (strokeColor && strokeColor != (id)[NSNull null]) { + NSNumber *strokeColor = FGMGetValueOrNilFromDict(data, @"strokeColor"); + if (strokeColor) { [self setStrokeColor:[FLTGoogleMapJSONConversions colorFromRGBA:strokeColor]]; } - NSNumber *strokeWidth = data[@"strokeWidth"]; - if (strokeWidth && strokeWidth != (id)[NSNull null]) { + NSNumber *strokeWidth = FGMGetValueOrNilFromDict(data, @"strokeWidth"); + if (strokeWidth) { [self setStrokeWidth:[strokeWidth intValue]]; } } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m index 47334576b1c..a97f372d6fd 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m @@ -12,12 +12,6 @@ @interface FLTGoogleMapPolylineController () @end -/// Returns dict[key], or nil if dict[key] is NSNull. -static id GetValueOrNilFromDict(NSDictionary *dict, NSString *key) { - id value = dict[key]; - return value == [NSNull null] ? nil : value; -} - @implementation FLTGoogleMapPolylineController - (instancetype)initPolylineWithPath:(GMSMutablePath *)path @@ -71,42 +65,42 @@ - (void)setPattern:(NSArray *)styles lengths:(NSArray *)registrar { - NSNumber *consumeTapEvents = GetValueOrNilFromDict(data, @"consumeTapEvents"); + NSNumber *consumeTapEvents = FGMGetValueOrNilFromDict(data, @"consumeTapEvents"); if (consumeTapEvents) { [self setConsumeTapEvents:[consumeTapEvents boolValue]]; } - NSNumber *visible = GetValueOrNilFromDict(data, @"visible"); + NSNumber *visible = FGMGetValueOrNilFromDict(data, @"visible"); if (visible) { [self setVisible:[visible boolValue]]; } - NSNumber *zIndex = GetValueOrNilFromDict(data, @"zIndex"); + NSNumber *zIndex = FGMGetValueOrNilFromDict(data, @"zIndex"); if (zIndex) { [self setZIndex:[zIndex intValue]]; } - NSArray *points = GetValueOrNilFromDict(data, @"points"); + NSArray *points = FGMGetValueOrNilFromDict(data, @"points"); if (points) { [self setPoints:[FLTGoogleMapJSONConversions pointsFromLatLongs:points]]; } - NSNumber *strokeColor = GetValueOrNilFromDict(data, @"color"); + NSNumber *strokeColor = FGMGetValueOrNilFromDict(data, @"color"); if (strokeColor) { [self setColor:[FLTGoogleMapJSONConversions colorFromRGBA:strokeColor]]; } - NSNumber *strokeWidth = GetValueOrNilFromDict(data, @"width"); + NSNumber *strokeWidth = FGMGetValueOrNilFromDict(data, @"width"); if (strokeWidth) { [self setStrokeWidth:[strokeWidth intValue]]; } - NSNumber *geodesic = GetValueOrNilFromDict(data, @"geodesic"); + NSNumber *geodesic = FGMGetValueOrNilFromDict(data, @"geodesic"); if (geodesic) { [self setGeodesic:geodesic.boolValue]; } - NSArray *patterns = GetValueOrNilFromDict(data, @"pattern"); + NSArray *patterns = FGMGetValueOrNilFromDict(data, @"pattern"); if (patterns) { [self setPattern:[FLTGoogleMapJSONConversions strokeStylesFromPatterns:patterns diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml index e2dc69dd1bb..0314d44e46f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_ios description: iOS implementation of the google_maps_flutter plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.8.0 +version: 2.8.1 environment: sdk: ^3.2.3 diff --git a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle index bd28bd93ed0..5f0fecc642e 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle @@ -69,5 +69,5 @@ dependencies { testImplementation 'androidx.test:core:1.5.0' testImplementation 'org.robolectric:robolectric:4.10.3' androidTestImplementation 'androidx.test:runner:1.5.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' } diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 5f74d5c8e6b..435e2ed98ab 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.41 + +* Updates espresso to 3.6.1. + ## 1.0.40 * Updates androidx.core version to 1.13.1. diff --git a/packages/local_auth/local_auth_android/android/build.gradle b/packages/local_auth/local_auth_android/android/build.gradle index b2326e4b212..57059c3dc56 100644 --- a/packages/local_auth/local_auth_android/android/build.gradle +++ b/packages/local_auth/local_auth_android/android/build.gradle @@ -67,7 +67,7 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.10.3' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:rules:1.2.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' // TODO(camsim99): org.jetbrains.kotlin:kotlin-bom artifact purpose is to align kotlin stdlib and related code versions. // This should be removed when https://github.com/flutter/flutter/issues/125062 is fixed. implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.10")) diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index 4352b3b8299..30c542ff3de 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/packages/tree/main/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.40 +version: 1.0.41 environment: sdk: ^3.4.0 diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 91b9f02c057..48b5e0b7eac 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.9.1 + +* Updates minimum web implementation version to ensure support for + the new `webOptions` exposed in `2.9.0`. + ## 2.9.0 * Exports types: `VideoPlayerWebOptions` and `VideoPlayerWebOptionsControls` to diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index aff4a9700c7..ecc85cfa25e 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,10 +3,10 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.9.0 +version: 2.9.1 environment: - sdk: ">=3.2.3 <4.0.0" + sdk: ^3.2.3 flutter: ">=3.16.6" flutter: @@ -28,7 +28,7 @@ dependencies: video_player_android: ^2.3.5 video_player_avfoundation: ^2.5.6 video_player_platform_interface: ^6.2.0 - video_player_web: ^2.0.0 + video_player_web: ^2.1.0 dev_dependencies: flutter_test: diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index d3c63dae9d8..863fcf80513 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.5.2 + +* Updates Android Gradle plugin to 8.5.0. + ## 2.5.1 * Removes additional references to the v1 Android embedding. diff --git a/packages/video_player/video_player_android/android/build.gradle b/packages/video_player/video_player_android/android/build.gradle index 789d0949572..5675d385de3 100644 --- a/packages/video_player/video_player_android/android/build.gradle +++ b/packages/video_player/video_player_android/android/build.gradle @@ -9,7 +9,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.2.1' + classpath 'com.android.tools.build:gradle:8.5.0' } } diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index 2d234734d71..b57f24d100c 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.5.1 +version: 2.5.2 environment: sdk: ^3.4.0