[google_maps_flutter] Fix circle and polygon stroke width (#2044)

Stroke width of Polygon and Circles on Android was not independent of the screen density
diff --git a/packages/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/CHANGELOG.md
index 53bfa5f..a51d924 100644
--- a/packages/google_maps_flutter/CHANGELOG.md
+++ b/packages/google_maps_flutter/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.5.22+3
+
+* Fix polygon and circle stroke width according to device density
+
 ## 0.5.22+2
 
 * Update README: Add steps to enable Google Map SDK in the Google Developer Console.
diff --git a/packages/google_maps_flutter/android/build.gradle b/packages/google_maps_flutter/android/build.gradle
index 250939f..aa2771e 100644
--- a/packages/google_maps_flutter/android/build.gradle
+++ b/packages/google_maps_flutter/android/build.gradle
@@ -39,3 +39,8 @@
         androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
     }
 }
+
+dependencies {
+    testImplementation 'junit:junit:4.12'
+    testImplementation 'org.mockito:mockito-core:3.2.4'
+}
diff --git a/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleBuilder.java b/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleBuilder.java
index 6229d67..751887a 100644
--- a/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleBuilder.java
+++ b/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleBuilder.java
@@ -9,10 +9,12 @@
 
 class CircleBuilder implements CircleOptionsSink {
   private final CircleOptions circleOptions;
+  private final float density;
   private boolean consumeTapEvents;
 
-  CircleBuilder() {
+  CircleBuilder(float density) {
     this.circleOptions = new CircleOptions();
+    this.density = density;
   }
 
   CircleOptions build() {
@@ -56,7 +58,7 @@
 
   @Override
   public void setStrokeWidth(float strokeWidth) {
-    circleOptions.strokeWidth(strokeWidth);
+    circleOptions.strokeWidth(strokeWidth * density);
   }
 
   @Override
diff --git a/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleController.java b/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleController.java
index a649a15..709045a 100644
--- a/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleController.java
+++ b/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleController.java
@@ -11,11 +11,13 @@
 class CircleController implements CircleOptionsSink {
   private final Circle circle;
   private final String googleMapsCircleId;
+  private final float density;
   private boolean consumeTapEvents;
 
-  CircleController(Circle circle, boolean consumeTapEvents) {
+  CircleController(Circle circle, boolean consumeTapEvents, float density) {
     this.circle = circle;
     this.consumeTapEvents = consumeTapEvents;
+    this.density = density;
     this.googleMapsCircleId = circle.getId();
   }
 
@@ -56,7 +58,7 @@
 
   @Override
   public void setStrokeWidth(float strokeWidth) {
-    circle.setStrokeWidth(strokeWidth);
+    circle.setStrokeWidth(strokeWidth * density);
   }
 
   @Override
diff --git a/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CirclesController.java b/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CirclesController.java
index e80a123..b7b562e 100644
--- a/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CirclesController.java
+++ b/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CirclesController.java
@@ -17,12 +17,14 @@
   private final Map<String, CircleController> circleIdToController;
   private final Map<String, String> googleMapsCircleIdToDartCircleId;
   private final MethodChannel methodChannel;
+  private final float density;
   private GoogleMap googleMap;
 
-  CirclesController(MethodChannel methodChannel) {
+  CirclesController(MethodChannel methodChannel, float density) {
     this.circleIdToController = new HashMap<>();
     this.googleMapsCircleIdToDartCircleId = new HashMap<>();
     this.methodChannel = methodChannel;
+    this.density = density;
   }
 
   void setGoogleMap(GoogleMap googleMap) {
@@ -79,7 +81,7 @@
     if (circle == null) {
       return;
     }
-    CircleBuilder circleBuilder = new CircleBuilder();
+    CircleBuilder circleBuilder = new CircleBuilder(density);
     String circleId = Convert.interpretCircleOptions(circle, circleBuilder);
     CircleOptions options = circleBuilder.build();
     addCircle(circleId, options, circleBuilder.consumeTapEvents());
@@ -87,7 +89,7 @@
 
   private void addCircle(String circleId, CircleOptions circleOptions, boolean consumeTapEvents) {
     final Circle circle = googleMap.addCircle(circleOptions);
-    CircleController controller = new CircleController(circle, consumeTapEvents);
+    CircleController controller = new CircleController(circle, consumeTapEvents, density);
     circleIdToController.put(circleId, controller);
     googleMapsCircleIdToDartCircleId.put(circle.getId(), circleId);
   }
diff --git a/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java b/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java
index 154515f..fe3960f 100644
--- a/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java
+++ b/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java
@@ -125,9 +125,9 @@
     this.registrar = registrar;
     this.activityHashCode = registrarActivityHashCode;
     this.markersController = new MarkersController(methodChannel);
-    this.polygonsController = new PolygonsController(methodChannel);
+    this.polygonsController = new PolygonsController(methodChannel, density);
     this.polylinesController = new PolylinesController(methodChannel, density);
-    this.circlesController = new CirclesController(methodChannel);
+    this.circlesController = new CirclesController(methodChannel, density);
   }
 
   @Override
diff --git a/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonBuilder.java b/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonBuilder.java
index f2a717f..600762a 100644
--- a/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonBuilder.java
+++ b/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonBuilder.java
@@ -10,10 +10,12 @@
 
 class PolygonBuilder implements PolygonOptionsSink {
   private final PolygonOptions polygonOptions;
+  private final float density;
   private boolean consumeTapEvents;
 
-  PolygonBuilder() {
+  PolygonBuilder(float density) {
     this.polygonOptions = new PolygonOptions();
+    this.density = density;
   }
 
   PolygonOptions build() {
@@ -57,7 +59,7 @@
 
   @Override
   public void setStrokeWidth(float width) {
-    polygonOptions.strokeWidth(width);
+    polygonOptions.strokeWidth(width * density);
   }
 
   @Override
diff --git a/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonController.java b/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonController.java
index 12989d1..adb01b8 100644
--- a/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonController.java
+++ b/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonController.java
@@ -12,10 +12,12 @@
 class PolygonController implements PolygonOptionsSink {
   private final Polygon polygon;
   private final String googleMapsPolygonId;
+  private final float density;
   private boolean consumeTapEvents;
 
-  PolygonController(Polygon polygon, boolean consumeTapEvents) {
+  PolygonController(Polygon polygon, boolean consumeTapEvents, float density) {
     this.polygon = polygon;
+    this.density = density;
     this.consumeTapEvents = consumeTapEvents;
     this.googleMapsPolygonId = polygon.getId();
   }
@@ -57,7 +59,7 @@
 
   @Override
   public void setStrokeWidth(float width) {
-    polygon.setStrokeWidth(width);
+    polygon.setStrokeWidth(width * density);
   }
 
   @Override
diff --git a/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonsController.java b/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonsController.java
index 992e866..07f2ad0 100644
--- a/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonsController.java
+++ b/packages/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonsController.java
@@ -17,12 +17,14 @@
   private final Map<String, PolygonController> polygonIdToController;
   private final Map<String, String> googleMapsPolygonIdToDartPolygonId;
   private final MethodChannel methodChannel;
+  private final float density;
   private GoogleMap googleMap;
 
-  PolygonsController(MethodChannel methodChannel) {
+  PolygonsController(MethodChannel methodChannel, float density) {
     this.polygonIdToController = new HashMap<>();
     this.googleMapsPolygonIdToDartPolygonId = new HashMap<>();
     this.methodChannel = methodChannel;
+    this.density = density;
   }
 
   void setGoogleMap(GoogleMap googleMap) {
@@ -79,7 +81,7 @@
     if (polygon == null) {
       return;
     }
-    PolygonBuilder polygonBuilder = new PolygonBuilder();
+    PolygonBuilder polygonBuilder = new PolygonBuilder(density);
     String polygonId = Convert.interpretPolygonOptions(polygon, polygonBuilder);
     PolygonOptions options = polygonBuilder.build();
     addPolygon(polygonId, options, polygonBuilder.consumeTapEvents());
@@ -88,7 +90,7 @@
   private void addPolygon(
       String polygonId, PolygonOptions polygonOptions, boolean consumeTapEvents) {
     final Polygon polygon = googleMap.addPolygon(polygonOptions);
-    PolygonController controller = new PolygonController(polygon, consumeTapEvents);
+    PolygonController controller = new PolygonController(polygon, consumeTapEvents, density);
     polygonIdToController.put(polygonId, controller);
     googleMapsPolygonIdToDartPolygonId.put(polygon.getId(), polygonId);
   }
diff --git a/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleBuilderTest.java b/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleBuilderTest.java
new file mode 100644
index 0000000..6585090
--- /dev/null
+++ b/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleBuilderTest.java
@@ -0,0 +1,22 @@
+package io.flutter.plugins.googlemaps;
+
+import static junit.framework.TestCase.assertEquals;
+
+import com.google.android.gms.maps.model.CircleOptions;
+import org.junit.Test;
+
+public class CircleBuilderTest {
+
+  @Test
+  public void density_AppliesToStrokeWidth() {
+    final float density = 5;
+    final float strokeWidth = 3;
+    final CircleBuilder builder = new CircleBuilder(density);
+    builder.setStrokeWidth(strokeWidth);
+
+    final CircleOptions options = builder.build();
+    final float width = options.getStrokeWidth();
+
+    assertEquals(density * strokeWidth, width);
+  }
+}
diff --git a/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java b/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java
new file mode 100644
index 0000000..e032dd4
--- /dev/null
+++ b/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java
@@ -0,0 +1,25 @@
+package io.flutter.plugins.googlemaps;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import com.google.android.gms.internal.maps.zzh;
+import com.google.android.gms.maps.model.Circle;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class CircleControllerTest {
+
+  @Test
+  public void controller_SetsStrokeDensity() {
+    final zzh z = mock(zzh.class);
+    final Circle circle = spy(new Circle(z));
+
+    final float density = 5;
+    final float strokeWidth = 3;
+    final CircleController controller = new CircleController(circle, false, density);
+    controller.setStrokeWidth(strokeWidth);
+
+    Mockito.verify(circle).setStrokeWidth(density * strokeWidth);
+  }
+}
diff --git a/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonBuilderTest.java b/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonBuilderTest.java
new file mode 100644
index 0000000..644e898
--- /dev/null
+++ b/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonBuilderTest.java
@@ -0,0 +1,23 @@
+package io.flutter.plugins.googlemaps;
+
+import static junit.framework.TestCase.assertEquals;
+
+import com.google.android.gms.maps.model.PolygonOptions;
+import org.junit.Test;
+
+public class PolygonBuilderTest {
+
+  @Test
+  public void density_AppliesToStrokeWidth() {
+    final float density = 5;
+    final float strokeWidth = 3;
+
+    final PolygonBuilder builder = new PolygonBuilder(density);
+    builder.setStrokeWidth(strokeWidth);
+
+    final PolygonOptions options = builder.build();
+    final float width = options.getStrokeWidth();
+
+    assertEquals(density * strokeWidth, width);
+  }
+}
diff --git a/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java b/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java
new file mode 100644
index 0000000..834c427
--- /dev/null
+++ b/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java
@@ -0,0 +1,25 @@
+package io.flutter.plugins.googlemaps;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import com.google.android.gms.internal.maps.zzw;
+import com.google.android.gms.maps.model.Polygon;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class PolygonControllerTest {
+
+  @Test
+  public void controller_SetsStrokeDensity() {
+    final zzw z = mock(zzw.class);
+    final Polygon polygon = spy(new Polygon(z));
+
+    final float density = 5;
+    final float strokeWidth = 3;
+    final PolygonController controller = new PolygonController(polygon, false, density);
+    controller.setStrokeWidth(strokeWidth);
+
+    Mockito.verify(polygon).setStrokeWidth(density * strokeWidth);
+  }
+}
diff --git a/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineBuilderTest.java b/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineBuilderTest.java
new file mode 100644
index 0000000..bf6d060
--- /dev/null
+++ b/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineBuilderTest.java
@@ -0,0 +1,23 @@
+package io.flutter.plugins.googlemaps;
+
+import static junit.framework.TestCase.assertEquals;
+
+import com.google.android.gms.maps.model.PolylineOptions;
+import org.junit.Test;
+
+public class PolylineBuilderTest {
+
+  @Test
+  public void density_AppliesToStrokeWidth() {
+    final float density = 5;
+    final float strokeWidth = 3;
+
+    final PolylineBuilder builder = new PolylineBuilder(density);
+    builder.setWidth(strokeWidth);
+
+    final PolylineOptions options = builder.build();
+    final float width = options.getWidth();
+
+    assertEquals(density * strokeWidth, width);
+  }
+}
diff --git a/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java b/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java
new file mode 100644
index 0000000..acd2316
--- /dev/null
+++ b/packages/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java
@@ -0,0 +1,25 @@
+package io.flutter.plugins.googlemaps;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import com.google.android.gms.internal.maps.zzz;
+import com.google.android.gms.maps.model.Polyline;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class PolylineControllerTest {
+
+  @Test
+  public void controller_SetsStrokeDensity() {
+    final zzz z = mock(zzz.class);
+    final Polyline polyline = spy(new Polyline(z));
+
+    final float density = 5;
+    final float strokeWidth = 3;
+    final PolylineController controller = new PolylineController(polyline, false, density);
+    controller.setWidth(strokeWidth);
+
+    Mockito.verify(polyline).setWidth(density * strokeWidth);
+  }
+}
diff --git a/packages/google_maps_flutter/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/packages/google_maps_flutter/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..1f0955d
--- /dev/null
+++ b/packages/google_maps_flutter/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline
diff --git a/packages/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/pubspec.yaml
index d34b3cc..31d0f82 100644
--- a/packages/google_maps_flutter/pubspec.yaml
+++ b/packages/google_maps_flutter/pubspec.yaml
@@ -1,7 +1,7 @@
 name: google_maps_flutter
 description: A Flutter plugin for integrating Google Maps in iOS and Android applications.
 homepage: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter
-version: 0.5.22+2
+version: 0.5.22+3
 
 dependencies:
   flutter: