blob: 72c6959fe55e840c50a9a8aa940a18c874b5777e [file] [log] [blame]
// 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.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.ButtCap;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.Cap;
import com.google.android.gms.maps.model.CustomCap;
import com.google.android.gms.maps.model.Dash;
import com.google.android.gms.maps.model.Dot;
import com.google.android.gms.maps.model.Gap;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.PatternItem;
import com.google.android.gms.maps.model.RoundCap;
import com.google.android.gms.maps.model.SquareCap;
import com.google.android.gms.maps.model.Tile;
import io.flutter.view.FlutterMain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** Conversions between JSON-like values and GoogleMaps data types. */
class Convert {
// TODO(hamdikahloun): FlutterMain has been deprecated and should be replaced with FlutterLoader
// when it's available in Stable channel: https://github.com/flutter/flutter/issues/70923.
@SuppressWarnings("deprecation")
private static BitmapDescriptor toBitmapDescriptor(Object o) {
final List<?> data = toList(o);
switch (toString(data.get(0))) {
case "defaultMarker":
if (data.size() == 1) {
return BitmapDescriptorFactory.defaultMarker();
} else {
return BitmapDescriptorFactory.defaultMarker(toFloat(data.get(1)));
}
case "fromAsset":
if (data.size() == 2) {
return BitmapDescriptorFactory.fromAsset(
FlutterMain.getLookupKeyForAsset(toString(data.get(1))));
} else {
return BitmapDescriptorFactory.fromAsset(
FlutterMain.getLookupKeyForAsset(toString(data.get(1)), toString(data.get(2))));
}
case "fromAssetImage":
if (data.size() == 3) {
return BitmapDescriptorFactory.fromAsset(
FlutterMain.getLookupKeyForAsset(toString(data.get(1))));
} else {
throw new IllegalArgumentException(
"'fromAssetImage' Expected exactly 3 arguments, got: " + data.size());
}
case "fromBytes":
return getBitmapFromBytes(data);
default:
throw new IllegalArgumentException("Cannot interpret " + o + " as BitmapDescriptor");
}
}
private static BitmapDescriptor getBitmapFromBytes(List<?> data) {
if (data.size() == 2) {
try {
Bitmap bitmap = toBitmap(data.get(1));
return BitmapDescriptorFactory.fromBitmap(bitmap);
} catch (Exception e) {
throw new IllegalArgumentException("Unable to interpret bytes as a valid image.", e);
}
} else {
throw new IllegalArgumentException(
"fromBytes should have exactly one argument, interpretTileOverlayOptions the bytes. Got: "
+ data.size());
}
}
private static boolean toBoolean(Object o) {
return (Boolean) o;
}
static CameraPosition toCameraPosition(Object o) {
final Map<?, ?> data = toMap(o);
final CameraPosition.Builder builder = CameraPosition.builder();
builder.bearing(toFloat(data.get("bearing")));
builder.target(toLatLng(data.get("target")));
builder.tilt(toFloat(data.get("tilt")));
builder.zoom(toFloat(data.get("zoom")));
return builder.build();
}
static CameraUpdate toCameraUpdate(Object o, float density) {
final List<?> data = toList(o);
switch (toString(data.get(0))) {
case "newCameraPosition":
return CameraUpdateFactory.newCameraPosition(toCameraPosition(data.get(1)));
case "newLatLng":
return CameraUpdateFactory.newLatLng(toLatLng(data.get(1)));
case "newLatLngBounds":
return CameraUpdateFactory.newLatLngBounds(
toLatLngBounds(data.get(1)), toPixels(data.get(2), density));
case "newLatLngZoom":
return CameraUpdateFactory.newLatLngZoom(toLatLng(data.get(1)), toFloat(data.get(2)));
case "scrollBy":
return CameraUpdateFactory.scrollBy( //
toFractionalPixels(data.get(1), density), //
toFractionalPixels(data.get(2), density));
case "zoomBy":
if (data.size() == 2) {
return CameraUpdateFactory.zoomBy(toFloat(data.get(1)));
} else {
return CameraUpdateFactory.zoomBy(toFloat(data.get(1)), toPoint(data.get(2), density));
}
case "zoomIn":
return CameraUpdateFactory.zoomIn();
case "zoomOut":
return CameraUpdateFactory.zoomOut();
case "zoomTo":
return CameraUpdateFactory.zoomTo(toFloat(data.get(1)));
default:
throw new IllegalArgumentException("Cannot interpret " + o + " as CameraUpdate");
}
}
private static double toDouble(Object o) {
return ((Number) o).doubleValue();
}
private static float toFloat(Object o) {
return ((Number) o).floatValue();
}
private static Float toFloatWrapper(Object o) {
return (o == null) ? null : toFloat(o);
}
private static int toInt(Object o) {
return ((Number) o).intValue();
}
static Object cameraPositionToJson(CameraPosition position) {
if (position == null) {
return null;
}
final Map<String, Object> data = new HashMap<>();
data.put("bearing", position.bearing);
data.put("target", latLngToJson(position.target));
data.put("tilt", position.tilt);
data.put("zoom", position.zoom);
return data;
}
static Object latlngBoundsToJson(LatLngBounds latLngBounds) {
final Map<String, Object> arguments = new HashMap<>(2);
arguments.put("southwest", latLngToJson(latLngBounds.southwest));
arguments.put("northeast", latLngToJson(latLngBounds.northeast));
return arguments;
}
static Object markerIdToJson(String markerId) {
if (markerId == null) {
return null;
}
final Map<String, Object> data = new HashMap<>(1);
data.put("markerId", markerId);
return data;
}
static Object polygonIdToJson(String polygonId) {
if (polygonId == null) {
return null;
}
final Map<String, Object> data = new HashMap<>(1);
data.put("polygonId", polygonId);
return data;
}
static Object polylineIdToJson(String polylineId) {
if (polylineId == null) {
return null;
}
final Map<String, Object> data = new HashMap<>(1);
data.put("polylineId", polylineId);
return data;
}
static Object circleIdToJson(String circleId) {
if (circleId == null) {
return null;
}
final Map<String, Object> data = new HashMap<>(1);
data.put("circleId", circleId);
return data;
}
static Map<String, Object> tileOverlayArgumentsToJson(
String tileOverlayId, int x, int y, int zoom) {
if (tileOverlayId == null) {
return null;
}
final Map<String, Object> data = new HashMap<>(4);
data.put("tileOverlayId", tileOverlayId);
data.put("x", x);
data.put("y", y);
data.put("zoom", zoom);
return data;
}
static Object latLngToJson(LatLng latLng) {
return Arrays.asList(latLng.latitude, latLng.longitude);
}
static LatLng toLatLng(Object o) {
final List<?> data = toList(o);
return new LatLng(toDouble(data.get(0)), toDouble(data.get(1)));
}
static Point toPoint(Object o) {
Object x = toMap(o).get("x");
Object y = toMap(o).get("y");
return new Point((int) x, (int) y);
}
static Map<String, Integer> pointToJson(Point point) {
final Map<String, Integer> data = new HashMap<>(2);
data.put("x", point.x);
data.put("y", point.y);
return data;
}
private static LatLngBounds toLatLngBounds(Object o) {
if (o == null) {
return null;
}
final List<?> data = toList(o);
return new LatLngBounds(toLatLng(data.get(0)), toLatLng(data.get(1)));
}
private static List<?> toList(Object o) {
return (List<?>) o;
}
private static Map<?, ?> toMap(Object o) {
return (Map<?, ?>) o;
}
private static Map<String, Object> toObjectMap(Object o) {
Map<String, Object> hashMap = new HashMap<>();
Map<?, ?> map = (Map<?, ?>) o;
for (Object key : map.keySet()) {
Object object = map.get(key);
if (object != null) {
hashMap.put((String) key, object);
}
}
return hashMap;
}
private static float toFractionalPixels(Object o, float density) {
return toFloat(o) * density;
}
private static int toPixels(Object o, float density) {
return (int) toFractionalPixels(o, density);
}
private static Bitmap toBitmap(Object o) {
byte[] bmpData = (byte[]) o;
Bitmap bitmap = BitmapFactory.decodeByteArray(bmpData, 0, bmpData.length);
if (bitmap == null) {
throw new IllegalArgumentException("Unable to decode bytes as a valid bitmap.");
} else {
return bitmap;
}
}
private static Point toPoint(Object o, float density) {
final List<?> data = toList(o);
return new Point(toPixels(data.get(0), density), toPixels(data.get(1), density));
}
private static String toString(Object o) {
return (String) o;
}
static void interpretGoogleMapOptions(Object o, GoogleMapOptionsSink sink) {
final Map<?, ?> data = toMap(o);
final Object cameraTargetBounds = data.get("cameraTargetBounds");
if (cameraTargetBounds != null) {
final List<?> targetData = toList(cameraTargetBounds);
sink.setCameraTargetBounds(toLatLngBounds(targetData.get(0)));
}
final Object compassEnabled = data.get("compassEnabled");
if (compassEnabled != null) {
sink.setCompassEnabled(toBoolean(compassEnabled));
}
final Object mapToolbarEnabled = data.get("mapToolbarEnabled");
if (mapToolbarEnabled != null) {
sink.setMapToolbarEnabled(toBoolean(mapToolbarEnabled));
}
final Object mapType = data.get("mapType");
if (mapType != null) {
sink.setMapType(toInt(mapType));
}
final Object minMaxZoomPreference = data.get("minMaxZoomPreference");
if (minMaxZoomPreference != null) {
final List<?> zoomPreferenceData = toList(minMaxZoomPreference);
sink.setMinMaxZoomPreference( //
toFloatWrapper(zoomPreferenceData.get(0)), //
toFloatWrapper(zoomPreferenceData.get(1)));
}
final Object padding = data.get("padding");
if (padding != null) {
final List<?> paddingData = toList(padding);
sink.setPadding(
toFloat(paddingData.get(0)),
toFloat(paddingData.get(1)),
toFloat(paddingData.get(2)),
toFloat(paddingData.get(3)));
}
final Object rotateGesturesEnabled = data.get("rotateGesturesEnabled");
if (rotateGesturesEnabled != null) {
sink.setRotateGesturesEnabled(toBoolean(rotateGesturesEnabled));
}
final Object scrollGesturesEnabled = data.get("scrollGesturesEnabled");
if (scrollGesturesEnabled != null) {
sink.setScrollGesturesEnabled(toBoolean(scrollGesturesEnabled));
}
final Object tiltGesturesEnabled = data.get("tiltGesturesEnabled");
if (tiltGesturesEnabled != null) {
sink.setTiltGesturesEnabled(toBoolean(tiltGesturesEnabled));
}
final Object trackCameraPosition = data.get("trackCameraPosition");
if (trackCameraPosition != null) {
sink.setTrackCameraPosition(toBoolean(trackCameraPosition));
}
final Object zoomGesturesEnabled = data.get("zoomGesturesEnabled");
if (zoomGesturesEnabled != null) {
sink.setZoomGesturesEnabled(toBoolean(zoomGesturesEnabled));
}
final Object liteModeEnabled = data.get("liteModeEnabled");
if (liteModeEnabled != null) {
sink.setLiteModeEnabled(toBoolean(liteModeEnabled));
}
final Object myLocationEnabled = data.get("myLocationEnabled");
if (myLocationEnabled != null) {
sink.setMyLocationEnabled(toBoolean(myLocationEnabled));
}
final Object zoomControlsEnabled = data.get("zoomControlsEnabled");
if (zoomControlsEnabled != null) {
sink.setZoomControlsEnabled(toBoolean(zoomControlsEnabled));
}
final Object myLocationButtonEnabled = data.get("myLocationButtonEnabled");
if (myLocationButtonEnabled != null) {
sink.setMyLocationButtonEnabled(toBoolean(myLocationButtonEnabled));
}
final Object indoorEnabled = data.get("indoorEnabled");
if (indoorEnabled != null) {
sink.setIndoorEnabled(toBoolean(indoorEnabled));
}
final Object trafficEnabled = data.get("trafficEnabled");
if (trafficEnabled != null) {
sink.setTrafficEnabled(toBoolean(trafficEnabled));
}
final Object buildingsEnabled = data.get("buildingsEnabled");
if (buildingsEnabled != null) {
sink.setBuildingsEnabled(toBoolean(buildingsEnabled));
}
}
/** Returns the dartMarkerId of the interpreted marker. */
static String interpretMarkerOptions(Object o, MarkerOptionsSink sink) {
final Map<?, ?> data = toMap(o);
final Object alpha = data.get("alpha");
if (alpha != null) {
sink.setAlpha(toFloat(alpha));
}
final Object anchor = data.get("anchor");
if (anchor != null) {
final List<?> anchorData = toList(anchor);
sink.setAnchor(toFloat(anchorData.get(0)), toFloat(anchorData.get(1)));
}
final Object consumeTapEvents = data.get("consumeTapEvents");
if (consumeTapEvents != null) {
sink.setConsumeTapEvents(toBoolean(consumeTapEvents));
}
final Object draggable = data.get("draggable");
if (draggable != null) {
sink.setDraggable(toBoolean(draggable));
}
final Object flat = data.get("flat");
if (flat != null) {
sink.setFlat(toBoolean(flat));
}
final Object icon = data.get("icon");
if (icon != null) {
sink.setIcon(toBitmapDescriptor(icon));
}
final Object infoWindow = data.get("infoWindow");
if (infoWindow != null) {
interpretInfoWindowOptions(sink, toObjectMap(infoWindow));
}
final Object position = data.get("position");
if (position != null) {
sink.setPosition(toLatLng(position));
}
final Object rotation = data.get("rotation");
if (rotation != null) {
sink.setRotation(toFloat(rotation));
}
final Object visible = data.get("visible");
if (visible != null) {
sink.setVisible(toBoolean(visible));
}
final Object zIndex = data.get("zIndex");
if (zIndex != null) {
sink.setZIndex(toFloat(zIndex));
}
final String markerId = (String) data.get("markerId");
if (markerId == null) {
throw new IllegalArgumentException("markerId was null");
} else {
return markerId;
}
}
private static void interpretInfoWindowOptions(
MarkerOptionsSink sink, Map<String, Object> infoWindow) {
String title = (String) infoWindow.get("title");
String snippet = (String) infoWindow.get("snippet");
// snippet is nullable.
if (title != null) {
sink.setInfoWindowText(title, snippet);
}
Object infoWindowAnchor = infoWindow.get("anchor");
if (infoWindowAnchor != null) {
final List<?> anchorData = toList(infoWindowAnchor);
sink.setInfoWindowAnchor(toFloat(anchorData.get(0)), toFloat(anchorData.get(1)));
}
}
static String interpretPolygonOptions(Object o, PolygonOptionsSink sink) {
final Map<?, ?> data = toMap(o);
final Object consumeTapEvents = data.get("consumeTapEvents");
if (consumeTapEvents != null) {
sink.setConsumeTapEvents(toBoolean(consumeTapEvents));
}
final Object geodesic = data.get("geodesic");
if (geodesic != null) {
sink.setGeodesic(toBoolean(geodesic));
}
final Object visible = data.get("visible");
if (visible != null) {
sink.setVisible(toBoolean(visible));
}
final Object fillColor = data.get("fillColor");
if (fillColor != null) {
sink.setFillColor(toInt(fillColor));
}
final Object strokeColor = data.get("strokeColor");
if (strokeColor != null) {
sink.setStrokeColor(toInt(strokeColor));
}
final Object strokeWidth = data.get("strokeWidth");
if (strokeWidth != null) {
sink.setStrokeWidth(toInt(strokeWidth));
}
final Object zIndex = data.get("zIndex");
if (zIndex != null) {
sink.setZIndex(toFloat(zIndex));
}
final Object points = data.get("points");
if (points != null) {
sink.setPoints(toPoints(points));
}
final Object holes = data.get("holes");
if (holes != null) {
sink.setHoles(toHoles(holes));
}
final String polygonId = (String) data.get("polygonId");
if (polygonId == null) {
throw new IllegalArgumentException("polygonId was null");
} else {
return polygonId;
}
}
static String interpretPolylineOptions(Object o, PolylineOptionsSink sink) {
final Map<?, ?> data = toMap(o);
final Object consumeTapEvents = data.get("consumeTapEvents");
if (consumeTapEvents != null) {
sink.setConsumeTapEvents(toBoolean(consumeTapEvents));
}
final Object color = data.get("color");
if (color != null) {
sink.setColor(toInt(color));
}
final Object endCap = data.get("endCap");
if (endCap != null) {
sink.setEndCap(toCap(endCap));
}
final Object geodesic = data.get("geodesic");
if (geodesic != null) {
sink.setGeodesic(toBoolean(geodesic));
}
final Object jointType = data.get("jointType");
if (jointType != null) {
sink.setJointType(toInt(jointType));
}
final Object startCap = data.get("startCap");
if (startCap != null) {
sink.setStartCap(toCap(startCap));
}
final Object visible = data.get("visible");
if (visible != null) {
sink.setVisible(toBoolean(visible));
}
final Object width = data.get("width");
if (width != null) {
sink.setWidth(toInt(width));
}
final Object zIndex = data.get("zIndex");
if (zIndex != null) {
sink.setZIndex(toFloat(zIndex));
}
final Object points = data.get("points");
if (points != null) {
sink.setPoints(toPoints(points));
}
final Object pattern = data.get("pattern");
if (pattern != null) {
sink.setPattern(toPattern(pattern));
}
final String polylineId = (String) data.get("polylineId");
if (polylineId == null) {
throw new IllegalArgumentException("polylineId was null");
} else {
return polylineId;
}
}
static String interpretCircleOptions(Object o, CircleOptionsSink sink) {
final Map<?, ?> data = toMap(o);
final Object consumeTapEvents = data.get("consumeTapEvents");
if (consumeTapEvents != null) {
sink.setConsumeTapEvents(toBoolean(consumeTapEvents));
}
final Object fillColor = data.get("fillColor");
if (fillColor != null) {
sink.setFillColor(toInt(fillColor));
}
final Object strokeColor = data.get("strokeColor");
if (strokeColor != null) {
sink.setStrokeColor(toInt(strokeColor));
}
final Object visible = data.get("visible");
if (visible != null) {
sink.setVisible(toBoolean(visible));
}
final Object strokeWidth = data.get("strokeWidth");
if (strokeWidth != null) {
sink.setStrokeWidth(toInt(strokeWidth));
}
final Object zIndex = data.get("zIndex");
if (zIndex != null) {
sink.setZIndex(toFloat(zIndex));
}
final Object center = data.get("center");
if (center != null) {
sink.setCenter(toLatLng(center));
}
final Object radius = data.get("radius");
if (radius != null) {
sink.setRadius(toDouble(radius));
}
final String circleId = (String) data.get("circleId");
if (circleId == null) {
throw new IllegalArgumentException("circleId was null");
} else {
return circleId;
}
}
private static List<LatLng> toPoints(Object o) {
final List<?> data = toList(o);
final List<LatLng> points = new ArrayList<>(data.size());
for (Object rawPoint : data) {
final List<?> point = toList(rawPoint);
points.add(new LatLng(toFloat(point.get(0)), toFloat(point.get(1))));
}
return points;
}
private static List<List<LatLng>> toHoles(Object o) {
final List<?> data = toList(o);
final List<List<LatLng>> holes = new ArrayList<>(data.size());
for (Object rawHole : data) {
holes.add(toPoints(rawHole));
}
return holes;
}
private static List<PatternItem> toPattern(Object o) {
final List<?> data = toList(o);
if (data.isEmpty()) {
return null;
}
final List<PatternItem> pattern = new ArrayList<>(data.size());
for (Object ob : data) {
final List<?> patternItem = toList(ob);
switch (toString(patternItem.get(0))) {
case "dot":
pattern.add(new Dot());
break;
case "dash":
pattern.add(new Dash(toFloat(patternItem.get(1))));
break;
case "gap":
pattern.add(new Gap(toFloat(patternItem.get(1))));
break;
default:
throw new IllegalArgumentException("Cannot interpret " + pattern + " as PatternItem");
}
}
return pattern;
}
private static Cap toCap(Object o) {
final List<?> data = toList(o);
switch (toString(data.get(0))) {
case "buttCap":
return new ButtCap();
case "roundCap":
return new RoundCap();
case "squareCap":
return new SquareCap();
case "customCap":
if (data.size() == 2) {
return new CustomCap(toBitmapDescriptor(data.get(1)));
} else {
return new CustomCap(toBitmapDescriptor(data.get(1)), toFloat(data.get(2)));
}
default:
throw new IllegalArgumentException("Cannot interpret " + o + " as Cap");
}
}
static String interpretTileOverlayOptions(Map<String, ?> data, TileOverlaySink sink) {
final Object fadeIn = data.get("fadeIn");
if (fadeIn != null) {
sink.setFadeIn(toBoolean(fadeIn));
}
final Object transparency = data.get("transparency");
if (transparency != null) {
sink.setTransparency(toFloat(transparency));
}
final Object zIndex = data.get("zIndex");
if (zIndex != null) {
sink.setZIndex(toFloat(zIndex));
}
final Object visible = data.get("visible");
if (visible != null) {
sink.setVisible(toBoolean(visible));
}
final String tileOverlayId = (String) data.get("tileOverlayId");
if (tileOverlayId == null) {
throw new IllegalArgumentException("tileOverlayId was null");
} else {
return tileOverlayId;
}
}
static Tile interpretTile(Map<String, ?> data) {
int width = toInt(data.get("width"));
int height = toInt(data.get("height"));
byte[] dataArray = null;
if (data.get("data") != null) {
dataArray = (byte[]) data.get("data");
}
return new Tile(width, height, dataArray);
}
}