package com.airbnb.android.airmapview; import android.graphics.Bitmap; import android.graphics.Point; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import com.airbnb.android.airmapview.listeners.InfoWindowCreator; import com.airbnb.android.airmapview.listeners.OnCameraChangeListener; import com.airbnb.android.airmapview.listeners.OnInfoWindowClickListener; import com.airbnb.android.airmapview.listeners.OnLatLngScreenLocationCallback; import com.airbnb.android.airmapview.listeners.OnMapBoundsCallback; import com.airbnb.android.airmapview.listeners.OnMapClickListener; import com.airbnb.android.airmapview.listeners.OnMapLoadedListener; import com.airbnb.android.airmapview.listeners.OnMapMarkerClickListener; import com.airbnb.android.airmapview.listeners.OnMapMarkerDragListener; import com.airbnb.android.airmapview.listeners.OnSnapshotReadyListener; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.Projection; import com.google.android.gms.maps.SupportMapFragment; import com.google.android.gms.maps.UiSettings; import com.google.android.gms.maps.model.CameraPosition; import com.google.android.gms.maps.model.CircleOptions; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.LatLngBounds; import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.Polygon; import com.google.maps.android.geojson.GeoJsonLayer; import com.google.maps.android.geojson.GeoJsonPolygonStyle; import org.json.JSONException; import org.json.JSONObject; import java.util.HashMap; import java.util.Map; public class NativeGoogleMapFragment extends SupportMapFragment implements AirMapInterface { private GoogleMap googleMap; private OnMapLoadedListener onMapLoadedListener; private boolean myLocationEnabled; private GeoJsonLayer layerOnMap; private final Map<Marker, AirMapMarker<?>> markers = new HashMap<>(); public static NativeGoogleMapFragment newInstance(AirGoogleMapOptions options) { return new NativeGoogleMapFragment().setArguments(options); } public NativeGoogleMapFragment setArguments(AirGoogleMapOptions options) { setArguments(options.toBundle()); return this; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = super.onCreateView(inflater, container, savedInstanceState); init(); return v; } public void init() { getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(GoogleMap googleMap) { if (googleMap != null && getActivity() != null) { NativeGoogleMapFragment.this.googleMap = googleMap; UiSettings settings = NativeGoogleMapFragment.this.googleMap.getUiSettings(); settings.setZoomControlsEnabled(false); settings.setMyLocationButtonEnabled(false); setMyLocationEnabled(myLocationEnabled); if (onMapLoadedListener != null) { onMapLoadedListener.onMapLoaded(); } } } }); } @Override public boolean isInitialized() { return googleMap != null && getActivity() != null; } @Override public void clearMarkers() { markers.clear(); googleMap.clear(); } @Override public void addMarker(AirMapMarker<?> airMarker) { Marker marker = googleMap.addMarker(airMarker.getMarkerOptions()); airMarker.setGoogleMarker(marker); markers.put(marker, airMarker); } @Override public void moveMarker(AirMapMarker<?> marker, LatLng to) { marker.setLatLng(to); marker.getMarker().setPosition(to); } @Override public void removeMarker(AirMapMarker<?> marker) { Marker nativeMarker = marker.getMarker(); if (nativeMarker != null) { nativeMarker.remove(); markers.remove(nativeMarker); } } @Override public void setOnInfoWindowClickListener(final OnInfoWindowClickListener listener) { googleMap.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() { @Override public void onInfoWindowClick(Marker marker) { AirMapMarker<?> airMarker = markers.get(marker); if (airMarker != null) { listener.onInfoWindowClick(airMarker); } } }); } @Override public void setInfoWindowCreator(GoogleMap.InfoWindowAdapter adapter, InfoWindowCreator creator) { googleMap.setInfoWindowAdapter(adapter); } @Override public void drawCircle(LatLng latLng, int radius) { drawCircle(latLng, radius, CIRCLE_BORDER_COLOR); } @Override public void drawCircle(LatLng latLng, int radius, int borderColor) { drawCircle(latLng, radius, borderColor, CIRCLE_BORDER_WIDTH); } @Override public void drawCircle(LatLng latLng, int radius, int borderColor, int borderWidth) { drawCircle(latLng, radius, borderColor, borderWidth, CIRCLE_FILL_COLOR); } @Override public void drawCircle(LatLng latLng, int radius, int borderColor, int borderWidth, int fillColor) { googleMap.addCircle(new CircleOptions() .center(latLng) .strokeColor(borderColor) .strokeWidth(borderWidth) .fillColor(fillColor) .radius(radius)); } @Override public void getMapScreenBounds(OnMapBoundsCallback callback) { final Projection projection = googleMap.getProjection(); int hOffset = getResources().getDimensionPixelOffset(R.dimen.map_horizontal_padding); int vOffset = getResources().getDimensionPixelOffset(R.dimen.map_vertical_padding); LatLngBounds.Builder builder = LatLngBounds.builder(); builder.include(projection.fromScreenLocation(new Point(hOffset, vOffset))); // top-left builder.include(projection.fromScreenLocation( new Point(getView().getWidth() - hOffset, vOffset))); // top-right builder.include(projection.fromScreenLocation( new Point(hOffset, getView().getHeight() - vOffset))); // bottom-left builder.include(projection.fromScreenLocation(new Point(getView().getWidth() - hOffset, getView().getHeight() - vOffset))); // bottom-right callback.onMapBoundsReady(builder.build()); } @Override public void getScreenLocation(LatLng latLng, OnLatLngScreenLocationCallback callback) { callback.onLatLngScreenLocationReady(googleMap.getProjection().toScreenLocation(latLng)); } @Override public void setCenter(LatLngBounds latLngBounds, int boundsPadding) { googleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(latLngBounds, boundsPadding)); } @Override public void setZoom(int zoom) { googleMap.animateCamera( CameraUpdateFactory.newLatLngZoom(googleMap.getCameraPosition().target, zoom)); } @Override public void animateCenter(LatLng latLng) { googleMap.animateCamera(CameraUpdateFactory.newLatLng(latLng)); } @Override public void setCenter(LatLng latLng) { googleMap.moveCamera(CameraUpdateFactory.newLatLng(latLng)); } @Override public LatLng getCenter() { return googleMap.getCameraPosition().target; } @Override public int getZoom() { return (int) googleMap.getCameraPosition().zoom; } @Override public void setOnCameraChangeListener(final OnCameraChangeListener onCameraChangeListener) { googleMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() { @Override public void onCameraChange(CameraPosition cameraPosition) { // camera change can occur programmatically. if (isResumed()) { onCameraChangeListener.onCameraChanged(cameraPosition.target, (int) cameraPosition.zoom); } } }); } @Override public void setOnMapLoadedListener(OnMapLoadedListener onMapLoadedListener) { this.onMapLoadedListener = onMapLoadedListener; } @Override public void setCenterZoom(LatLng latLng, int zoom) { googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, zoom)); } @Override public void animateCenterZoom(LatLng latLng, int zoom) { googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, zoom)); } @Override public void setOnMarkerClickListener(final OnMapMarkerClickListener listener) { googleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() { @Override public boolean onMarkerClick(Marker marker) { AirMapMarker<?> airMarker = markers.get(marker); if (airMarker != null) { return listener.onMapMarkerClick(airMarker); } return false; } }); } @Override public void setOnMarkerDragListener(final OnMapMarkerDragListener listener) { if (listener == null) { googleMap.setOnMarkerDragListener(null); return; } googleMap.setOnMarkerDragListener(new GoogleMap.OnMarkerDragListener() { @Override public void onMarkerDragStart(Marker marker) { listener.onMapMarkerDragStart(marker); } @Override public void onMarkerDrag(Marker marker) { listener.onMapMarkerDrag(marker); } @Override public void onMarkerDragEnd(Marker marker) { listener.onMapMarkerDragEnd(marker); } }); } @Override public void setOnMapClickListener(final OnMapClickListener listener) { googleMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() { @Override public void onMapClick(LatLng latLng) { listener.onMapClick(latLng); } }); } @Override public void setPadding(int left, int top, int right, int bottom) { googleMap.setPadding(left, top, right, bottom); } @Override public void setMyLocationEnabled(boolean enabled) { if (myLocationEnabled != enabled) { myLocationEnabled = enabled; if (!RuntimePermissionUtils.checkLocationPermissions(getActivity(), this)) { myLocationEnabled = false; } } } @Override public void onLocationPermissionsGranted() { //noinspection MissingPermission googleMap.setMyLocationEnabled(myLocationEnabled); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); RuntimePermissionUtils.onRequestPermissionsResult(this, requestCode, grantResults); } @Override public boolean isMyLocationEnabled() { return googleMap.isMyLocationEnabled(); } @Override public void setMyLocationButtonEnabled(boolean enabled) { googleMap.getUiSettings().setMyLocationButtonEnabled(enabled); } @Override public void setMapToolbarEnabled(boolean enabled) { googleMap.getUiSettings().setMapToolbarEnabled(enabled); } @Override public <T> void addPolyline(AirMapPolyline<T> polyline) { polyline.addToGoogleMap(googleMap); } @Override public <T> void removePolyline(AirMapPolyline<T> polyline) { polyline.removeFromGoogleMap(); } @Override public <T> void addPolygon(AirMapPolygon<T> polygon) { Polygon googlePolygon = googleMap.addPolygon(polygon.getPolygonOptions()); polygon.setGooglePolygon(googlePolygon); } @Override public <T> void removePolygon(AirMapPolygon<T> polygon) { Polygon nativePolygon = polygon.getGooglePolygon(); if (nativePolygon != null) { nativePolygon.remove(); } } @Override public void setMapType(MapType type) { int nativeType = 0; switch (type) { case MAP_TYPE_NORMAL: nativeType = GoogleMap.MAP_TYPE_NORMAL; break; case MAP_TYPE_SATELLITE: nativeType = GoogleMap.MAP_TYPE_SATELLITE; break; case MAP_TYPE_TERRAIN: nativeType = GoogleMap.MAP_TYPE_TERRAIN; break; } googleMap.setMapType(nativeType); } /** * This method will return the google map if initialized. Will return null otherwise * * @return returns google map if initialized */ public GoogleMap getGoogleMap() { return googleMap; } @Override public void setGeoJsonLayer(final AirMapGeoJsonLayer airMapGeoJsonLayer) throws JSONException { // clear any existing layers clearGeoJsonLayer(); layerOnMap = new GeoJsonLayer(googleMap, new JSONObject(airMapGeoJsonLayer.geoJson)); GeoJsonPolygonStyle style = layerOnMap.getDefaultPolygonStyle(); style.setStrokeColor(airMapGeoJsonLayer.strokeColor); style.setStrokeWidth(airMapGeoJsonLayer.strokeWidth); style.setFillColor(airMapGeoJsonLayer.fillColor); layerOnMap.addLayerToMap(); } @Override public void clearGeoJsonLayer() { if (layerOnMap == null) { return; } layerOnMap.removeLayerFromMap(); layerOnMap = null; } @Override public void getSnapshot(final OnSnapshotReadyListener listener) { getGoogleMap().snapshot(new GoogleMap.SnapshotReadyCallback() { @Override public void onSnapshotReady(Bitmap bitmap) { listener.onSnapshotReady(bitmap); } }); } @Override public void onDestroyView() { clearGeoJsonLayer(); super.onDestroyView(); } }