Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 2.4.0

* Adds the ability to request a specific map renderer.
* Updates code for new analysis options.

## 2.3.3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,30 @@ Hybrid Composition, but currently [misses certain map updates][4].
This mode will likely become the default in future versions if/when the
missed updates issue can be resolved.

## Map renderer

This plugin supports the option to request a specific [map renderer][5].

The renderer must be requested before creating GoogleMap instances, as the renderer can be initialized only once per application context.

<?code-excerpt "readme_excerpts.dart (MapRenderer)"?>
```dart
AndroidMapRenderer mapRenderer = AndroidMapRenderer.platformDefault;
// ···
final GoogleMapsFlutterPlatform mapsImplementation =
GoogleMapsFlutterPlatform.instance;
if (mapsImplementation is GoogleMapsFlutterAndroid) {
WidgetsFlutterBinding.ensureInitialized();
mapRenderer = await mapsImplementation
.initializeWithRenderer(AndroidMapRenderer.latest);
}
```

Available values are `AndroidMapRenderer.latest`, `AndroidMapRenderer.legacy`, `AndroidMapRenderer.platformDefault`.
Note that getting the requested renderer as a response is not guaranteed.

[1]: https://pub.dev/packages/google_maps_flutter
[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin
[3]: https://docs.flutter.dev/development/platform-integration/android/platform-views
[4]: https://github.com/flutter/flutter/issues/103686
[5]: https://developers.google.com/maps/documentation/android-sdk/renderer
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ public class GoogleMapFactory extends PlatformViewFactory {

private final BinaryMessenger binaryMessenger;
private final LifecycleProvider lifecycleProvider;
private final GoogleMapInitializer googleMapInitializer;

GoogleMapFactory(BinaryMessenger binaryMessenger, LifecycleProvider lifecycleProvider) {
GoogleMapFactory(
BinaryMessenger binaryMessenger, Context context, LifecycleProvider lifecycleProvider) {
super(StandardMessageCodec.INSTANCE);

this.binaryMessenger = binaryMessenger;
this.lifecycleProvider = lifecycleProvider;
this.googleMapInitializer = new GoogleMapInitializer(context, binaryMessenger);
}

@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// 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.

package io.flutter.plugins.googlemaps;

import android.content.Context;
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;
private final Context context;
private static MethodChannel.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);
}

@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.
*
* <p>Supported renderer types are "latest", "legacy" and "default".
*/
private void initializeWithPreferredRenderer(
String preferredRenderer, MethodChannel.Result result) {
if (rendererInitialized || initializationResult != null) {
result.error(
"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;
}
}
}

/**
* Initializes map renderer to with preferred renderer type.
*
* <p>This method is visible for testing purposes only and should never be used outside this
* class.
*/
@VisibleForTesting
public void initializeWithRendererRequest(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) {
rendererInitialized = true;
if (initializationResult != null) {
switch (renderer) {
case LATEST:
initializationResult.success("latest");
break;
case LEGACY:
initializationResult.success("legacy");
break;
default:
initializationResult.error(
"Unknown renderer type", "Initialized with unknown renderer type", null);
}
initializationResult = null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public static void registerWith(
VIEW_TYPE,
new GoogleMapFactory(
registrar.messenger(),
registrar.context(),
new LifecycleProvider() {
@Override
public Lifecycle getLifecycle() {
Expand All @@ -57,7 +58,10 @@ public Lifecycle getLifecycle() {
.platformViewRegistry()
.registerViewFactory(
VIEW_TYPE,
new GoogleMapFactory(registrar.messenger(), new ProxyLifecycleProvider(activity)));
new GoogleMapFactory(
registrar.messenger(),
registrar.context(),
new ProxyLifecycleProvider(activity)));
}
}

Expand All @@ -73,6 +77,7 @@ public void onAttachedToEngine(FlutterPluginBinding binding) {
VIEW_TYPE,
new GoogleMapFactory(
binding.getBinaryMessenger(),
binding.getApplicationContext(),
new LifecycleProvider() {
@Nullable
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// 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.

package io.flutter.plugins.googlemaps;

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;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import android.content.Context;
import android.os.Build;
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;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

@RunWith(RobolectricTestRunner.class)
@Config(sdk = Build.VERSION_CODES.P)
public class GoogleMapInitializerTest {
private GoogleMapInitializer googleMapInitializer;

@Mock BinaryMessenger mockMessenger;

@Before
public void before() {
MockitoAnnotations.openMocks(this);
Context context = ApplicationProvider.getApplicationContext();
googleMapInitializer = spy(new GoogleMapInitializer(context, mockMessenger));
}

@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<String, Object>() {
{
put("value", "latest");
}
}),
result);
googleMapInitializer.onMapsSdkInitialized(Renderer.LATEST);
verify(result, times(1)).success("latest");
verify(result, never()).error(any(), any(), 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<String, Object>() {
{
put("value", "legacy");
}
}),
result);
googleMapInitializer.onMapsSdkInitialized(Renderer.LEGACY);
verify(result, times(1)).success("legacy");
verify(result, never()).error(any(), any(), 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<String, Object>() {
{
put("value", "wrong_renderer");
}
}),
result);
verify(result, never()).success(any());
verify(result, times(1)).error(eq("Invalid renderer type"), any(), any());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:google_maps_flutter_android/google_maps_flutter_android.dart';
import 'package:google_maps_flutter_example/example_google_map.dart';
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
import 'package:integration_test/integration_test.dart';

const LatLng _kInitialMapCenter = LatLng(0, 0);
const double _kInitialZoomLevel = 5;
const CameraPosition _kInitialCameraPosition =
CameraPosition(target: _kInitialMapCenter, zoom: _kInitialZoomLevel);

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
void googleMapsTests() {
GoogleMapsFlutterPlatform.instance.enableDebugInspection();

// Repeatedly checks an asynchronous value against a test condition, waiting
Expand Down Expand Up @@ -511,7 +509,9 @@ void main() {
await waitForValueMatchingPredicate<LatLngBounds>(
tester,
() => mapController.getVisibleRegion(),
(LatLngBounds bounds) => bounds != zeroLatLngBounds) ??
(LatLngBounds bounds) =>
bounds != zeroLatLngBounds &&
bounds.northeast != bounds.southwest) ??
Copy link
Contributor Author

@jokerttu jokerttu Nov 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is done, as otherwise test fails with latest renderer, as bounds with zero area is given at start.

zeroLatLngBounds;
expect(firstVisibleRegion, isNot(zeroLatLngBounds));
expect(firstVisibleRegion.contains(_kInitialMapCenter), isTrue);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:google_maps_flutter_android/google_maps_flutter_android.dart';
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
import 'package:integration_test/integration_test.dart';

import 'google_maps_tests.dart' show googleMapsTests;

void main() {
late AndroidMapRenderer initializedRenderer;
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

setUpAll(() async {
final GoogleMapsFlutterAndroid instance =
GoogleMapsFlutterPlatform.instance as GoogleMapsFlutterAndroid;
initializedRenderer =
await instance.initializeWithRenderer(AndroidMapRenderer.latest);
});

testWidgets('initialized with latest renderer', (WidgetTester _) async {
expect(initializedRenderer, AndroidMapRenderer.latest);
});

testWidgets('throws PlatformException on multiple renderer initializations',
(WidgetTester _) async {
final GoogleMapsFlutterAndroid instance =
GoogleMapsFlutterPlatform.instance as GoogleMapsFlutterAndroid;
expect(
() async => instance.initializeWithRenderer(AndroidMapRenderer.latest),
throwsA(isA<PlatformException>().having((PlatformException e) => e.code,
'code', 'Renderer already initialized')));
});

// Run tests.
googleMapsTests();
}
Loading