diff options
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src/main/java/com')
23 files changed, 1771 insertions, 1013 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java index 57e9373a42..c801720fa1 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java @@ -13,6 +13,7 @@ import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.Callback; import java.lang.ref.WeakReference; @@ -113,14 +114,15 @@ public class InfoWindow { * the view from the object position. * @return this {@link InfoWindow}. */ - InfoWindow open(MapView mapView, Marker boundMarker, LatLng position, int offsetX, int offsetY) { + InfoWindow open(final MapView mapView, final Marker boundMarker, final LatLng position, final int offsetX, + final int offsetY) { setBoundMarker(boundMarker); - MapView.LayoutParams lp = new MapView.LayoutParams(MapView.LayoutParams.WRAP_CONTENT, + final MapView.LayoutParams lp = new MapView.LayoutParams(MapView.LayoutParams.WRAP_CONTENT, MapView.LayoutParams.WRAP_CONTENT); MapboxMap mapboxMap = this.mapboxMap.get(); - View view = this.view.get(); + final View view = this.view.get(); if (view != null && mapboxMap != null) { view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); @@ -129,78 +131,83 @@ public class InfoWindow { markerWidthOffset = -offsetX; // Calculate default Android x,y coordinate - coordinates = mapboxMap.getProjection().toScreenLocation(position); - float x = coordinates.x - (view.getMeasuredWidth() / 2) + offsetX; - float y = coordinates.y - view.getMeasuredHeight() + offsetY; - - if (view instanceof InfoWindowView) { - // only apply repositioning/margin for InfoWindowView - Resources resources = mapView.getContext().getResources(); - - // get right/left popup window - float rightSideInfowWindow = x + view.getMeasuredWidth(); - float leftSideInfoWindow = x; - - // get right/left map view - final float mapRight = mapView.getRight(); - final float mapLeft = mapView.getLeft(); - - float marginHorizontal = resources.getDimension(R.dimen.mapbox_infowindow_margin); - float tipViewOffset = resources.getDimension(R.dimen.mapbox_infowindow_tipview_width) / 2; - float tipViewMarginLeft = view.getMeasuredWidth() / 2 - tipViewOffset; - - boolean outOfBoundsLeft = false; - boolean outOfBoundsRight = false; - - // only optimise margins if view is inside current viewport - if (coordinates.x >= 0 && coordinates.x <= mapView.getWidth() - && coordinates.y >= 0 && coordinates.y <= mapView.getHeight()) { - - // if out of bounds right - if (rightSideInfowWindow > mapRight) { - outOfBoundsRight = true; - x -= rightSideInfowWindow - mapRight; - tipViewMarginLeft += rightSideInfowWindow - mapRight + tipViewOffset; - rightSideInfowWindow = x + view.getMeasuredWidth(); + mapboxMap.getProjection().toScreenLocation(position, new Callback<PointF>() { + @Override + public void onResult(PointF pointF) { + coordinates = pointF; + float x = coordinates.x - (view.getMeasuredWidth() / 2) + offsetX; + float y = coordinates.y - view.getMeasuredHeight() + offsetY; + + if (view instanceof InfoWindowView) { + // only apply repositioning/margin for InfoWindowView + Resources resources = mapView.getContext().getResources(); + + // get right/left popup window + float rightSideInfowWindow = x + view.getMeasuredWidth(); + float leftSideInfoWindow = x; + + // get right/left map view + final float mapRight = mapView.getRight(); + final float mapLeft = mapView.getLeft(); + + float marginHorizontal = resources.getDimension(R.dimen.mapbox_infowindow_margin); + float tipViewOffset = resources.getDimension(R.dimen.mapbox_infowindow_tipview_width) / 2; + float tipViewMarginLeft = view.getMeasuredWidth() / 2 - tipViewOffset; + + boolean outOfBoundsLeft = false; + boolean outOfBoundsRight = false; + + // only optimise margins if view is inside current viewport + if (coordinates.x >= 0 && coordinates.x <= mapView.getWidth() + && coordinates.y >= 0 && coordinates.y <= mapView.getHeight()) { + + // if out of bounds right + if (rightSideInfowWindow > mapRight) { + outOfBoundsRight = true; + x -= rightSideInfowWindow - mapRight; + tipViewMarginLeft += rightSideInfowWindow - mapRight + tipViewOffset; + rightSideInfowWindow = x + view.getMeasuredWidth(); + } + + // fit screen left + if (leftSideInfoWindow < mapLeft) { + outOfBoundsLeft = true; + x += mapLeft - leftSideInfoWindow; + tipViewMarginLeft -= mapLeft - leftSideInfoWindow + tipViewOffset; + leftSideInfoWindow = x; + } + + // Add margin right + if (outOfBoundsRight && mapRight - rightSideInfowWindow < marginHorizontal) { + x -= marginHorizontal - (mapRight - rightSideInfowWindow); + tipViewMarginLeft += marginHorizontal - (mapRight - rightSideInfowWindow) - tipViewOffset; + leftSideInfoWindow = x; + } + + // Add margin left + if (outOfBoundsLeft && leftSideInfoWindow - mapLeft < marginHorizontal) { + x += marginHorizontal - (leftSideInfoWindow - mapLeft); + tipViewMarginLeft -= (marginHorizontal - (leftSideInfoWindow - mapLeft)) - tipViewOffset; + } + } + + // Adjust tipView + InfoWindowView infoWindowView = (InfoWindowView) view; + infoWindowView.setTipViewMarginLeft((int) tipViewMarginLeft); } - // fit screen left - if (leftSideInfoWindow < mapLeft) { - outOfBoundsLeft = true; - x += mapLeft - leftSideInfoWindow; - tipViewMarginLeft -= mapLeft - leftSideInfoWindow + tipViewOffset; - leftSideInfoWindow = x; - } + // set anchor popupwindowview + view.setX(x); + view.setY(y); - // Add margin right - if (outOfBoundsRight && mapRight - rightSideInfowWindow < marginHorizontal) { - x -= marginHorizontal - (mapRight - rightSideInfowWindow); - tipViewMarginLeft += marginHorizontal - (mapRight - rightSideInfowWindow) - tipViewOffset; - leftSideInfoWindow = x; - } + // Calculate x-offset for update method + viewWidthOffset = x - coordinates.x - offsetX; - // Add margin left - if (outOfBoundsLeft && leftSideInfoWindow - mapLeft < marginHorizontal) { - x += marginHorizontal - (leftSideInfoWindow - mapLeft); - tipViewMarginLeft -= (marginHorizontal - (leftSideInfoWindow - mapLeft)) - tipViewOffset; - } + close(); // if it was already opened + mapView.addView(view, lp); + isVisible = true; } - - // Adjust tipView - InfoWindowView infoWindowView = (InfoWindowView) view; - infoWindowView.setTipViewMarginLeft((int) tipViewMarginLeft); - } - - // set anchor popupwindowview - view.setX(x); - view.setY(y); - - // Calculate x-offset for update method - viewWidthOffset = x - coordinates.x - offsetX; - - close(); // if it was already opened - mapView.addView(view, lp); - isVisible = true; + }); } return this; } @@ -214,9 +221,14 @@ public class InfoWindow { MapboxMap mapboxMap = this.mapboxMap.get(); if (isVisible && mapboxMap != null) { isVisible = false; - View view = this.view.get(); + final View view = this.view.get(); if (view != null && view.getParent() != null) { - ((ViewGroup) view.getParent()).removeView(view); + view.post(new Runnable() { + @Override + public void run() { + ((ViewGroup) view.getParent()).removeView(view); + } + }); } Marker marker = getBoundMarker(); @@ -280,16 +292,20 @@ public class InfoWindow { public void update() { MapboxMap mapboxMap = this.mapboxMap.get(); Marker marker = boundMarker.get(); - View view = this.view.get(); + final View view = this.view.get(); if (mapboxMap != null && marker != null && view != null) { - coordinates = mapboxMap.getProjection().toScreenLocation(marker.getPosition()); - - if (view instanceof InfoWindowView) { - view.setX(coordinates.x + viewWidthOffset - markerWidthOffset); - } else { - view.setX(coordinates.x - (view.getMeasuredWidth() / 2) - markerWidthOffset); - } - view.setY(coordinates.y + markerHeightOffset); + mapboxMap.getProjection().toScreenLocation(marker.getPosition(), new Callback<PointF>() { + @Override + public void onResult(PointF pointF) { + coordinates = pointF; + if (view instanceof InfoWindowView) { + view.setX(coordinates.x + viewWidthOffset - markerWidthOffset); + } else { + view.setX(coordinates.x - (view.getMeasuredWidth() / 2) - markerWidthOffset); + } + view.setY(coordinates.y + markerHeightOffset); + } + }); } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java index e6d7843d9f..b47407f571 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java @@ -16,6 +16,7 @@ import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.Callback; import com.mapbox.mapboxsdk.utils.AnimatorUtils; import java.util.ArrayList; @@ -34,7 +35,7 @@ public class MarkerViewManager implements MapView.OnMapChangedListener { private final ViewGroup markerViewContainer; private final Map<MarkerView, View> markerViewMap = new HashMap<>(); - private final LongSparseArray<OnMarkerViewAddedListener> markerViewAddedListenerMap = new LongSparseArray<>(); + private final LongSparseArray<Callback<MarkerView>> markerViewAddedListenerMap = new LongSparseArray<>(); private final List<MapboxMap.MarkerViewAdapter> markerViewAdapters = new ArrayList<>(); // TODO refactor MapboxMap out for Projection and Transform @@ -177,30 +178,34 @@ public class MarkerViewManager implements MapView.OnMapChangedListener { for (final MarkerView marker : markerViewMap.keySet()) { final View convertView = markerViewMap.get(marker); if (convertView != null) { - PointF point = mapboxMap.getProjection().toScreenLocation(marker.getPosition()); - if (marker.getOffsetX() == MapboxConstants.UNMEASURED) { - // ensure view is measured first - if (marker.getWidth() == 0) { - convertView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); - if (convertView.getMeasuredWidth() != 0) { - marker.setWidth(convertView.getMeasuredWidth()); - marker.setHeight(convertView.getMeasuredHeight()); + mapboxMap.getProjection().toScreenLocation(marker.getPosition(), new Callback<PointF>() { + @Override + public void onResult(PointF point) { + if (marker.getOffsetX() == MapboxConstants.UNMEASURED) { + // ensure view is measured first + if (marker.getWidth() == 0) { + convertView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + if (convertView.getMeasuredWidth() != 0) { + marker.setWidth(convertView.getMeasuredWidth()); + marker.setHeight(convertView.getMeasuredHeight()); + } + } + } + if (marker.getWidth() != 0) { + int x = (int) (marker.getAnchorU() * marker.getWidth()); + int y = (int) (marker.getAnchorV() * marker.getHeight()); + marker.setOffset(x, y); } - } - } - if (marker.getWidth() != 0) { - int x = (int) (marker.getAnchorU() * marker.getWidth()); - int y = (int) (marker.getAnchorV() * marker.getHeight()); - marker.setOffset(x, y); - } - convertView.setX(point.x - marker.getOffsetX()); - convertView.setY(point.y - marker.getOffsetY()); + convertView.setX(point.x - marker.getOffsetX()); + convertView.setY(point.y - marker.getOffsetY()); - // animate visibility - if (marker.isVisible() && convertView.getVisibility() == View.GONE) { - animateVisible(marker, true); - } + // animate visibility + if (marker.isVisible() && convertView.getVisibility() == View.GONE) { + animateVisible(marker, true); + } + } + }); } } } @@ -239,7 +244,7 @@ public class MarkerViewManager implements MapView.OnMapChangedListener { * Animate a MarkerView to a deselected state. * <p> * The {@link com.mapbox.mapboxsdk.maps.MapboxMap.MarkerViewAdapter#onDeselect(MarkerView, View)} - * will be called to execute an animation. + * will be called to executeOnRenderThread an animation. * </p> * * @param marker the MarkerView to deselect. @@ -252,7 +257,7 @@ public class MarkerViewManager implements MapView.OnMapChangedListener { * Animate a MarkerView to a deselected state. * <p> * The {@link com.mapbox.mapboxsdk.maps.MapboxMap.MarkerViewAdapter#onDeselect(MarkerView, View)} - * will be called to execute an animation. + * will be called to executeOnRenderThread an animation. * </p> * * @param marker the MarkerView to deselect. @@ -301,7 +306,7 @@ public class MarkerViewManager implements MapView.OnMapChangedListener { * Animate a MarkerView to a selected state. * <p> * The {@link com.mapbox.mapboxsdk.maps.MapboxMap.MarkerViewAdapter#onSelect(MarkerView, View, boolean)} - * will be called to execute an animation. + * will be called to executeOnRenderThread an animation. * </p> * * @param marker the MarkerView object to select. @@ -317,7 +322,7 @@ public class MarkerViewManager implements MapView.OnMapChangedListener { * Animate a MarkerView to a selected state. * <p> * The {@link com.mapbox.mapboxsdk.maps.MapboxMap.MarkerViewAdapter#onSelect(MarkerView, View, boolean)} - * will be called to execute an animation. + * will be called to executeOnRenderThread an animation. * </p> * * @param marker the MarkerView object to select. @@ -460,75 +465,79 @@ public class MarkerViewManager implements MapView.OnMapChangedListener { */ public void invalidateViewMarkersInVisibleRegion() { RectF mapViewRect = new RectF(0, 0, markerViewContainer.getWidth(), markerViewContainer.getHeight()); - List<MarkerView> markers = mapboxMap.getMarkerViewsInRect(mapViewRect); - View convertView; - - // remove old markers - Iterator<MarkerView> iterator = markerViewMap.keySet().iterator(); - while (iterator.hasNext()) { - MarkerView marker = iterator.next(); - if (!markers.contains(marker)) { - // remove marker - convertView = markerViewMap.get(marker); - for (MapboxMap.MarkerViewAdapter adapter : markerViewAdapters) { - if (adapter.getMarkerClass().equals(marker.getClass())) { - adapter.prepareViewForReuse(marker, convertView); - adapter.releaseView(convertView); - marker.setMapboxMap(null); - iterator.remove(); + mapboxMap.getMarkerViewsInRect(mapViewRect, new Callback<List<MarkerView>>() { + @Override + public void onResult(List<MarkerView> markers) { + View convertView; + + // remove old markers + Iterator<MarkerView> iterator = markerViewMap.keySet().iterator(); + while (iterator.hasNext()) { + MarkerView marker = iterator.next(); + if (!markers.contains(marker)) { + // remove marker + convertView = markerViewMap.get(marker); + for (MapboxMap.MarkerViewAdapter adapter : markerViewAdapters) { + if (adapter.getMarkerClass().equals(marker.getClass())) { + adapter.prepareViewForReuse(marker, convertView); + adapter.releaseView(convertView); + marker.setMapboxMap(null); + iterator.remove(); + } + } } } - } - } - // introduce new markers - for (final MarkerView marker : markers) { - if (!markerViewMap.containsKey(marker)) { - for (final MapboxMap.MarkerViewAdapter adapter : markerViewAdapters) { - if (adapter.getMarkerClass().equals(marker.getClass())) { - - // Inflate View - convertView = (View) adapter.getViewReusePool().acquire(); - final View adaptedView = adapter.getView(marker, convertView, markerViewContainer); - if (adaptedView != null) { - adaptedView.setRotationX(marker.getTilt()); - adaptedView.setRotation(marker.getRotation()); - adaptedView.setAlpha(marker.getAlpha()); - adaptedView.setVisibility(View.GONE); - - if (mapboxMap.getSelectedMarkers().contains(marker)) { - // if a marker to be shown was selected - // replay that animation with duration 0 - if (adapter.onSelect(marker, adaptedView, true)) { - mapboxMap.selectMarker(marker); + // introduce new markers + for (final MarkerView marker : markers) { + if (!markerViewMap.containsKey(marker)) { + for (final MapboxMap.MarkerViewAdapter adapter : markerViewAdapters) { + if (adapter.getMarkerClass().equals(marker.getClass())) { + + // Inflate View + convertView = (View) adapter.getViewReusePool().acquire(); + final View adaptedView = adapter.getView(marker, convertView, markerViewContainer); + if (adaptedView != null) { + adaptedView.setRotationX(marker.getTilt()); + adaptedView.setRotation(marker.getRotation()); + adaptedView.setAlpha(marker.getAlpha()); + adaptedView.setVisibility(View.GONE); + + if (mapboxMap.getSelectedMarkers().contains(marker)) { + // if a marker to be shown was selected + // replay that animation with duration 0 + if (adapter.onSelect(marker, adaptedView, true)) { + mapboxMap.selectMarker(marker); + } + } + + marker.setMapboxMap(mapboxMap); + markerViewMap.put(marker, adaptedView); + if (convertView == null) { + adaptedView.setVisibility(View.GONE); + markerViewContainer.addView(adaptedView); + } } - } - marker.setMapboxMap(mapboxMap); - markerViewMap.put(marker, adaptedView); - if (convertView == null) { - adaptedView.setVisibility(View.GONE); - markerViewContainer.addView(adaptedView); + // notify listener is marker view is rendered + Callback<MarkerView> onViewAddedListener = markerViewAddedListenerMap.get(marker.getId()); + if (onViewAddedListener != null) { + onViewAddedListener.onResult(marker); + markerViewAddedListenerMap.remove(marker.getId()); + } } } - - // notify listener is marker view is rendered - OnMarkerViewAddedListener onViewAddedListener = markerViewAddedListenerMap.get(marker.getId()); - if (onViewAddedListener != null) { - onViewAddedListener.onViewAdded(marker); - markerViewAddedListenerMap.remove(marker.getId()); - } } } - } - } - // clear map, don't keep references to MarkerView listeners that are not found in the bounds of the map. - markerViewAddedListenerMap.clear(); + // clear map, don't keep references to MarkerView listeners that are not found in the bounds of the map. + markerViewAddedListenerMap.clear(); - // trigger update to make newly added ViewMarker visible, - // these would only be updated when the map is moved. - updateMarkerViewsPosition(); + // trigger update to make newly added ViewMarker visible, + // these would only be updated when the map is moved. + updateMarkerViewsPosition(); + } + }); } /** @@ -607,7 +616,7 @@ public class MarkerViewManager implements MapView.OnMapChangedListener { return markerViewContainer; } - public void addOnMarkerViewAddedListener(MarkerView markerView, OnMarkerViewAddedListener onMarkerViewAddedListener) { + public void addOnMarkerViewAddedListener(MarkerView markerView, Callback<MarkerView> onMarkerViewAddedListener) { markerViewAddedListenerMap.put(markerView.getId(), onMarkerViewAddedListener); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java index 74170bb72b..afe0e2272a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java @@ -249,6 +249,14 @@ public final class CameraPosition implements Parcelable { } } + public Builder(LatLng latLng, double zoom, double tilt, double bearing) { + super(); + this.target = latLng; + this.zoom = zoom; + this.tilt = tilt; + this.bearing = bearing; + } + /** * Sets the direction that the camera is pointing in, in degrees clockwise from north. * diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdate.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdate.java index 7e0dbf08fb..2c6aa3160e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdate.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdate.java @@ -1,6 +1,7 @@ package com.mapbox.mapboxsdk.camera; import android.support.annotation.NonNull; +import android.support.annotation.WorkerThread; import com.mapbox.mapboxsdk.maps.MapboxMap; @@ -9,6 +10,7 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; */ public interface CameraUpdate { + @WorkerThread CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java index fb0614c4a4..59fcb8e3e8 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java @@ -1,7 +1,6 @@ package com.mapbox.mapboxsdk.camera; import android.graphics.Point; -import android.graphics.PointF; import android.graphics.RectF; import android.support.annotation.IntDef; import android.support.annotation.NonNull; @@ -9,9 +8,6 @@ import android.support.annotation.NonNull; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.Projection; -import com.mapbox.mapboxsdk.maps.UiSettings; -import com.mapbox.services.android.telemetry.utils.MathUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -224,58 +220,60 @@ public final class CameraUpdateFactory { @Override public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) { - // Get required objects - Projection projection = mapboxMap.getProjection(); - UiSettings uiSettings = mapboxMap.getUiSettings(); - - // calculate correct padding - int[] mapPadding = mapboxMap.getPadding(); - RectF latLngPadding = getPadding(); - RectF padding = new RectF(latLngPadding.left + mapPadding[0], - latLngPadding.top + mapPadding[1], - latLngPadding.right + mapPadding[2], - latLngPadding.bottom + mapPadding[3]); - - // Calculate the bounds of the possibly rotated shape with respect to the viewport - PointF nePixel = new PointF(-Float.MAX_VALUE, -Float.MAX_VALUE); - PointF swPixel = new PointF(Float.MAX_VALUE, Float.MAX_VALUE); - float viewportHeight = uiSettings.getHeight(); - for (LatLng latLng : getBounds().toLatLngs()) { - PointF pixel = projection.toScreenLocation(latLng); - swPixel.x = Math.min(swPixel.x, pixel.x); - nePixel.x = Math.max(nePixel.x, pixel.x); - swPixel.y = Math.min(swPixel.y, viewportHeight - pixel.y); - nePixel.y = Math.max(nePixel.y, viewportHeight - pixel.y); - } - - // Calculate width/height - float width = nePixel.x - swPixel.x; - float height = nePixel.y - swPixel.y; - - double zoom = 0; - float minScale = 1; - // Calculate the zoom level - if (padding != null) { - float scaleX = (uiSettings.getWidth() - padding.left - padding.right) / width; - float scaleY = (uiSettings.getHeight() - padding.top - padding.bottom) / height; - minScale = scaleX < scaleY ? scaleX : scaleY; - zoom = calculateZoom(mapboxMap, minScale); - zoom = MathUtils.clamp(zoom, mapboxMap.getMinZoomLevel(), mapboxMap.getMaxZoomLevel()); - } - - // Calculate the center point - PointF paddedNEPixel = new PointF(nePixel.x + padding.right / minScale, nePixel.y + padding.top / minScale); - PointF paddedSWPixel = new PointF(swPixel.x - padding.left / minScale, swPixel.y - padding.bottom / minScale); - PointF centerPixel = new PointF((paddedNEPixel.x + paddedSWPixel.x) / 2, (paddedNEPixel.y + paddedSWPixel.y) / 2); - centerPixel.y = viewportHeight - centerPixel.y; - LatLng center = projection.fromScreenLocation(centerPixel); - - return new CameraPosition.Builder() - .target(center) - .zoom(zoom) - .tilt(0) - .bearing(0) - .build(); + //// Get required objects + //Projection projection = mapboxMap.getProjection(); + //UiSettings uiSettings = mapboxMap.getUiSettings(); + // + //// calculate correct padding + //int[] mapPadding = mapboxMap.getPadding(); + //RectF latLngPadding = getPadding(); + //RectF padding = new RectF(latLngPadding.left + mapPadding[0], + //latLngPadding.top + mapPadding[1], + //latLngPadding.right + mapPadding[2], + //latLngPadding.bottom + mapPadding[3]); + // + //// Calculate the bounds of the possibly rotated shape with respect to the viewport + //PointF nePixel = new PointF(-Float.MAX_VALUE, -Float.MAX_VALUE); + //PointF swPixel = new PointF(Float.MAX_VALUE, Float.MAX_VALUE); + //float viewportHeight = uiSettings.getHeight(); + //for (LatLng latLng : getBounds().toLatLngs()) { + // PointF pixel = projection.toScreenLocation(latLng); + // swPixel.x = Math.min(swPixel.x, pixel.x); + // nePixel.x = Math.max(nePixel.x, pixel.x); + // swPixel.y = Math.min(swPixel.y, viewportHeight - pixel.y); + // nePixel.y = Math.max(nePixel.y, viewportHeight - pixel.y); + //} + // + //// Calculate width/height + //float width = nePixel.x - swPixel.x; + //float height = nePixel.y - swPixel.y; + // + //double zoom = 0; + //float minScale = 1; + //// Calculate the zoom level + //if (padding != null) { + // float scaleX = (uiSettings.getWidth() - padding.left - padding.right) / width; + // float scaleY = (uiSettings.getHeight() - padding.top - padding.bottom) / height; + // minScale = scaleX < scaleY ? scaleX : scaleY; + // zoom = calculateZoom(mapboxMap, minScale); + // zoom = MathUtils.clamp(zoom, mapboxMap.getMinZoomLevel(), mapboxMap.getMaxZoomLevel()); + //} + // + //// Calculate the center point + //PointF paddedNEPixel = new PointF(nePixel.x + padding.right / minScale, nePixel.y + padding.top / minScale); + //PointF paddedSWPixel = new PointF(swPixel.x - padding.left / minScale, swPixel.y - padding.bottom / minScale); + //PointF centerPixel = new PointF((paddedNEPixel.x + paddedSWPixel.x) / 2, (paddedNEPixel.y + paddedSWPixel.y) + // / 2); + //centerPixel.y = viewportHeight - centerPixel.y; + //LatLng center = projection.fromScreenLocation(centerPixel); + // + //return new CameraPosition.Builder() + // .target(center) + // .zoom(zoom) + // .tilt(0) + // .bearing(0) + // .build(); + throw new RuntimeException(); } /** @@ -301,24 +299,25 @@ public final class CameraUpdateFactory { @Override public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) { - UiSettings uiSettings = mapboxMap.getUiSettings(); - Projection projection = mapboxMap.getProjection(); - - // Calculate the new center point - float viewPortWidth = uiSettings.getWidth(); - float viewPortHeight = uiSettings.getHeight(); - PointF targetPoint = new PointF(viewPortWidth / 2 + x, viewPortHeight / 2 + y); - - // Convert point to LatLng - LatLng latLng = projection.fromScreenLocation(targetPoint); - - CameraPosition previousPosition = mapboxMap.getCameraPosition(); - return new CameraPosition.Builder() - .target(latLng != null ? latLng : previousPosition.target) - .zoom(previousPosition.zoom) - .tilt(previousPosition.tilt) - .bearing(previousPosition.bearing) - .build(); + // UiSettings uiSettings = mapboxMap.getUiSettings(); + // Projection projection = mapboxMap.getProjection(); + // + // // Calculate the new center point + // float viewPortWidth = uiSettings.getWidth(); + // float viewPortHeight = uiSettings.getHeight(); + // PointF targetPoint = new PointF(viewPortWidth / 2 + x, viewPortHeight / 2 + y); + // + // // Convert point to LatLng + // LatLng latLng = projection.fromScreenLocation(targetPoint); + // + // CameraPosition previousPosition = mapboxMap.getCameraPosition(); + // return new CameraPosition.Builder() + // .target(latLng != null ? latLng : previousPosition.target) + // .zoom(previousPosition.zoom) + // .tilt(previousPosition.tilt) + // .bearing(previousPosition.bearing) + // .build(); + throw new RuntimeException(); } } @@ -401,17 +400,18 @@ public final class CameraUpdateFactory { @Override public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) { - CameraPosition cameraPosition = mapboxMap.getCameraPosition(); - if (getType() != CameraUpdateFactory.ZoomUpdate.ZOOM_TO_POINT) { - return new CameraPosition.Builder(cameraPosition) - .zoom(transformZoom(cameraPosition.zoom)) - .build(); - } else { - return new CameraPosition.Builder(cameraPosition) - .zoom(transformZoom(cameraPosition.zoom)) - .target(mapboxMap.getProjection().fromScreenLocation(new PointF(getX(), getY()))) - .build(); - } + throw new RuntimeException(); + // CameraPosition cameraPosition = mapboxMap.getCameraPosition(); + // if (getType() != CameraUpdateFactory.ZoomUpdate.ZOOM_TO_POINT) { + // return new CameraPosition.Builder(cameraPosition) + // .zoom(transformZoom(cameraPosition.zoom)) + // .build(); + // } else { + // return new CameraPosition.Builder(cameraPosition) + // .zoom(transformZoom(cameraPosition.zoom)) + // .target(mapboxMap.getProjection().fromScreenLocation(new PointF(getX(), getY()))) + // .build(); + // } } } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java index d53216b811..587dd20426 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java @@ -105,6 +105,12 @@ public class MapboxConstants { // Save instance state keys public static final String STATE_HAS_SAVED_STATE = "savedState"; public static final String STATE_CAMERA_POSITION = "cameraPosition"; + public static final String STATE_CAMERA_LAT = "cameraPositionLat"; + public static final String STATE_CAMERA_LNG = "cameraPositionLng"; + public static final String STATE_CAMERA_BEARING = "cameraPositionBearing"; + public static final String STATE_CAMERA_TILT = "cameraPositionTilt"; + public static final String STATE_CAMERA_ZOOM = "cameraPositionZoom"; + public static final String STATE_ZOOM_ENABLED = "zoomEnabled"; public static final String STATE_ZOOM_ENABLED_CHANGE = "zoomEnabledChange"; public static final String STATE_SCROLL_ENABLED = "scrollEnabled"; @@ -118,6 +124,7 @@ public class MapboxConstants { public static final String STATE_DOUBLE_TAP_ENABLED_CHANGE = "doubleTapEnabledChange"; public static final String STATE_DEBUG_ACTIVE = "debugActive"; public static final String STATE_STYLE_URL = "styleUrl"; + public static final String STATE_API_BASE_URL = "apiBaseUrl"; public static final String STATE_MY_LOCATION_ENABLED = "myLocationEnabled"; public static final String STATE_MY_LOCATION_TRACKING_MODE = "myLocationTracking"; public static final String STATE_MY_BEARING_TRACKING_MODE = "myBearingTracking"; @@ -150,4 +157,6 @@ public class MapboxConstants { public static final String MAPBOX_SHARED_PREFERENCE_KEY_TELEMETRY_STAGING_URL = "mapboxTelemetryStagingUrl"; public static final String MAPBOX_SHARED_PREFERENCE_KEY_TELEMETRY_STAGING_ACCESS_TOKEN = "mapboxTelemetryStagingAccessToken"; + public static final String STATE_TRANSITION_DELAY = "transitionDelay"; + public static final String STATE_TRANSITION_DURATION = "transitionDuration"; } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java index 0c77723354..81044a2afa 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java @@ -4,6 +4,8 @@ import android.graphics.PointF; import android.graphics.RectF; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.annotation.UiThread; +import android.support.annotation.WorkerThread; import android.support.v4.util.LongSparseArray; import com.mapbox.mapboxsdk.annotations.Annotation; @@ -17,6 +19,7 @@ import com.mapbox.mapboxsdk.annotations.Polygon; import com.mapbox.mapboxsdk.annotations.PolygonOptions; import com.mapbox.mapboxsdk.annotations.Polyline; import com.mapbox.mapboxsdk.annotations.PolylineOptions; +import com.mapbox.mapboxsdk.geometry.LatLng; import java.util.ArrayList; import java.util.Collections; @@ -33,27 +36,29 @@ import java.util.List; * com.mapbox.mapboxsdk.annotations. * </p> */ -class AnnotationManager { +class AnnotationManager extends MapThreadExecutor { - private final NativeMapView nativeMapView; private final MapView mapView; private final IconManager iconManager; private final InfoWindowManager infoWindowManager = new InfoWindowManager(); private final MarkerViewManager markerViewManager; + + // FIXME: 08/02/2017 threadsafe collection private final LongSparseArray<Annotation> annotations = new LongSparseArray<>(); private final List<Marker> selectedMarkers = new ArrayList<>(); private MapboxMap mapboxMap; private MapboxMap.OnMarkerClickListener onMarkerClickListener; - AnnotationManager(NativeMapView view, MapView mapView, MarkerViewManager markerViewManager) { - this.nativeMapView = view; + AnnotationManager(NativeMapView view, ThreadExecutor threadExecutor, MapView mapView, + MarkerViewManager markerViewManager) { + super(view, threadExecutor); this.mapView = mapView; - this.iconManager = new IconManager(nativeMapView); + this.iconManager = new IconManager(getNativeMapView()); this.markerViewManager = markerViewManager; if (view != null) { // null checking needed for unit tests - nativeMapView.addOnMapChangedListener(markerViewManager); + getNativeMapView().addOnMapChangedListener(markerViewManager); } } @@ -74,6 +79,7 @@ class AnnotationManager { // Annotations // + @UiThread Annotation getAnnotation(long id) { return annotations.get(id); } @@ -86,6 +92,7 @@ class AnnotationManager { return annotations; } + @UiThread void removeAnnotation(@NonNull Annotation annotation) { if (annotation instanceof Marker) { Marker marker = (Marker) annotation; @@ -94,23 +101,25 @@ class AnnotationManager { markerViewManager.removeMarkerView((MarkerView) marker); } } - long id = annotation.getId(); - if (nativeMapView != null) { - nativeMapView.removeAnnotation(id); - } + final long id = annotation.getId(); + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.removeAnnotation(id); + } + }); annotations.remove(id); } + @UiThread void removeAnnotation(long id) { - if (nativeMapView != null) { - nativeMapView.removeAnnotation(id); - } - annotations.remove(id); + removeAnnotation(annotations.get(id)); } + @UiThread void removeAnnotations(@NonNull List<? extends Annotation> annotationList) { int count = annotationList.size(); - long[] ids = new long[count]; + final long[] ids = new long[count]; for (int i = 0; i < count; i++) { Annotation annotation = annotationList.get(i); if (annotation instanceof Marker) { @@ -123,9 +132,12 @@ class AnnotationManager { ids[i] = annotationList.get(i).getId(); } - if (nativeMapView != null) { - nativeMapView.removeAnnotations(ids); - } + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.removeAnnotations(ids); + } + }); for (long id : ids) { annotations.remove(id); @@ -135,7 +147,7 @@ class AnnotationManager { void removeAnnotations() { Annotation annotation; int count = annotations.size(); - long[] ids = new long[count]; + final long[] ids = new long[count]; for (int i = 0; i < count; i++) { ids[i] = annotations.keyAt(i); annotation = annotations.get(ids[i]); @@ -148,9 +160,12 @@ class AnnotationManager { } } - if (nativeMapView != null) { - nativeMapView.removeAnnotations(ids); - } + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.removeAnnotations(ids); + } + }); annotations.clear(); } @@ -159,53 +174,78 @@ class AnnotationManager { // Markers // - Marker addMarker(@NonNull BaseMarkerOptions markerOptions, @NonNull MapboxMap mapboxMap) { - Marker marker = prepareMarker(markerOptions); - long id = nativeMapView != null ? nativeMapView.addMarker(marker) : 0; - marker.setMapboxMap(mapboxMap); - marker.setId(id); - annotations.put(id, marker); - return marker; - } - - List<Marker> addMarkers(@NonNull List<? extends BaseMarkerOptions> markerOptionsList, @NonNull MapboxMap mapboxMap) { - int count = markerOptionsList.size(); - List<Marker> markers = new ArrayList<>(count); - if (count > 0) { - BaseMarkerOptions markerOptions; - Marker marker; - for (int i = 0; i < count; i++) { - markerOptions = markerOptionsList.get(i); - marker = prepareMarker(markerOptions); - markers.add(marker); + void addMarker(@NonNull final BaseMarkerOptions markerOptions, @NonNull final MapboxMap mapboxMap, + final Callback<Marker> onMarkerResult) { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + final Marker marker = prepareMarker(markerOptions); + long id = nativeMapView != null ? nativeMapView.addMarker(marker) : 0; + marker.setMapboxMap(mapboxMap); + marker.setId(id); + annotations.put(id, marker); + + queueUiEvent(new Runnable() { + @Override + public void run() { + if (onMarkerResult != null) { + onMarkerResult.onResult(marker); + } + } + }); } + }); + } + + void addMarkers(@NonNull final List<? extends BaseMarkerOptions> markerOptionsList, + @NonNull final MapboxMap mapboxMap, final Callback<List<Marker>> onMarkersAddedResult) { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + int count = markerOptionsList.size(); + final List<Marker> markers = new ArrayList<>(count); + if (count > 0) { + BaseMarkerOptions markerOptions; + Marker marker; + for (int i = 0; i < count; i++) { + markerOptions = markerOptionsList.get(i); + marker = prepareMarker(markerOptions); + markers.add(marker); + } - if (markers.size() > 0) { - long[] ids = null; - if (nativeMapView != null) { - ids = nativeMapView.addMarkers(markers); - } + if (markers.size() > 0) { + long[] ids = null; + if (nativeMapView != null) { + ids = nativeMapView.addMarkers(markers); + } - long id = 0; - Marker m; - for (int i = 0; i < markers.size(); i++) { - m = markers.get(i); - m.setMapboxMap(mapboxMap); - if (ids != null) { - id = ids[i]; - } else { - // unit test - id++; + long id = 0; + Marker m; + for (int i = 0; i < markers.size(); i++) { + m = markers.get(i); + m.setMapboxMap(mapboxMap); + if (ids != null) { + id = ids[i]; + } else { + // unit test + id++; + } + m.setId(id); + annotations.put(id, m); + } } - m.setId(id); - annotations.put(id, m); } - + queueUiEvent(new Runnable() { + @Override + public void run() { + onMarkersAddedResult.onResult(markers); + } + }); } - } - return markers; + }); } + @WorkerThread private Marker prepareMarker(BaseMarkerOptions markerOptions) { Marker marker = markerOptions.getMarker(); Icon icon = iconManager.loadIconForMarker(marker); @@ -213,54 +253,75 @@ class AnnotationManager { return marker; } - MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions, @NonNull MapboxMap mapboxMap, - @Nullable MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener) { - final MarkerView marker = prepareViewMarker(markerOptions); - - // add marker to map - marker.setMapboxMap(mapboxMap); - long id = nativeMapView.addMarker(marker); - marker.setId(id); - annotations.put(id, marker); - - if (onMarkerViewAddedListener != null) { - markerViewManager.addOnMarkerViewAddedListener(marker, onMarkerViewAddedListener); - } - markerViewManager.setEnabled(true); - markerViewManager.setWaitingForRenderInvoke(true); - return marker; - } - + @UiThread + void addMarker(@NonNull final BaseMarkerViewOptions markerOptions, @NonNull final MapboxMap mapboxMap, + final Callback<MarkerView> callback) { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + final MarkerView marker = prepareViewMarker(markerOptions); + + // add marker to map + marker.setMapboxMap(mapboxMap); + long id = nativeMapView.addMarker(marker); + marker.setId(id); + annotations.put(id, marker); + + queueUiEvent(new Runnable() { + @Override + public void run() { + if (callback != null) { + markerViewManager.addOnMarkerViewAddedListener(marker, callback); + } + markerViewManager.setEnabled(true); + markerViewManager.setWaitingForRenderInvoke(true); + } + }); + } + }); + } + + + @UiThread + void addMarkerViews(@NonNull final List<? extends BaseMarkerViewOptions> markerViewOptions, + @NonNull final MapboxMap mapboxMap, final Callback<List<MarkerView>> callback) { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + List<MarkerView> markers = new ArrayList<>(); + for (BaseMarkerViewOptions markerViewOption : markerViewOptions) { + // if last marker + if (markerViewOptions.indexOf(markerViewOption) == markerViewOptions.size() - 1) { + // get notified when render occurs to invalidate and draw MarkerViews + markerViewManager.setWaitingForRenderInvoke(true); + } + // add marker to map + MarkerView marker = prepareViewMarker(markerViewOption); + marker.setMapboxMap(mapboxMap); + long id = nativeMapView.addMarker(marker); + marker.setId(id); + annotations.put(id, marker); + markers.add(marker); + } + markerViewManager.setEnabled(true); + markerViewManager.update(); - List<MarkerView> addMarkerViews(@NonNull List<? extends BaseMarkerViewOptions> markerViewOptions, - @NonNull MapboxMap mapboxMap) { - List<MarkerView> markers = new ArrayList<>(); - for (BaseMarkerViewOptions markerViewOption : markerViewOptions) { - // if last marker - if (markerViewOptions.indexOf(markerViewOption) == markerViewOptions.size() - 1) { - // get notified when render occurs to invalidate and draw MarkerViews - markerViewManager.setWaitingForRenderInvoke(true); + if (callback != null) { + callback.onResult(markers); + } } - // add marker to map - MarkerView marker = prepareViewMarker(markerViewOption); - marker.setMapboxMap(mapboxMap); - long id = nativeMapView.addMarker(marker); - marker.setId(id); - annotations.put(id, marker); - markers.add(marker); - } - markerViewManager.setEnabled(true); - markerViewManager.update(); - return markers; + }); } + @WorkerThread private MarkerView prepareViewMarker(BaseMarkerViewOptions markerViewOptions) { MarkerView marker = markerViewOptions.getMarker(); iconManager.loadIconForMarkerView(marker); return marker; } - void updateMarker(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap) { + @UiThread + void updateMarker(@NonNull final Marker updatedMarker, @NonNull final MapboxMap mapboxMap) { if (updatedMarker == null) { return; } @@ -269,16 +330,21 @@ class AnnotationManager { return; } - if (!(updatedMarker instanceof MarkerView)) { - iconManager.ensureIconLoaded(updatedMarker, mapboxMap); - } + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + if (!(updatedMarker instanceof MarkerView)) { + iconManager.ensureIconLoaded(updatedMarker, mapboxMap); + } - nativeMapView.updateMarker(updatedMarker); + nativeMapView.updateMarker(updatedMarker); - int index = annotations.indexOfKey(updatedMarker.getId()); - if (index > -1) { - annotations.setValueAt(index, updatedMarker); - } + int index = annotations.indexOfKey(updatedMarker.getId()); + if (index > -1) { + annotations.setValueAt(index, updatedMarker); + } + } + }); } List<Marker> getMarkers() { @@ -293,10 +359,12 @@ class AnnotationManager { return markers; } + @UiThread void setOnMarkerClickListener(@Nullable MapboxMap.OnMarkerClickListener listener) { onMarkerClickListener = listener; } + @UiThread void selectMarker(@NonNull Marker marker) { if (selectedMarkers.contains(marker)) { return; @@ -320,6 +388,7 @@ class AnnotationManager { selectedMarkers.add(marker); } + @UiThread void deselectMarkers() { if (selectedMarkers.isEmpty()) { return; @@ -339,6 +408,7 @@ class AnnotationManager { selectedMarkers.clear(); } + @UiThread void deselectMarker(@NonNull Marker marker) { if (!selectedMarkers.contains(marker)) { return; @@ -359,113 +429,161 @@ class AnnotationManager { return selectedMarkers; } - @NonNull - List<Marker> getMarkersInRect(@NonNull RectF rectangle) { - // convert Rectangle to be density depedent - float pixelRatio = nativeMapView.getPixelRatio(); - RectF rect = new RectF(rectangle.left / pixelRatio, - rectangle.top / pixelRatio, - rectangle.right / pixelRatio, - rectangle.bottom / pixelRatio); + void getMarkersInRect(@NonNull final RectF rectangle, final Callback<List<Marker>> callback) { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + // convert Rectangle to be density depedent + float pixelRatio = nativeMapView.getPixelRatio(); + RectF rect = new RectF(rectangle.left / pixelRatio, + rectangle.top / pixelRatio, + rectangle.right / pixelRatio, + rectangle.bottom / pixelRatio); - long[] ids = nativeMapView.queryPointAnnotations(rect); + long[] ids = nativeMapView.queryPointAnnotations(rect); - List<Long> idsList = new ArrayList<>(ids.length); - for (long id : ids) { - idsList.add(id); - } + List<Long> idsList = new ArrayList<>(ids.length); + for (long id : ids) { + idsList.add(id); + } - List<Marker> annotations = new ArrayList<>(ids.length); - List<Annotation> annotationList = getAnnotations(); - int count = annotationList.size(); - for (int i = 0; i < count; i++) { - Annotation annotation = annotationList.get(i); - if (annotation instanceof com.mapbox.mapboxsdk.annotations.Marker && idsList.contains(annotation.getId())) { - annotations.add((com.mapbox.mapboxsdk.annotations.Marker) annotation); - } - } + final List<Marker> annotations = new ArrayList<>(ids.length); + List<Annotation> annotationList = getAnnotations(); + int count = annotationList.size(); + for (int i = 0; i < count; i++) { + Annotation annotation = annotationList.get(i); + if (annotation instanceof Marker && idsList.contains(annotation.getId())) { + annotations.add((Marker) annotation); + } + } - return new ArrayList<>(annotations); + queueUiEvent(new Runnable() { + @Override + public void run() { + if (callback != null) { + callback.onResult(new ArrayList<>(annotations)); + } + } + }); + } + }); } - List<MarkerView> getMarkerViewsInRect(@NonNull RectF rectangle) { - float pixelRatio = nativeMapView.getPixelRatio(); - RectF rect = new RectF(rectangle.left / pixelRatio, - rectangle.top / pixelRatio, - rectangle.right / pixelRatio, - rectangle.bottom / pixelRatio); + void getMarkerViewsInRect(@NonNull final RectF rectangle, final Callback<List<MarkerView>> callback) { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + float pixelRatio = nativeMapView.getPixelRatio(); + RectF rect = new RectF(rectangle.left / pixelRatio, + rectangle.top / pixelRatio, + rectangle.right / pixelRatio, + rectangle.bottom / pixelRatio); - long[] ids = nativeMapView.queryPointAnnotations(rect); + long[] ids = nativeMapView.queryPointAnnotations(rect); - List<Long> idsList = new ArrayList<>(ids.length); - for (long id : ids) { - idsList.add(id); - } + List<Long> idsList = new ArrayList<>(ids.length); + for (long id : ids) { + idsList.add(id); + } - List<MarkerView> annotations = new ArrayList<>(ids.length); - List<Annotation> annotationList = getAnnotations(); - int count = annotationList.size(); - for (int i = 0; i < count; i++) { - Annotation annotation = annotationList.get(i); - if (annotation instanceof MarkerView && idsList.contains(annotation.getId())) { - annotations.add((MarkerView) annotation); - } - } + final List<MarkerView> annotations = new ArrayList<>(ids.length); + List<Annotation> annotationList = getAnnotations(); + int count = annotationList.size(); + for (int i = 0; i < count; i++) { + Annotation annotation = annotationList.get(i); + if (annotation instanceof MarkerView && idsList.contains(annotation.getId())) { + annotations.add((MarkerView) annotation); + } + } - return new ArrayList<>(annotations); + if (callback != null) { + queueUiEvent(new Runnable() { + @Override + public void run() { + callback.onResult(annotations); + } + }); + } + } + }); } // // Polygons // - Polygon addPolygon(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) { - Polygon polygon = polygonOptions.getPolygon(); - if (!polygon.getPoints().isEmpty()) { - long id = nativeMapView != null ? nativeMapView.addPolygon(polygon) : 0; - polygon.setId(id); - polygon.setMapboxMap(mapboxMap); - annotations.put(id, polygon); - } - return polygon; - } - - List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap) { - int count = polygonOptionsList.size(); - - Polygon polygon; - List<Polygon> polygons = new ArrayList<>(count); - if (count > 0) { - for (PolygonOptions polygonOptions : polygonOptionsList) { - polygon = polygonOptions.getPolygon(); + void addPolygon(@NonNull final PolygonOptions polygonOptions, @NonNull final MapboxMap mapboxMap, + final Callback<Polygon> listener) { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + final Polygon polygon = polygonOptions.getPolygon(); if (!polygon.getPoints().isEmpty()) { - polygons.add(polygon); + long id = nativeMapView.addPolygon(polygon); + polygon.setId(id); + polygon.setMapboxMap(mapboxMap); + annotations.put(id, polygon); + queueUiEvent(new Runnable() { + @Override + public void run() { + listener.onResult(polygon); + } + }); } } + }); + } + + void addPolygons(@NonNull final List<PolygonOptions> polygonOptionsList, @NonNull final MapboxMap mapboxMap, + final Callback<List<Polygon>> listCallback) { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + int count = polygonOptionsList.size(); + + Polygon polygon; + final List<Polygon> polygons = new ArrayList<>(count); + if (count > 0) { + for (PolygonOptions polygonOptions : polygonOptionsList) { + polygon = polygonOptions.getPolygon(); + if (!polygon.getPoints().isEmpty()) { + polygons.add(polygon); + } + } - long[] ids = null; - if (nativeMapView != null) { - ids = nativeMapView.addPolygons(polygons); - } + long[] ids = null; + if (nativeMapView != null) { + ids = nativeMapView.addPolygons(polygons); + } - long id = 0; - for (int i = 0; i < polygons.size(); i++) { - polygon = polygons.get(i); - polygon.setMapboxMap(mapboxMap); - if (ids != null) { - id = ids[i]; - } else { - // unit test - id++; + long id = 0; + for (int i = 0; i < polygons.size(); i++) { + polygon = polygons.get(i); + polygon.setMapboxMap(mapboxMap); + if (ids != null) { + id = ids[i]; + } else { + // unit test + id++; + } + polygon.setId(id); + annotations.put(id, polygon); + } } - polygon.setId(id); - annotations.put(id, polygon); + + queueUiEvent(new Runnable() { + @Override + public void run() { + if (listCallback != null) { + listCallback.onResult(polygons); + } + } + }); } - } - return polygons; + }); } - void updatePolygon(Polygon polygon) { + void updatePolygon(final Polygon polygon) { if (polygon == null) { return; } @@ -474,12 +592,16 @@ class AnnotationManager { return; } - nativeMapView.updatePolygon(polygon); - - int index = annotations.indexOfKey(polygon.getId()); - if (index > -1) { - annotations.setValueAt(index, polygon); - } + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.updatePolygon(polygon); + int index = annotations.indexOfKey(polygon.getId()); + if (index > -1) { + annotations.setValueAt(index, polygon); + } + } + }); } List<Polygon> getPolygons() { @@ -498,55 +620,75 @@ class AnnotationManager { // Polylines // - Polyline addPolyline(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) { - Polyline polyline = polylineOptions.getPolyline(); - if (!polyline.getPoints().isEmpty()) { - long id = nativeMapView != null ? nativeMapView.addPolyline(polyline) : 0; - polyline.setMapboxMap(mapboxMap); - polyline.setId(id); - annotations.put(id, polyline); - } - return polyline; - } - - List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap) { - int count = polylineOptionsList.size(); - Polyline polyline; - List<Polyline> polylines = new ArrayList<>(count); - - if (count > 0) { - for (PolylineOptions options : polylineOptionsList) { - polyline = options.getPolyline(); + void addPolyline(@NonNull final PolylineOptions polylineOptions, @NonNull final MapboxMap mapboxMap, + final Callback<Polyline> listener) { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + final Polyline polyline = polylineOptions.getPolyline(); if (!polyline.getPoints().isEmpty()) { - polylines.add(polyline); + long id = nativeMapView != null ? nativeMapView.addPolyline(polyline) : 0; + polyline.setMapboxMap(mapboxMap); + polyline.setId(id); + annotations.put(id, polyline); + queueUiEvent(new Runnable() { + @Override + public void run() { + if (listener != null) { + listener.onResult(polyline); + } + } + }); } } + }); + } + + void addPolylines(@NonNull final List<PolylineOptions> polylineOptionsList, @NonNull final MapboxMap mapboxMap, + final Callback<List<Polyline>> listener) { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + int count = polylineOptionsList.size(); + Polyline polyline; + final List<Polyline> polylines = new ArrayList<>(count); + + if (count > 0) { + for (PolylineOptions options : polylineOptionsList) { + polyline = options.getPolyline(); + if (!polyline.getPoints().isEmpty()) { + polylines.add(polyline); + } + } - long[] ids = null; - if (nativeMapView != null) { - ids = nativeMapView.addPolylines(polylines); - } + long[] ids = nativeMapView.addPolylines(polylines); + long id = 0; + Polyline p; - long id = 0; - Polyline p; - - for (int i = 0; i < polylines.size(); i++) { - p = polylines.get(i); - p.setMapboxMap(mapboxMap); - if (ids != null) { - id = ids[i]; - } else { - // unit test - id++; + for (int i = 0; i < polylines.size(); i++) { + p = polylines.get(i); + p.setMapboxMap(mapboxMap); + if (ids != null) { + id = ids[i]; + } else { + // unit test + id++; + } + p.setId(id); + annotations.put(id, p); + } } - p.setId(id); - annotations.put(id, p); + queueUiEvent(new Runnable() { + @Override + public void run() { + listener.onResult(polylines); + } + }); } - } - return polylines; + }); } - void updatePolyline(Polyline polyline) { + void updatePolyline(final Polyline polyline) { if (polyline == null) { return; } @@ -555,12 +697,17 @@ class AnnotationManager { return; } - nativeMapView.updatePolyline(polyline); + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.updatePolyline(polyline); - int index = annotations.indexOfKey(polyline.getId()); - if (index > -1) { - annotations.setValueAt(index, polyline); - } + int index = annotations.indexOfKey(polyline.getId()); + if (index > -1) { + annotations.setValueAt(index, polyline); + } + } + }); } List<Polyline> getPolylines() { @@ -604,101 +751,130 @@ class AnnotationManager { void reloadMarkers() { iconManager.reloadIcons(); - int count = annotations.size(); - for (int i = 0; i < count; i++) { - Annotation annotation = annotations.get(i); - if (annotation instanceof Marker) { - Marker marker = (Marker) annotation; - nativeMapView.removeAnnotation(annotation.getId()); - long newId = nativeMapView.addMarker(marker); - marker.setId(newId); + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + int count = annotations.size(); + for (int i = 0; i < count; i++) { + Annotation annotation = annotations.get(i); + if (annotation instanceof Marker) { + Marker marker = (Marker) annotation; + nativeMapView.removeAnnotation(annotation.getId()); + long newId = nativeMapView.addMarker(marker); + marker.setId(newId); + } + } } - } + }); } // // Click event // - boolean onTap(PointF tapPoint, float screenDensity) { + // FIXME: 09/02/2017 cleanup + void onTap(final PointF tapPoint, final UiSettings uiSettings, final Projection projection, + final MapboxMap.OnMapClickListener onMapClickListener) { + float screenDensity = uiSettings.getPixelRatio(); float toleranceSides = 4 * screenDensity; float toleranceTopBottom = 10 * screenDensity; - boolean handledDefaultClick = false; RectF tapRect = new RectF(tapPoint.x - iconManager.getAverageIconWidth() / 2 - toleranceSides, tapPoint.y - iconManager.getAverageIconHeight() / 2 - toleranceTopBottom, tapPoint.x + iconManager.getAverageIconWidth() / 2 + toleranceSides, tapPoint.y + iconManager.getAverageIconHeight() / 2 + toleranceTopBottom); - List<Marker> nearbyMarkers = getMarkersInRect(tapRect); - long newSelectedMarkerId = -1; - - // find a Marker that isn't selected yet - if (nearbyMarkers.size() > 0) { - Collections.sort(nearbyMarkers); - for (Marker nearbyMarker : nearbyMarkers) { - boolean found = false; - for (Marker selectedMarker : selectedMarkers) { - if (selectedMarker.equals(nearbyMarker)) { - found = true; + getMarkersInRect(tapRect, new Callback<List<Marker>>() { + @Override + public void onResult(List<Marker> nearbyMarkers) { + boolean tapHandled = false; + + long newSelectedMarkerId = -1; + boolean handledDefaultClick = false; + + // find a Marker that isn't selected yet + if (nearbyMarkers.size() > 0) { + Collections.sort(nearbyMarkers); + for (Marker nearbyMarker : nearbyMarkers) { + boolean found = false; + for (Marker selectedMarker : selectedMarkers) { + if (selectedMarker.equals(nearbyMarker)) { + found = true; + } + } + if (!found) { + newSelectedMarkerId = nearbyMarker.getId(); + break; + } } } - if (!found) { - newSelectedMarkerId = nearbyMarker.getId(); - break; - } - } - } - // if unselected marker found - if (newSelectedMarkerId >= 0) { - List<Annotation> annotations = getAnnotations(); - int count = annotations.size(); - for (int i = 0; i < count; i++) { - Annotation annotation = annotations.get(i); - if (annotation instanceof Marker) { - if (annotation.getId() == newSelectedMarkerId) { - Marker marker = (Marker) annotation; - - if (marker instanceof MarkerView) { - handledDefaultClick = markerViewManager.onClickMarkerView((MarkerView) marker); - } else { - if (onMarkerClickListener != null) { - // end developer has provided a custom click listener - handledDefaultClick = onMarkerClickListener.onMarkerClick(marker); + // if unselected marker found + if (newSelectedMarkerId >= 0) { + List<Annotation> annotations = getAnnotations(); + int count = annotations.size(); + for (int i = 0; i < count; i++) { + Annotation annotation = annotations.get(i); + if (annotation instanceof Marker) { + if (annotation.getId() == newSelectedMarkerId) { + Marker marker = (Marker) annotation; + + if (marker instanceof MarkerView) { + handledDefaultClick = markerViewManager.onClickMarkerView((MarkerView) marker); + } else { + if (onMarkerClickListener != null) { + // end developer has provided a custom click listener + handledDefaultClick = onMarkerClickListener.onMarkerClick(marker); + } + } + + if (annotation instanceof MarkerView) { + markerViewManager.onClickMarkerView((MarkerView) annotation); + } else { + if (!handledDefaultClick) { + // only select marker if user didn't handle the click event themselves + selectMarker(marker); + } + } + tapHandled = true; } } - - if (annotation instanceof MarkerView) { - markerViewManager.onClickMarkerView((MarkerView) annotation); - } else { - if (!handledDefaultClick) { - // only select marker if user didn't handle the click event themselves - selectMarker(marker); + } + } else if (nearbyMarkers.size() > 0) { + // we didn't find an unselected marker, check if we can close an already open markers + for (Marker nearbyMarker : nearbyMarkers) { + for (Marker selectedMarker : selectedMarkers) { + if (nearbyMarker.equals(selectedMarker)) { + if (onMarkerClickListener != null) { + // end developer has provided a custom click listener + handledDefaultClick = onMarkerClickListener.onMarkerClick(nearbyMarker); + if (!handledDefaultClick) { + deselectMarker(nearbyMarker); + } + } + tapHandled = true; } } - - return true; } } - } - } else if (nearbyMarkers.size() > 0) { - // we didn't find an unselected marker, check if we can close an already open markers - for (Marker nearbyMarker : nearbyMarkers) { - for (Marker selectedMarker : selectedMarkers) { - if (nearbyMarker.equals(selectedMarker)) { - if (onMarkerClickListener != null) { - // end developer has provided a custom click listener - handledDefaultClick = onMarkerClickListener.onMarkerClick(nearbyMarker); - if (!handledDefaultClick) { - deselectMarker(nearbyMarker); + + if (!tapHandled) { + if (uiSettings.isDeselectMarkersOnTap()) { + // deselect any selected marker + deselectMarkers(); + } + + // notify app of map click + if (onMapClickListener != null) { + projection.fromScreenLocation(tapPoint, new Callback<LatLng>() { + @Override + public void onResult(LatLng latLng) { + onMapClickListener.onMapClick(latLng); } - } - return true; + }); } } } - } - return false; + }); } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Callback.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Callback.java new file mode 100644 index 0000000000..2b0fd58d78 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Callback.java @@ -0,0 +1,18 @@ +package com.mapbox.mapboxsdk.maps; + +import com.mapbox.mapboxsdk.style.layers.Layer; +import com.mapbox.mapboxsdk.style.sources.Source; + +public interface Callback<T> { + + void onResult(T t); + + interface SourceCallback<U extends Source> { + void onResult(U u); + } + + interface LayerCallback<V extends Layer> { + void onResult(V v); + } + +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java index c9d81a88bc..ca3a4496aa 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java @@ -1,6 +1,7 @@ package com.mapbox.mapboxsdk.maps; import android.graphics.Bitmap; +import android.support.annotation.WorkerThread; import android.util.DisplayMetrics; import com.mapbox.mapboxsdk.annotations.Icon; @@ -92,10 +93,12 @@ class IconManager { return icon; } + @WorkerThread int getTopOffsetPixelsForIcon(Icon icon) { return (int) (nativeMapView.getTopOffsetPixelsForAnnotationSymbol(icon.getId()) * nativeMapView.getPixelRatio()); } + @WorkerThread void loadIcon(Icon icon) { Bitmap bitmap = icon.getBitmap(); String id = icon.getId(); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java index 0f4d3197cc..be337b2bb0 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java @@ -100,9 +100,13 @@ final class MapGestureDetector { * @param y coordinate * @return location */ - private Location getLocationFromGesture(float x, float y) { - LatLng latLng = projection.fromScreenLocation(new PointF(x, y)); - return TelemetryUtils.buildLocation(latLng.getLongitude(), latLng.getLatitude()); + private void getLocationFromGesture(float x, float y, final Callback<Location> locationCallback) { + projection.fromScreenLocation(new PointF(x, y), new Callback<LatLng>() { + @Override + public void onResult(LatLng latLng) { + locationCallback.onResult(TelemetryUtils.buildLocation(latLng.getLongitude(), latLng.getLatitude())); + } + }); } /** @@ -139,9 +143,13 @@ final class MapGestureDetector { && uiSettings.isZoomGesturesEnabled(); if (twoTap) { // Confirmed 2nd Finger Down - MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent( - getLocationFromGesture(event.getX(), event.getY()), - MapboxEvent.GESTURE_TWO_FINGER_SINGLETAP, transform.getZoom())); + getLocationFromGesture(event.getX(), event.getY(), new Callback<Location>() { + @Override + public void onResult(Location location) { + MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent( + location, MapboxEvent.GESTURE_TWO_FINGER_SINGLETAP, transform.getZoom())); + } + }); } break; @@ -170,8 +178,13 @@ final class MapGestureDetector { // Scroll / Pan Has Stopped if (scrollInProgress) { - MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapDragEndEvent( - getLocationFromGesture(event.getX(), event.getY()), transform.getZoom())); + getLocationFromGesture(event.getX(), event.getY(), new Callback<Location>() { + @Override + public void onResult(Location location) { + MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapDragEndEvent( + location, transform.getZoom())); + } + }); scrollInProgress = false; } @@ -242,12 +255,12 @@ final class MapGestureDetector { } @Override - public boolean onDoubleTapEvent(MotionEvent e) { + public boolean onDoubleTapEvent(MotionEvent event) { if (!uiSettings.isZoomGesturesEnabled() || !uiSettings.isDoubleTapGesturesEnabled()) { return false; } - switch (e.getAction()) { + switch (event.getAction()) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: @@ -265,14 +278,18 @@ final class MapGestureDetector { transform.zoom(true, focalPoint.x, focalPoint.y); } else { // Zoom in on gesture - transform.zoom(true, e.getX(), e.getY()); + transform.zoom(true, event.getX(), event.getY()); } break; } - MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent( - getLocationFromGesture(e.getX(), e.getY()), - MapboxEvent.GESTURE_DOUBLETAP, transform.getZoom())); + getLocationFromGesture(event.getX(), event.getY(), new Callback<Location>() { + @Override + public void onResult(Location location) { + MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent( + location, MapboxEvent.GESTURE_DOUBLETAP, transform.getZoom())); + } + }); return true; } @@ -287,32 +304,28 @@ final class MapGestureDetector { @Override public boolean onSingleTapConfirmed(MotionEvent motionEvent) { PointF tapPoint = new PointF(motionEvent.getX(), motionEvent.getY()); - boolean tapHandled = annotationManager.onTap(tapPoint, uiSettings.getPixelRatio()); - - if (!tapHandled) { - if (uiSettings.isDeselectMarkersOnTap()) { - // deselect any selected marker - annotationManager.deselectMarkers(); - } - - // notify app of map click - if (onMapClickListener != null) { - onMapClickListener.onMapClick(projection.fromScreenLocation(tapPoint)); + annotationManager.onTap(tapPoint, uiSettings, projection, onMapClickListener); + getLocationFromGesture(motionEvent.getX(), motionEvent.getY(), new Callback<Location>() { + @Override + public void onResult(Location location) { + MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent( + location, MapboxEvent.GESTURE_SINGLETAP, transform.getZoom())); } - } - - MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent( - getLocationFromGesture(motionEvent.getX(), motionEvent.getY()), - MapboxEvent.GESTURE_SINGLETAP, transform.getZoom())); - + }); return true; } @Override public void onLongPress(MotionEvent motionEvent) { if (onMapLongClickListener != null && !quickZoom) { - onMapLongClickListener.onMapLongClick( - projection.fromScreenLocation(new PointF(motionEvent.getX(), motionEvent.getY()))); + projection.fromScreenLocation(new PointF(motionEvent.getX(), motionEvent.getY()), new Callback<LatLng>() { + @Override + public void onResult(LatLng latLng) { + if (onMapLongClickListener != null) { + onMapLongClickListener.onMapLongClick(latLng); + } + } + }); } } @@ -363,9 +376,13 @@ final class MapGestureDetector { public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { if (!scrollInProgress) { scrollInProgress = true; - MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent( - getLocationFromGesture(e1.getX(), e1.getY()), - MapboxEvent.GESTURE_PAN_START, transform.getZoom())); + getLocationFromGesture(e1.getX(), e1.getY(), new Callback<Location>() { + @Override + public void onResult(Location location) { + MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent( + location, MapboxEvent.GESTURE_PAN_START, transform.getZoom())); + } + }); } if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { return false; @@ -407,9 +424,13 @@ final class MapGestureDetector { scaleGestureOccurred = true; beginTime = detector.getEventTime(); - MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent( - getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), - MapboxEvent.GESTURE_PINCH_START, transform.getZoom())); + getLocationFromGesture(detector.getFocusX(), detector.getFocusY(), new Callback<Location>() { + @Override + public void onResult(Location location) { + MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent( + location, MapboxEvent.GESTURE_PINCH_START, transform.getZoom())); + } + }); return true; } @@ -499,9 +520,13 @@ final class MapGestureDetector { } beginTime = detector.getEventTime(); - MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent( - getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), - MapboxEvent.GESTURE_ROTATION_START, transform.getZoom())); + getLocationFromGesture(detector.getFocusX(), detector.getFocusY(), new Callback<Location>() { + @Override + public void onResult(Location location) { + MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent( + location, MapboxEvent.GESTURE_ROTATION_START, transform.getZoom())); + } + }); return true; } @@ -549,7 +574,7 @@ final class MapGestureDetector { trackingSettings.resetTrackingModesIfRequired(true, true); // Get rotate value - double bearing = transform.getRawBearing(); + double bearing = transform.getBearing(); bearing += detector.getRotationDegreesDelta(); // Rotate the map @@ -580,9 +605,14 @@ final class MapGestureDetector { } beginTime = detector.getEventTime(); - MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent( - getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), - MapboxEvent.GESTURE_PITCH_START, transform.getZoom())); + getLocationFromGesture(detector.getFocusX(), detector.getFocusY(), new Callback<Location>() { + @Override + public void onResult(Location location) { + MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapClickEvent( + location, MapboxEvent.GESTURE_PITCH_START, transform.getZoom())); + } + }); + return true; } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapRunnable.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapRunnable.java new file mode 100644 index 0000000000..0268e2cf95 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapRunnable.java @@ -0,0 +1,9 @@ +package com.mapbox.mapboxsdk.maps; + +import android.support.annotation.NonNull; +import android.support.annotation.WorkerThread; + +interface MapRunnable { + @WorkerThread + void execute(@NonNull NativeMapView nativeMapView); +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapState.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapState.java new file mode 100644 index 0000000000..90764b709b --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapState.java @@ -0,0 +1,139 @@ +package com.mapbox.mapboxsdk.maps; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.UiThread; +import android.text.TextUtils; + +import com.mapbox.mapboxsdk.constants.MapboxConstants; + +class MapState extends MapThreadExecutor implements State { + + private String apiBaseUrl; + private String styleUrl; + private boolean debug; + private long transitionDuration; + private long transitionDelay; + + MapState(@NonNull NativeMapView nativeMapView, @NonNull ThreadExecutor invocation) { + super(nativeMapView, invocation); + } + + @UiThread + void setApiBaseUrl(@NonNull final String apiBaseUrl) { + this.apiBaseUrl = apiBaseUrl; + queueRenderEvent(new MapRunnable() { + @Override + public void execute(NativeMapView nativeMapView) { + nativeMapView.setApiBaseUrl(apiBaseUrl); + } + }); + } + + @UiThread + String getStyleUrl() { + return styleUrl; + } + + @UiThread + void setStyleUrl(final String styleUrl) { + this.styleUrl = styleUrl; + queueRenderEvent(new MapRunnable() { + @Override + public void execute(NativeMapView nativeMapView) { + nativeMapView.setStyleUrl(styleUrl); + } + }); + } + + @UiThread + boolean getDebug() { + return debug; + } + + @UiThread + void setDebug(final boolean debug) { + this.debug = debug; + queueRenderEvent(new MapRunnable() { + @Override + public void execute(NativeMapView nativeMapView) { + nativeMapView.setDebug(debug); + } + }); + } + + @UiThread + void cycleDebugOptions() { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.cycleDebugOptions(); + } + }); + } + + long getTransitionDuration() { + return transitionDuration; + } + + void setTransitionDuration(final long transitionDuration) { + this.transitionDuration = transitionDuration; + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.setTransitionDuration(transitionDuration); + } + }); + } + + long getTransitionDelay() { + return transitionDelay; + } + + void setTransitionDelay(final long transitionDelay) { + this.transitionDelay = transitionDelay; + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.setTransitionDuration(transitionDelay); + } + }); + } + + @UiThread + public void onSaveInstanceState(Bundle outState) { + outState.putBoolean(MapboxConstants.STATE_DEBUG_ACTIVE, debug); + outState.putString(MapboxConstants.STATE_STYLE_URL, styleUrl); + outState.putString(MapboxConstants.STATE_API_BASE_URL, apiBaseUrl); + outState.putLong(MapboxConstants.STATE_TRANSITION_DELAY, transitionDelay); + outState.putLong(MapboxConstants.STATE_TRANSITION_DURATION, transitionDuration); + } + + @UiThread + public void onRestoreInstanceState(Bundle savedInstanceState) { + boolean debug = savedInstanceState.getBoolean(MapboxConstants.STATE_DEBUG_ACTIVE); + if (debug) { + setDebug(debug); + } + + String styleUrl = savedInstanceState.getString(MapboxConstants.STATE_STYLE_URL); + if (!TextUtils.isEmpty(styleUrl)) { + setStyleUrl(styleUrl); + } + + String apiBaseUrl = savedInstanceState.getString(MapboxConstants.STATE_API_BASE_URL); + if (!TextUtils.isEmpty(apiBaseUrl)) { + setApiBaseUrl(apiBaseUrl); + } + + long transitionDuration = savedInstanceState.getLong(MapboxConstants.STATE_TRANSITION_DURATION); + if (transitionDuration != 0) { + setTransitionDuration(transitionDuration); + } + + long transitionDelay = savedInstanceState.getLong(MapboxConstants.STATE_TRANSITION_DELAY); + if (transitionDelay != 0) { + setTransitionDelay(transitionDelay); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapThreadExecutor.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapThreadExecutor.java new file mode 100644 index 0000000000..24fc29224f --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapThreadExecutor.java @@ -0,0 +1,32 @@ +package com.mapbox.mapboxsdk.maps; + +import android.support.annotation.MainThread; +import android.support.annotation.NonNull; +import android.support.annotation.WorkerThread; + +abstract class MapThreadExecutor implements ThreadExecutor { + + private final NativeMapView nativeMapView; + private final ThreadExecutor threadExecutor; + + MapThreadExecutor(@NonNull NativeMapView nativeMapView, @NonNull ThreadExecutor threadExecutor) { + this.nativeMapView = nativeMapView; + this.threadExecutor = threadExecutor; + } + + @MainThread + public void queueRenderEvent(final MapRunnable mapRunnable) { + threadExecutor.queueRenderEvent(mapRunnable); + } + + @WorkerThread + public void queueUiEvent(Runnable runnable) { + threadExecutor.queueUiEvent(runnable); + } + + @NonNull + NativeMapView getNativeMapView() { + return nativeMapView; + } + +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index c124531fa5..e6c08ef021 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -8,7 +8,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; -import android.graphics.Canvas; import android.graphics.PointF; import android.net.ConnectivityManager; import android.net.NetworkInfo; @@ -21,7 +20,6 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.UiThread; import android.support.v7.app.AlertDialog; -import android.text.TextUtils; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -74,6 +72,7 @@ import static android.opengl.GLSurfaceView.RENDERMODE_WHEN_DIRTY; public class MapView extends FrameLayout { private NativeMapView nativeMapView; + private MapState mapState; private boolean destroyed; private GLSurfaceView glSurfaceView; @@ -141,6 +140,7 @@ public class MapView extends FrameLayout { glSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceView); glSurfaceView.setEGLConfigChooser(8, 8, 8, 0 /** TODO: What alpha value do we need here?? */, 16, 8); glSurfaceView.setEGLContextClientVersion(2); + glSurfaceView.setRenderer(new GLSurfaceView.Renderer() { @Override public void onSurfaceCreated(final GL10 gl, EGLConfig eglConfig) { @@ -149,9 +149,8 @@ public class MapView extends FrameLayout { // Create native Map object nativeMapView = new NativeMapView(MapView.this); - if (TextUtils.isEmpty(nativeMapView.getAccessToken())) { - nativeMapView.setAccessToken(Mapbox.getAccessToken()); - } + nativeMapView.setAccessToken(Mapbox.getAccessToken()); + nativeMapView.setReachability(isConnected()); //Continue configuring the map view on the main thread MapView.this.post(new Runnable() { @@ -191,6 +190,25 @@ public class MapView extends FrameLayout { } protected void onNativeMapViewReady() { + ThreadExecutor threadExecutor = new ThreadExecutor() { + @Override + public void queueRenderEvent(final MapRunnable runnable) { + glSurfaceView.queueEvent(new Runnable() { + @Override + public void run() { + runnable.execute(nativeMapView); + } + }); + } + + @Override + public void queueUiEvent(Runnable runnable) { + glSurfaceView.post(runnable); + } + }; + + + mapState = new MapState(nativeMapView, threadExecutor); // callback for focal point invalidation FocalPointInvalidator focalPoint = new FocalPointInvalidator(compassView); @@ -202,19 +220,19 @@ public class MapView extends FrameLayout { CameraZoomInvalidator zoomInvalidator = new CameraZoomInvalidator(); // setup components for MapboxMap creation - Projection proj = new Projection(nativeMapView); + Projection proj = new Projection(mapState, getWidth(), getHeight()); UiSettings uiSettings = new UiSettings(proj, focalPoint, compassView, attrView, logoView); - TrackingSettings trackingSettings = new TrackingSettings(myLocationView, uiSettings, focalPoint, zoomInvalidator); + TrackingSettings tracking = new TrackingSettings(myLocationView, uiSettings, focalPoint, zoomInvalidator); MyLocationViewSettings myLocationViewSettings = new MyLocationViewSettings(myLocationView, proj, focalPoint); MarkerViewManager markerViewManager = new MarkerViewManager((ViewGroup) findViewById(R.id.markerViewContainer)); - AnnotationManager annotations = new AnnotationManager(nativeMapView, this, markerViewManager); - Transform transform = new Transform(nativeMapView, annotations.getMarkerViewManager(), trackingSettings); - mapboxMap = new MapboxMap(nativeMapView, transform, uiSettings, trackingSettings, myLocationViewSettings, proj, + AnnotationManager annotations = new AnnotationManager(nativeMapView, threadExecutor, this, markerViewManager); + Transform transform = new Transform(nativeMapView, threadExecutor, annotations.getMarkerViewManager(), tracking); + mapboxMap = new MapboxMap(mapState, transform, uiSettings, tracking, myLocationViewSettings, proj, registerTouchListener, annotations); // user input - mapGestureDetector = new MapGestureDetector(getContext(), transform, proj, uiSettings, trackingSettings, annotations); - mapKeyListener = new MapKeyListener(transform, trackingSettings, uiSettings); + mapGestureDetector = new MapGestureDetector(getContext(), transform, proj, uiSettings, tracking, annotations); + mapKeyListener = new MapKeyListener(transform, tracking, uiSettings); mapZoomButtonController = new MapZoomButtonController(this, uiSettings, transform); // inject widgets with MapboxMap @@ -222,9 +240,6 @@ public class MapView extends FrameLayout { myLocationView.setMapboxMap(mapboxMap); attrView.setOnClickListener(new AttributionOnClickListener(getContext(), transform)); - // notify Map object about current connectivity state - nativeMapView.setReachability(isConnected()); - // initialise MapboxMap mapboxMap.initialise(getContext(), mapboxMapOptions); @@ -249,14 +264,12 @@ public class MapView extends FrameLayout { */ @UiThread public void onCreate(@Nullable Bundle savedInstanceState) { - //nativeMapView.setAccessToken(Mapbox.getAccessToken()); - if (savedInstanceState == null) { MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapLoadEvent()); } else if (savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) { + mapState.onRestoreInstanceState(savedInstanceState); mapboxMap.onRestoreInstanceState(savedInstanceState); } - } /** @@ -269,6 +282,7 @@ public class MapView extends FrameLayout { @UiThread public void onSaveInstanceState(@NonNull Bundle outState) { outState.putBoolean(MapboxConstants.STATE_HAS_SAVED_STATE, true); + mapState.onSaveInstanceState(outState); mapboxMap.onSaveInstanceState(outState); } @@ -403,7 +417,12 @@ public class MapView extends FrameLayout { */ @UiThread public void onLowMemory() { - nativeMapView.onLowMemory(); + glSurfaceView.queueEvent(new Runnable() { + @Override + public void run() { + nativeMapView.onLowMemory(); + } + }); } // Called when debug mode is enabled to update a FPS counter @@ -449,17 +468,12 @@ public class MapView extends FrameLayout { * @param url The URL of the map style * @see Style */ - public void setStyleUrl(@NonNull String url) { + public void setStyleUrl(@NonNull final String url) { if (destroyed) { return; } - // stopgap for https://github.com/mapbox/mapbox-gl-native/issues/6242 - if (TextUtils.isEmpty(nativeMapView.getAccessToken())) { - nativeMapView.setAccessToken(Mapbox.getAccessToken()); - } - - nativeMapView.setStyleUrl(url); + mapState.setStyleUrl(url); } // @@ -485,28 +499,6 @@ public class MapView extends FrameLayout { } } - @Override - public void onDraw(Canvas canvas) { - Timber.i("onDraw"); - super.onDraw(canvas); - if (isInEditMode()) { - return; - } - } - - @Override - protected void onSizeChanged(int width, int height, int oldw, int oldh) { - if (destroyed) { - return; - } - - if (!isInEditMode()) { - if (nativeMapView != null) { - //XXX This should happen through GLSurfaceView#Renderer callbacks nativeMapView.resizeView(width, height); - } - } - } - // // View events // @@ -1005,14 +997,4 @@ public class MapView extends FrameLayout { onMapReadyCallbackList.add(callback); } } - - private class RenderRunnable implements Runnable { - @Override - public void run() { - if (destroyed) { - return; - } - nativeMapView.render(); - } - } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 69a95457b8..2ea9f422b9 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -6,7 +6,6 @@ import android.graphics.PointF; import android.graphics.RectF; import android.location.Location; import android.os.Bundle; -import android.os.Handler; import android.support.annotation.FloatRange; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -16,7 +15,6 @@ import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; -import com.mapbox.mapboxsdk.Mapbox; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions; @@ -59,8 +57,7 @@ import timber.log.Timber; */ public final class MapboxMap { - private final NativeMapView nativeMapView; - + private final MapState mapState; private final UiSettings uiSettings; private final TrackingSettings trackingSettings; private final Projection projection; @@ -69,13 +66,12 @@ public final class MapboxMap { private final MyLocationViewSettings myLocationViewSettings; private final OnRegisterTouchListener onRegisterTouchListener; - private MapboxMap.OnFpsChangedListener onFpsChangedListener; - MapboxMap(NativeMapView map, Transform transform, UiSettings ui, TrackingSettings tracking, + MapboxMap(MapState mapState, Transform transform, UiSettings ui, TrackingSettings tracking, MyLocationViewSettings myLocationView, Projection projection, OnRegisterTouchListener listener, AnnotationManager annotations) { - this.nativeMapView = map; + this.mapState = mapState; this.uiSettings = ui; this.trackingSettings = tracking; this.projection = projection; @@ -94,16 +90,23 @@ public final class MapboxMap { // Map configuration setDebugActive(options.getDebugActive()); setApiBaseUrl(options); - setStyleUrl(options); + //setStyleUrl(options); } void onStart() { - nativeMapView.update(); - trackingSettings.onStart(); - if (TextUtils.isEmpty(nativeMapView.getStyleUrl())) { - // if user hasn't loaded a Style yet - nativeMapView.setStyleUrl(Style.MAPBOX_STREETS); + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.update(); + } + }); + + if (TextUtils.isEmpty(mapState.getStyleUrl())) { + Timber.e("Loading default style!!!!"); + mapState.setStyleUrl(Style.MAPBOX_STREETS); } + + trackingSettings.onStart(); } void onStop() { @@ -111,9 +114,7 @@ public final class MapboxMap { } void onSaveInstanceState(Bundle outState) { - outState.putParcelable(MapboxConstants.STATE_CAMERA_POSITION, transform.getCameraPosition()); - outState.putBoolean(MapboxConstants.STATE_DEBUG_ACTIVE, nativeMapView.getDebug()); - outState.putString(MapboxConstants.STATE_STYLE_URL, nativeMapView.getStyleUrl()); + transform.onSaveInstanceState(outState); trackingSettings.onSaveInstanceState(outState); uiSettings.onSaveInstanceState(outState); } @@ -126,12 +127,6 @@ public final class MapboxMap { uiSettings.onRestoreInstanceState(savedInstanceState); trackingSettings.onRestoreInstanceState(savedInstanceState); - nativeMapView.setDebug(savedInstanceState.getBoolean(MapboxConstants.STATE_DEBUG_ACTIVE)); - - final String styleUrl = savedInstanceState.getString(MapboxConstants.STATE_STYLE_URL); - if (!TextUtils.isEmpty(styleUrl)) { - nativeMapView.setStyleUrl(savedInstanceState.getString(MapboxConstants.STATE_STYLE_URL)); - } } /** @@ -157,11 +152,13 @@ public final class MapboxMap { * Called when the user */ void onUpdate() { - CameraPosition cameraPosition = transform.invalidateCameraPosition(); - uiSettings.update(cameraPosition); - // FIXME introduce update method with camera position - trackingSettings.update(); - annotationManager.update(); + CameraPosition cameraPosition = transform.getCameraPosition(); + if (cameraPosition != null) { + uiSettings.update(cameraPosition); + // FIXME introduce update method with camera position + trackingSettings.update(); + annotationManager.update(); + } } // Style @@ -176,7 +173,7 @@ public final class MapboxMap { */ @UiThread public long getTransitionDuration() { - return nativeMapView.getTransitionDuration(); + return mapState.getTransitionDuration(); } /** @@ -186,7 +183,7 @@ public final class MapboxMap { */ @UiThread public void setTransitionDuration(long duration) { - nativeMapView.setTransitionDuration(duration); + mapState.setTransitionDuration(duration); } /** @@ -199,7 +196,7 @@ public final class MapboxMap { */ @UiThread public long getTransitionDelay() { - return nativeMapView.getTransitionDelay(); + return mapState.getTransitionDelay(); } /** @@ -209,13 +206,26 @@ public final class MapboxMap { */ @UiThread public void setTransitionDelay(long delay) { - nativeMapView.setTransitionDelay(delay); + mapState.setTransitionDelay(delay); } - @Nullable + // FIXME: 10/02/2017 javadoc @UiThread - public Layer getLayer(@NonNull String layerId) { - return nativeMapView.getLayer(layerId); + public void getLayer(@NonNull final String layerId, final Callback<Layer> listener) { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + final Layer layer = nativeMapView.getLayer(layerId); + mapState.queueUiEvent(new Runnable() { + @Override + public void run() { + if (listener != null) { + listener.onResult(layer); + } + } + }); + } + }); } /** @@ -225,16 +235,29 @@ public final class MapboxMap { * @param <T> the generic attribute of a Layer * @return the casted Layer, null if another type */ - @Nullable @UiThread - public <T extends Layer> T getLayerAs(@NonNull String layerId) { - try { - // noinspection unchecked - return (T) nativeMapView.getLayer(layerId); - } catch (ClassCastException exception) { - Timber.e(String.format("Layer: %s is a different type: %s", layerId, exception)); - return null; - } + public void getLayerAs(@NonNull final String layerId, final Callback.LayerCallback listener) { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + final Layer layer = nativeMapView.getLayer(layerId); + mapState.queueUiEvent(new Runnable() { + @Override + public void run() { + if (listener != null) { + try { + // noinspection unchecked + listener.onResult(layer); + } catch (ClassCastException exception) { + Timber.e(String.format("Layer: %s is a different type: %s", layerId, exception)); + // noinspection unchecked + listener.onResult(null); + } + } + } + }); + } + }); } /** @@ -254,10 +277,16 @@ public final class MapboxMap { * @param before the layer id to add this layer before */ @UiThread - public void addLayer(@NonNull Layer layer, String before) { - nativeMapView.addLayer(layer, before); + public void addLayer(@NonNull final Layer layer, final String before) { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.addLayer(layer, before); + } + }); } + /** * Removes the layer. Any references to the layer become invalid and should not be used anymore * @@ -265,8 +294,17 @@ public final class MapboxMap { * @throws NoSuchLayerException the exception thrown when layer with layerId doesn't exist */ @UiThread - public void removeLayer(@NonNull String layerId) throws NoSuchLayerException { - nativeMapView.removeLayer(layerId); + public void removeLayer(@NonNull final String layerId) throws NoSuchLayerException { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + try { + nativeMapView.removeLayer(layerId); + } catch (NoSuchLayerException exception) { + // FIXME handle exception + } + } + }); } /** @@ -276,14 +314,33 @@ public final class MapboxMap { * @throws NoSuchLayerException the exeption thrown when the layer doesn't exist */ @UiThread - public void removeLayer(@NonNull Layer layer) throws NoSuchLayerException { - nativeMapView.removeLayer(layer); + public void removeLayer(@NonNull final Layer layer) throws NoSuchLayerException { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + try { + nativeMapView.removeLayer(layer); + } catch (NoSuchLayerException exception) { + // FIXME handle exception + } + } + }); } - @Nullable @UiThread - public Source getSource(@NonNull String sourceId) { - return nativeMapView.getSource(sourceId); + public void getSource(@NonNull final String sourceId, final Callback<Source> listener) { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + final Source source = nativeMapView.getSource(sourceId); + mapState.queueUiEvent(new Runnable() { + @Override + public void run() { + listener.onResult(source); + } + }); + } + }); } /** @@ -293,16 +350,26 @@ public final class MapboxMap { * @param <T> the generic type of a Source * @return the casted Source, null if another type */ - @Nullable @UiThread - public <T extends Source> T getSourceAs(@NonNull String sourceId) { - try { - // noinspection unchecked - return (T) nativeMapView.getSource(sourceId); - } catch (ClassCastException exception) { - Timber.e(String.format("Source: %s is a different type: %s", sourceId, exception)); - return null; - } + public void getSourceAs(@NonNull final String sourceId, final Callback.SourceCallback listener) { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + final Source source = nativeMapView.getSource(sourceId); + mapState.queueUiEvent(new Runnable() { + @Override + public void run() { + try { + // noinspection unchecked + listener.onResult(source); + } catch (ClassCastException exception) { + Timber.e(String.format("Source: %s is a different type: %s", sourceId, exception)); + listener.onResult(null); + } + } + }); + } + }); } /** @@ -311,8 +378,13 @@ public final class MapboxMap { * @param source the source to add */ @UiThread - public void addSource(@NonNull Source source) { - nativeMapView.addSource(source); + public void addSource(@NonNull final Source source) { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.addSource(source); + } + }); } /** @@ -322,8 +394,17 @@ public final class MapboxMap { * @throws NoSuchSourceException the exception thrown when the source with sourceId doesn't exist */ @UiThread - public void removeSource(@NonNull String sourceId) throws NoSuchSourceException { - nativeMapView.removeSource(sourceId); + public void removeSource(@NonNull final String sourceId) throws NoSuchSourceException { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + try { + nativeMapView.removeSource(sourceId); + } catch (NoSuchSourceException exception) { + // FIXME: 10/02/2017 handle exception + } + } + }); } /** @@ -333,8 +414,17 @@ public final class MapboxMap { * @throws NoSuchSourceException the exception thrown when the source with sourceId doesn't exist */ @UiThread - public void removeSource(@NonNull Source source) throws NoSuchSourceException { - nativeMapView.removeSource(source); + public void removeSource(@NonNull final Source source) throws NoSuchSourceException { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + try { + nativeMapView.removeSource(source); + } catch (NoSuchSourceException exception) { + // FIXME: 10/02/2017 handle exception + } + } + }); } /** @@ -344,8 +434,13 @@ public final class MapboxMap { * @param image the pre-multiplied Bitmap */ @UiThread - public void addImage(@NonNull String name, @NonNull Bitmap image) { - nativeMapView.addImage(name, image); + public void addImage(@NonNull final String name, @NonNull final Bitmap image) { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.addImage(name, image); + } + }); } /** @@ -354,8 +449,13 @@ public final class MapboxMap { * @param name the name of the image to remove */ @UiThread - public void removeImage(String name) { - nativeMapView.removeImage(name); + public void removeImage(final String name) { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.removeImage(name); + } + }); } // @@ -465,6 +565,7 @@ public final class MapboxMap { * * @return the Projection associated with this map */ + @UiThread public Projection getProjection() { return projection; } @@ -479,6 +580,7 @@ public final class MapboxMap { * This invokes the {@link CancelableCallback} for ongoing camera updates. * </p> */ + @UiThread public void cancelTransitions() { transform.cancelTransitions(); } @@ -490,6 +592,7 @@ public final class MapboxMap { * * @return The current position of the Camera. */ + @UiThread public final CameraPosition getCameraPosition() { return transform.getCameraPosition(); } @@ -501,6 +604,7 @@ public final class MapboxMap { * * @param cameraPosition the camera position to set */ + @UiThread public void setCameraPosition(@NonNull CameraPosition cameraPosition) { moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), null); } @@ -527,15 +631,10 @@ public final class MapboxMap { */ @UiThread public final void moveCamera(final CameraUpdate update, final MapboxMap.CancelableCallback callback) { - new Handler().post(new Runnable() { - @Override - public void run() { - transform.moveCamera(MapboxMap.this, update, callback); - // MapChange.REGION_DID_CHANGE_ANIMATED is not called for `jumpTo` - // invalidate camera position to provide OnCameraChange event. - invalidateCameraPosition(); - } - }); + transform.moveCamera(MapboxMap.this, update, callback); + // MapChange.REGION_DID_CHANGE_ANIMATED is not called for `jumpTo` + // invalidate camera position to provide OnCameraChange event. + invalidateCameraPosition(); } /** @@ -633,12 +732,7 @@ public final class MapboxMap { @UiThread public final void easeCamera(final CameraUpdate update, final int durationMs, final boolean easingInterpolator, final MapboxMap.CancelableCallback callback) { - new Handler().post(new Runnable() { - @Override - public void run() { - transform.easeCamera(MapboxMap.this, update, durationMs, easingInterpolator, callback); - } - }); + transform.easeCamera(MapboxMap.this, update, durationMs, easingInterpolator, callback); } /** @@ -709,19 +803,14 @@ public final class MapboxMap { @UiThread public final void animateCamera(final CameraUpdate update, final int durationMs, final MapboxMap.CancelableCallback callback) { - new Handler().post(new Runnable() { - @Override - public void run() { - transform.animateCamera(MapboxMap.this, update, durationMs, callback); - } - }); + transform.animateCamera(MapboxMap.this, update, durationMs, callback); } /** * Invalidates the current camera position by reconstructing it from mbgl */ void invalidateCameraPosition() { - CameraPosition cameraPosition = transform.invalidateCameraPosition(); + CameraPosition cameraPosition = transform.getCameraPosition(); if (cameraPosition != null) { transform.updateCameraPosition(cameraPosition); } @@ -738,19 +827,12 @@ public final class MapboxMap { transform.resetNorth(); } - /** - * Set focal bearing. - */ - public void setFocalBearing(double bearing, float focalX, float focalY, long duration) { - transform.setBearing(bearing, focalX, focalY, duration); - } - public float getHeight() { - return nativeMapView.getHeight(); + return projection.getHeight(); } public float getWidth() { - return nativeMapView.getWidth(); + return projection.getWidth(); } // @@ -764,7 +846,7 @@ public final class MapboxMap { */ @UiThread public boolean isDebugActive() { - return nativeMapView.getDebug(); + return mapState.getDebug(); } /** @@ -777,7 +859,7 @@ public final class MapboxMap { */ @UiThread public void setDebugActive(boolean debugActive) { - nativeMapView.setDebug(debugActive); + mapState.setDebug(debugActive); } /** @@ -791,7 +873,7 @@ public final class MapboxMap { */ @UiThread public void cycleDebugOptions() { - nativeMapView.cycleDebugOptions(); + mapState.cycleDebugOptions(); } // @@ -801,7 +883,7 @@ public final class MapboxMap { private void setApiBaseUrl(@NonNull MapboxMapOptions options) { String apiBaseUrl = options.getApiBaseUrl(); if (!TextUtils.isEmpty(apiBaseUrl)) { - nativeMapView.setApiBaseUrl(apiBaseUrl); + mapState.setApiBaseUrl(apiBaseUrl); } } @@ -840,7 +922,7 @@ public final class MapboxMap { */ @UiThread public void setStyleUrl(@NonNull String url) { - nativeMapView.setStyleUrl(url); + mapState.setStyleUrl(url); } /** @@ -869,13 +951,10 @@ public final class MapboxMap { * * @param options the object containing the style url */ + @UiThread private void setStyleUrl(@NonNull MapboxMapOptions options) { String style = options.getStyle(); if (!TextUtils.isEmpty(style)) { - // stopgap for https://github.com/mapbox/mapbox-gl-native/issues/6242 - if (TextUtils.isEmpty(nativeMapView.getAccessToken())) { - nativeMapView.setAccessToken(Mapbox.getAccessToken()); - } setStyleUrl(style); } } @@ -891,7 +970,7 @@ public final class MapboxMap { @UiThread @NonNull public String getStyleUrl() { - return nativeMapView.getStyleUrl(); + return mapState.getStyleUrl(); } // @@ -909,9 +988,8 @@ public final class MapboxMap { * @return The {@code Marker} that was added to the map. */ @UiThread - @NonNull - public Marker addMarker(@NonNull MarkerOptions markerOptions) { - return annotationManager.addMarker(markerOptions, this); + public void addMarker(@NonNull MarkerOptions markerOptions) { + annotationManager.addMarker(markerOptions, this, null); } /** @@ -925,9 +1003,37 @@ public final class MapboxMap { * @return The {@code Marker} that was added to the map. */ @UiThread - @NonNull - public Marker addMarker(@NonNull BaseMarkerOptions markerOptions) { - return annotationManager.addMarker(markerOptions, this); + public void addMarker(@NonNull MarkerOptions markerOptions, @Nullable Callback<Marker> markerCallback) { + annotationManager.addMarker(markerOptions, this, markerCallback); + } + + + /** + * <p> + * Adds a marker to this map. + * </p> + * The marker's icon is rendered on the map at the location {@code Marker.position}. + * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet. + * + * @param markerOptions A marker options object that defines how to render the marker. + */ + @UiThread + public void addMarker(@NonNull BaseMarkerOptions markerOptions) { + annotationManager.addMarker(markerOptions, this, null); + } + + /** + * <p> + * Adds a marker to this map. + * </p> + * The marker's icon is rendered on the map at the location {@code Marker.position}. + * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet. + * + * @param markerOptions A marker options object that defines how to render the marker. + */ + @UiThread + public void addMarker(@NonNull BaseMarkerOptions markerOptions, @Nullable Callback<Marker> markerCallback) { + annotationManager.addMarker(markerOptions, this, markerCallback); } /** @@ -942,8 +1048,8 @@ public final class MapboxMap { */ @UiThread @NonNull - public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions) { - return annotationManager.addMarker(markerOptions, this, null); + public void addMarker(@NonNull BaseMarkerViewOptions markerOptions) { + annotationManager.addMarker(markerOptions, this, null); } @@ -959,29 +1065,34 @@ public final class MapboxMap { * @return The {@code Marker} that was added to the map. */ @UiThread - @NonNull - public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions, - final MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener) { - return annotationManager.addMarker(markerOptions, this, onMarkerViewAddedListener); + public void addMarker(@NonNull BaseMarkerViewOptions markerOptions, + Callback<MarkerView> listener) { + annotationManager.addMarker(markerOptions, this, listener); } /** * FIXME javadoc */ @UiThread - @NonNull - public List<MarkerView> addMarkerViews(@NonNull List<? extends - BaseMarkerViewOptions> markerViewOptions) { - return annotationManager.addMarkerViews(markerViewOptions, this); + public void addMarkerViews(@NonNull List<? extends BaseMarkerViewOptions> markerViewOptions) { + annotationManager.addMarkerViews(markerViewOptions, this, null); } /** * FIXME javadoc */ @UiThread - @NonNull - public List<MarkerView> getMarkerViewsInRect(@NonNull RectF rect) { - return annotationManager.getMarkerViewsInRect(rect); + public void addMarkerViews(@NonNull List<? extends BaseMarkerViewOptions> markerViewOptions, + Callback<List<MarkerView>> callback) { + annotationManager.addMarkerViews(markerViewOptions, this, callback); + } + + /** + * FIXME javadoc + */ + @UiThread + public void getMarkerViewsInRect(@NonNull RectF rect, Callback<List<MarkerView>> listener) { + annotationManager.getMarkerViewsInRect(rect, listener); } /** @@ -995,10 +1106,9 @@ public final class MapboxMap { * @return A list of the {@code Marker}s that were added to the map. */ @UiThread - @NonNull - public List<Marker> addMarkers(@NonNull List<? extends - BaseMarkerOptions> markerOptionsList) { - return annotationManager.addMarkers(markerOptionsList, this); + public void addMarkers(@NonNull List<? extends + BaseMarkerOptions> markerOptionsList, Callback<List<Marker>> listCallback) { + annotationManager.addMarkers(markerOptionsList, this, listCallback); } /** @@ -1020,9 +1130,8 @@ public final class MapboxMap { * @return The {@code Polyine} that was added to the map. */ @UiThread - @NonNull - public Polyline addPolyline(@NonNull PolylineOptions polylineOptions) { - return annotationManager.addPolyline(polylineOptions, this); + public void addPolyline(@NonNull PolylineOptions polylineOptions, Callback<Polyline> listener) { + annotationManager.addPolyline(polylineOptions, this, listener); } /** @@ -1032,9 +1141,8 @@ public final class MapboxMap { * @return A list of the {@code Polyline}s that were added to the map. */ @UiThread - @NonNull - public List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList) { - return annotationManager.addPolylines(polylineOptionsList, this); + public void addPolylines(@NonNull List<PolylineOptions> polylineOptionsList, Callback<List<Polyline>> listener) { + annotationManager.addPolylines(polylineOptionsList, this, listener); } /** @@ -1054,9 +1162,8 @@ public final class MapboxMap { * @return The {@code Polygon} that was added to the map. */ @UiThread - @NonNull - public Polygon addPolygon(@NonNull PolygonOptions polygonOptions) { - return annotationManager.addPolygon(polygonOptions, this); + public void addPolygon(@NonNull PolygonOptions polygonOptions, Callback<Polygon> listener) { + annotationManager.addPolygon(polygonOptions, this, listener); } /** @@ -1066,9 +1173,8 @@ public final class MapboxMap { * @return A list of the {@code Polygon}s that were added to the map. */ @UiThread - @NonNull - public List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList) { - return annotationManager.addPolygons(polygonOptionsList, this); + public void addPolygons(@NonNull List<PolygonOptions> polygonOptionsList, Callback<List<Polygon>> listener) { + annotationManager.addPolygons(polygonOptionsList, this, listener); } @@ -1591,11 +1697,15 @@ public final class MapboxMap { * Takes a snapshot of the map. * * @param callback Callback method invoked when the snapshot is taken. - * @param bitmap A pre-allocated bitmap. */ @UiThread - public void snapshot(@NonNull SnapshotReadyCallback callback) { - nativeMapView.addSnapshotCallback(callback); + public void snapshot(@NonNull final SnapshotReadyCallback callback) { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.addSnapshotCallback(callback); + } + }); } /** @@ -1606,10 +1716,20 @@ public final class MapboxMap { * @return the list of feature */ @UiThread - @NonNull - public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates, @Nullable String... - layerIds) { - return nativeMapView.queryRenderedFeatures(coordinates, layerIds); + public void queryRenderedFeatures(@NonNull final PointF coordinates, @NonNull final Callback<List<Feature>> callback, + @Nullable final String... layerIds) { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + final List<Feature> features = nativeMapView.queryRenderedFeatures(coordinates, layerIds); + mapState.queueUiEvent(new Runnable() { + @Override + public void run() { + callback.onResult(features); + } + }); + } + }); } /** @@ -1620,10 +1740,20 @@ public final class MapboxMap { * @return the list of feature */ @UiThread - @NonNull - public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates, @Nullable String... - layerIds) { - return nativeMapView.queryRenderedFeatures(coordinates, layerIds); + public void queryRenderedFeatures(@NonNull final RectF coordinates, @NonNull final Callback<List<Feature>> callback, + @Nullable final String... layerIds) { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + final List<Feature> features = nativeMapView.queryRenderedFeatures(coordinates, layerIds); + mapState.queueUiEvent(new Runnable() { + @Override + public void run() { + callback.onResult(features); + } + }); + } + }); } // @@ -2012,4 +2142,4 @@ public final class MapboxMap { Transform getTransform() { return transform; } -} +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index f5fd886662..c1f6afbda1 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -578,10 +578,15 @@ final class NativeMapView { * * @param bitmap the snapshot as a bitmap */ - protected void onSnapshotReady(Bitmap bitmap) { - if (snapshotReadyCallback != null && bitmap != null) { - snapshotReadyCallback.onSnapshotReady(bitmap); - } + protected void onSnapshotReady(final Bitmap bitmap) { + mapView.post(new Runnable() { + @Override + public void run() { + if (snapshotReadyCallback != null && bitmap != null) { + snapshotReadyCallback.onSnapshotReady(bitmap); + } + } + }); } native void setReachability(boolean status); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java index b7f93cc913..b52184912c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java @@ -15,25 +15,34 @@ import com.mapbox.mapboxsdk.geometry.VisibleRegion; */ public class Projection { - private final NativeMapView nativeMapView; - private int[] contentPadding; + private final MapState mapState; + private final float height; + private final float width; - Projection(@NonNull NativeMapView nativeMapView) { - this.nativeMapView = nativeMapView; - this.contentPadding = new int[] {0, 0, 0, 0}; + private int[] contentPadding = new int[] {0, 0, 0, 0}; + + Projection(@NonNull MapState mapState, float width, float height) { + this.mapState = mapState; + this.width = width; + this.height = height; } void setContentPadding(int[] contentPadding, int[] userLocationViewPadding) { this.contentPadding = contentPadding; - int[] padding = new int[] { + final int[] padding = new int[] { contentPadding[0] + userLocationViewPadding[0], contentPadding[1] + userLocationViewPadding[1], contentPadding[2] + userLocationViewPadding[2], contentPadding[3] + userLocationViewPadding[3] }; - nativeMapView.setContentPadding(padding); + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.setContentPadding(padding); + } + }); } int[] getContentPadding() { @@ -54,8 +63,20 @@ public class Projection { * @param latitude The latitude for which to return the value. * @return The distance measured in meters. */ - public double getMetersPerPixelAtLatitude(@FloatRange(from = -90, to = 90) double latitude) { - return nativeMapView.getMetersPerPixelAtLatitude(latitude); + public void getMetersPerPixelAtLatitude(@FloatRange(from = -90, to = 90) final double latitude, + final Callback<Double> listener) { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + final double metersPerPixel = nativeMapView.getMetersPerPixelAtLatitude(latitude); + mapState.queueUiEvent(new Runnable() { + @Override + public void run() { + listener.onResult(metersPerPixel); + } + }); + } + }); } /** @@ -67,8 +88,19 @@ public class Projection { * @return The LatLng corresponding to the point on the screen, or null if the ray through * the given screen point does not intersect the ground plane. */ - public LatLng fromScreenLocation(PointF point) { - return nativeMapView.latLngForPixel(point); + public void fromScreenLocation(final PointF point, final Callback<LatLng> listener) { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + final LatLng latLng = nativeMapView.latLngForPixel(point); + mapState.queueUiEvent(new Runnable() { + @Override + public void run() { + listener.onResult(latLng); + } + }); + } + }); } /** @@ -77,25 +109,37 @@ public class Projection { * * @return The projection of the viewing frustum in its current state. */ - public VisibleRegion getVisibleRegion() { - LatLngBounds.Builder builder = new LatLngBounds.Builder(); - - float left = contentPadding[0]; - float right = nativeMapView.getWidth() - contentPadding[2]; - float top = contentPadding[1]; - float bottom = nativeMapView.getHeight() - contentPadding[3]; - - LatLng topLeft = fromScreenLocation(new PointF(left, top)); - LatLng topRight = fromScreenLocation(new PointF(right, top)); - LatLng bottomRight = fromScreenLocation(new PointF(right, bottom)); - LatLng bottomLeft = fromScreenLocation(new PointF(left, bottom)); - - builder.include(topLeft) - .include(topRight) - .include(bottomRight) - .include(bottomLeft); - - return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight, builder.build()); + public void getVisibleRegion(final Callback<VisibleRegion> listener) { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + LatLngBounds.Builder builder = new LatLngBounds.Builder(); + + final float left = contentPadding[0]; + float right = nativeMapView.getWidth() - contentPadding[2]; + float top = contentPadding[1]; + float bottom = nativeMapView.getHeight() - contentPadding[3]; + + LatLng topLeft = nativeMapView.latLngForPixel(new PointF(left, top)); + LatLng topRight = nativeMapView.latLngForPixel(new PointF(right, top)); + LatLng bottomRight = nativeMapView.latLngForPixel(new PointF(right, bottom)); + LatLng bottomLeft = nativeMapView.latLngForPixel(new PointF(left, bottom)); + + LatLngBounds bounds = builder.include(topLeft) + .include(topRight) + .include(bottomRight) + .include(bottomLeft) + .build(); + + final VisibleRegion visibleRegion = new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight, bounds); + mapState.queueUiEvent(new Runnable() { + @Override + public void run() { + listener.onResult(visibleRegion); + } + }); + } + }); } /** @@ -106,15 +150,26 @@ public class Projection { * @param location A LatLng on the map to convert to a screen location. * @return A Point representing the screen location in screen pixels. */ - public PointF toScreenLocation(LatLng location) { - return nativeMapView.pixelForLatLng(location); + public void toScreenLocation(final LatLng location, final Callback<PointF> listener) { + mapState.queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + final PointF pointF = nativeMapView.pixelForLatLng(location); + mapState.queueUiEvent(new Runnable() { + @Override + public void run() { + listener.onResult(pointF); + } + }); + } + }); } float getHeight() { - return nativeMapView.getHeight(); + return height; } float getWidth() { - return nativeMapView.getWidth(); + return width; } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/State.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/State.java new file mode 100644 index 0000000000..5835312371 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/State.java @@ -0,0 +1,13 @@ +package com.mapbox.mapboxsdk.maps; + +import android.os.Bundle; +import android.support.annotation.UiThread; + +interface State { + + @UiThread + void onSaveInstanceState(Bundle outState); + + @UiThread + void onRestoreInstanceState(Bundle savedInstanceState); +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ThreadExecutor.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ThreadExecutor.java new file mode 100644 index 0000000000..1533ca5733 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ThreadExecutor.java @@ -0,0 +1,12 @@ +package com.mapbox.mapboxsdk.maps; + +import android.support.annotation.UiThread; +import android.support.annotation.WorkerThread; + +interface ThreadExecutor { + @UiThread + void queueRenderEvent(MapRunnable runnable); + + @WorkerThread + void queueUiEvent(Runnable runnable); +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java index 97170ca3d4..f2edac63a8 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java @@ -1,8 +1,10 @@ package com.mapbox.mapboxsdk.maps; +import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.UiThread; +import android.support.annotation.WorkerThread; import com.mapbox.mapboxsdk.annotations.MarkerViewManager; import com.mapbox.mapboxsdk.camera.CameraPosition; @@ -16,8 +18,6 @@ import java.util.concurrent.TimeUnit; import timber.log.Timber; -import static com.mapbox.mapboxsdk.maps.MapView.REGION_DID_CHANGE_ANIMATED; - /** * Resembles the current Map transformation. * <p> @@ -25,19 +25,24 @@ import static com.mapbox.mapboxsdk.maps.MapView.REGION_DID_CHANGE_ANIMATED; * {@link com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraChangeListener}. * </p> */ -final class Transform implements MapView.OnMapChangedListener { +// FIXME use MapState instead of exending MapThreadExecutor +final class Transform extends MapThreadExecutor implements State { - private final NativeMapView mapView; private final MarkerViewManager markerViewManager; private final TrackingSettings trackingSettings; private final MyLocationView myLocationView; - private CameraPosition cameraPosition; private MapboxMap.CancelableCallback cameraCancelableCallback; private MapboxMap.OnCameraChangeListener onCameraChangeListener; - Transform(NativeMapView mapView, MarkerViewManager markerViewManager, TrackingSettings trackingSettings) { - this.mapView = mapView; + private LatLng centerLatLng; + private double bearing; + private double tilt; + private double zoom; + + Transform(NativeMapView mapView, ThreadExecutor threadExecutor, MarkerViewManager markerViewManager, + TrackingSettings trackingSettings) { + super(mapView, threadExecutor); this.markerViewManager = markerViewManager; this.trackingSettings = trackingSettings; this.myLocationView = trackingSettings.getMyLocationView(); @@ -51,15 +56,43 @@ final class Transform implements MapView.OnMapChangedListener { } // + // State + // + + @Override + public void onSaveInstanceState(Bundle outState) { + outState.putDouble(MapboxConstants.STATE_CAMERA_LAT, centerLatLng.getLatitude()); + outState.putDouble(MapboxConstants.STATE_CAMERA_LNG, centerLatLng.getLongitude()); + outState.putDouble(MapboxConstants.STATE_CAMERA_BEARING, bearing); + outState.putDouble(MapboxConstants.STATE_CAMERA_TILT, tilt); + outState.putDouble(MapboxConstants.STATE_CAMERA_ZOOM, zoom); + } + + @Override + public void onRestoreInstanceState(Bundle savedInstanceState) { + double latitude = savedInstanceState.getDouble(MapboxConstants.STATE_CAMERA_LAT, 0); + double longitude = savedInstanceState.getDouble(MapboxConstants.STATE_CAMERA_LNG, 0); + centerLatLng = new LatLng(latitude, longitude); + bearing = savedInstanceState.getDouble(MapboxConstants.STATE_CAMERA_BEARING, 0); + tilt = savedInstanceState.getDouble(MapboxConstants.STATE_CAMERA_TILT, 0); + zoom = savedInstanceState.getDouble(MapboxConstants.STATE_CAMERA_ZOOM, 0); + // FIXME: 10/02/2017 ACTUALLY RESTORE STATE + } + + // // Camera API // @UiThread public final CameraPosition getCameraPosition() { - if (cameraPosition == null) { - cameraPosition = invalidateCameraPosition(); - } - return cameraPosition; + return new CameraPosition.Builder(centerLatLng, zoom, tilt, bearing).build(); + } + + void setCameraPosition(CameraPosition cameraPostion) { + centerLatLng = cameraPostion.target; + tilt = cameraPostion.tilt; + bearing = cameraPostion.bearing; + zoom = cameraPostion.zoom; } @UiThread @@ -70,87 +103,123 @@ final class Transform implements MapView.OnMapChangedListener { markerViewManager.setTilt((float) position.tilt); } - @Override - public void onMapChanged(@MapView.MapChange int change) { - if (change == REGION_DID_CHANGE_ANIMATED && cameraCancelableCallback != null) { - updateCameraPosition(invalidateCameraPosition()); - if (cameraCancelableCallback != null) { - cameraCancelableCallback.onFinish(); - cameraCancelableCallback = null; + @UiThread + final void moveCamera(final MapboxMap mapboxMap, final CameraUpdate update, + final MapboxMap.CancelableCallback callback) { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + CameraPosition cameraPosition = update.getCameraPosition(mapboxMap); + setCameraPosition(cameraPosition); + cancelTransitions(nativeMapView); + nativeMapView.jumpTo(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt, cameraPosition.zoom); + trackingSettings.resetTrackingModesIfRequired(cameraPosition); + if (callback != null) { + callback.onFinish(); + } } - mapView.removeOnMapChangedListener(this); - } - } + }); - @UiThread - final void moveCamera(MapboxMap mapboxMap, CameraUpdate update, MapboxMap.CancelableCallback callback) { - cameraPosition = update.getCameraPosition(mapboxMap); - trackingSettings.resetTrackingModesIfRequired(cameraPosition); - cancelTransitions(); - mapView.jumpTo(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt, cameraPosition.zoom); - if (callback != null) { - callback.onFinish(); - } } @UiThread - final void easeCamera(MapboxMap mapboxMap, CameraUpdate update, int durationMs, boolean easingInterpolator, - final MapboxMap.CancelableCallback callback) { - cameraPosition = update.getCameraPosition(mapboxMap); - trackingSettings.resetTrackingModesIfRequired(cameraPosition); - - cancelTransitions(); - if (callback != null) { - cameraCancelableCallback = callback; - mapView.addOnMapChangedListener(this); - } - - mapView.easeTo(cameraPosition.bearing, cameraPosition.target, getDurationNano(durationMs), cameraPosition.tilt, - cameraPosition.zoom, easingInterpolator); + final void easeCamera(final MapboxMap mapboxMap, final CameraUpdate update, final int durationMs, + final boolean easingInterpolator, final MapboxMap.CancelableCallback callback) { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + CameraPosition position = update.getCameraPosition(mapboxMap); + trackingSettings.resetTrackingModesIfRequired(position); + + cancelTransitions(nativeMapView); + if (callback != null) { + cameraCancelableCallback = callback; + // nativeMapView.addOnMapChangedListener(Transform.this); + } + + nativeMapView.easeTo(position.bearing, position.target, getDurationNano(durationMs), position.tilt, + position.zoom, easingInterpolator); + } + }); } @UiThread - final void animateCamera(MapboxMap mapboxMap, CameraUpdate update, int durationMs, + final void animateCamera(final MapboxMap mapboxMap, final CameraUpdate update, final int durationMs, final MapboxMap.CancelableCallback callback) { - cameraPosition = update.getCameraPosition(mapboxMap); - trackingSettings.resetTrackingModesIfRequired(cameraPosition); - - cancelTransitions(); - if (callback != null) { - cameraCancelableCallback = callback; - mapView.addOnMapChangedListener(this); - } - - mapView.flyTo(cameraPosition.bearing, cameraPosition.target, getDurationNano(durationMs), cameraPosition.tilt, - cameraPosition.zoom); + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + CameraPosition position = update.getCameraPosition(mapboxMap); + trackingSettings.resetTrackingModesIfRequired(position); + + cancelTransitions(nativeMapView); + if (callback != null) { + cameraCancelableCallback = callback; + // nativeMapView.addOnMapChangedListener(Transform.this); + } + + nativeMapView.flyTo(position.bearing, position.target, getDurationNano(durationMs), position.tilt, + position.zoom); + } + }); } @UiThread - @Nullable - CameraPosition invalidateCameraPosition() { - if (mapView != null) { - cameraPosition = new CameraPosition.Builder(mapView.getCameraValues()).build(); - if (onCameraChangeListener != null) { - onCameraChangeListener.onCameraChange(this.cameraPosition); + void invalidateCameraPosition() { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + final CameraPosition cameraPosition = new CameraPosition.Builder(nativeMapView.getCameraValues()).build(); + queueUiEvent(new Runnable() { + @Override + public void run() { + updateCameraPosition(cameraPosition); + if (onCameraChangeListener != null) { + // post camera change event on ui Thread + onCameraChangeListener.onCameraChange(cameraPosition); + } + } + }); } - } - return cameraPosition; + }); } + @UiThread void cancelTransitions() { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + cancelTransitions(nativeMapView); + } + }); + } + + @WorkerThread + void cancelTransitions(NativeMapView nativeMapView) { if (cameraCancelableCallback != null) { - cameraCancelableCallback.onCancel(); - cameraCancelableCallback = null; + queueUiEvent(new Runnable() { + @Override + public void run() { + cameraCancelableCallback.onCancel(); + cameraCancelableCallback = null; + } + }); } - mapView.cancelTransitions(); + nativeMapView.cancelTransitions(); } @UiThread void resetNorth() { - cancelTransitions(); - mapView.resetNorth(); + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + cancelTransitions(nativeMapView); + nativeMapView.resetNorth(); + } + }); } + @UiThread void setOnCameraChangeListener(@Nullable MapboxMap.OnCameraChangeListener listener) { this.onCameraChangeListener = listener; } @@ -159,151 +228,175 @@ final class Transform implements MapView.OnMapChangedListener { return durationMs > 0 ? TimeUnit.NANOSECONDS.convert(durationMs, TimeUnit.MILLISECONDS) : 0; } - // - // non Camera API - // - - // Zoom in or out - + @UiThread double getZoom() { - return cameraPosition.zoom; + return zoom; } + @UiThread void zoom(boolean zoomIn) { + zoom = zoomIn ? zoom + 1 : zoom - 1; zoom(zoomIn, -1.0f, -1.0f); } - void zoom(boolean zoomIn, float x, float y) { - // Cancel any animation - cancelTransitions(); + @UiThread + void zoom(final boolean zoomIn, final float x, final float y) { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + cancelTransitions(nativeMapView); + + if (zoomIn) { + nativeMapView.scaleBy(2.0, x, y, MapboxConstants.ANIMATION_DURATION); + } else { + nativeMapView.scaleBy(0.5, x, y, MapboxConstants.ANIMATION_DURATION); + } + } + }); - if (zoomIn) { - mapView.scaleBy(2.0, x, y, MapboxConstants.ANIMATION_DURATION); - } else { - mapView.scaleBy(0.5, x, y, MapboxConstants.ANIMATION_DURATION); - } } - void setZoom(double zoom) { - mapView.setZoom(zoom); + @UiThread + void setZoom(final double zoom) { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.setZoom(zoom); + } + }); } - // Direction + @UiThread double getBearing() { - double direction = -mapView.getBearing(); - - while (direction > 360) { - direction -= 360; - } - while (direction < 0) { - direction += 360; - } - - return direction; + return bearing; } - double getRawBearing() { - return mapView.getBearing(); - } - - void setBearing(double bearing) { - if (myLocationView != null) { - myLocationView.setBearing(bearing); - } - mapView.setBearing(bearing); - } - - void setBearing(double bearing, float focalX, float focalY) { + @UiThread + void setBearing(final double bearing) { if (myLocationView != null) { myLocationView.setBearing(bearing); } - mapView.setBearing(bearing, focalX, focalY); + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.setBearing(bearing); + } + }); } - void setBearing(double bearing, float focalX, float focalY, long duration) { + @UiThread + void setBearing(final double bearing, final float focalX, final float focalY) { + this.bearing = bearing; if (myLocationView != null) { myLocationView.setBearing(bearing); } - mapView.setBearing(bearing, focalX, focalY, duration); - } - - - // - // LatLng / CenterCoordinate - // - - LatLng getLatLng() { - return mapView.getLatLng(); + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.setBearing(bearing, focalX, focalY); + } + }); } // // Pitch / Tilt // + @UiThread double getTilt() { - return mapView.getPitch(); + return tilt; } - void setTilt(Double pitch) { + @UiThread + void setTilt(final Double pitch) { + tilt = pitch; if (myLocationView != null) { myLocationView.setTilt(pitch); } markerViewManager.setTilt(pitch.floatValue()); - mapView.setPitch(pitch, 0); + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.setPitch(pitch, 0); + } + }); } // // Center coordinate // - LatLng getCenterCoordinate() { - return mapView.getLatLng(); - } - - void setCenterCoordinate(LatLng centerCoordinate) { - mapView.setLatLng(centerCoordinate); - } - - void setGestureInProgress(boolean gestureInProgress) { - mapView.setGestureInProgress(gestureInProgress); + void setGestureInProgress(final boolean gestureInProgress) { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.setGestureInProgress(gestureInProgress); + } + }); if (!gestureInProgress) { invalidateCameraPosition(); } } - void zoomBy(double pow, float x, float y) { - mapView.scaleBy(pow, x, y); + void zoomBy(final double pow, final float x, final float y) { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.scaleBy(pow, x, y); + } + }); } - void moveBy(double offsetX, double offsetY, long duration) { - mapView.moveBy(offsetX, offsetY, duration); + void moveBy(final double offsetX, final double offsetY, final long duration) { + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.moveBy(offsetX, offsetY, duration); + } + }); } // // Min & Max ZoomLevel // - void setMinZoom(double minZoom) { + private double minZoom; + private double maxZoom; + + void setMinZoom(final double minZoom) { if ((minZoom < MapboxConstants.MINIMUM_ZOOM) || (minZoom > MapboxConstants.MAXIMUM_ZOOM)) { Timber.e("Not setting minZoomPreference, value is in unsupported range: " + minZoom); return; } - mapView.setMinZoom(minZoom); + this.minZoom = minZoom; + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.setMinZoom(minZoom); + } + }); } double getMinZoom() { - return mapView.getMinZoom(); + return minZoom; } - void setMaxZoom(double maxZoom) { + void setMaxZoom(final double maxZoom) { if ((maxZoom < MapboxConstants.MINIMUM_ZOOM) || (maxZoom > MapboxConstants.MAXIMUM_ZOOM)) { Timber.e("Not setting maxZoomPreference, value is in unsupported range: " + maxZoom); return; } - mapView.setMaxZoom(maxZoom); + + this.maxZoom = maxZoom; + queueRenderEvent(new MapRunnable() { + @Override + public void execute(@NonNull NativeMapView nativeMapView) { + nativeMapView.setMaxZoom(maxZoom); + } + }); } double getMaxZoom() { - return mapView.getMaxZoom(); + return maxZoom; } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java index ed6ef5199a..b3a7fd787f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java @@ -172,12 +172,14 @@ public final class CompassView extends AppCompatImageView implements Runnable, F final MapboxMap mapboxMap = this.mapboxMap.get(); final CompassView compassView = this.compassView.get(); if (mapboxMap != null && compassView != null) { - PointF focalPoint = compassView.getFocalPoint(); - if (focalPoint != null) { - mapboxMap.setFocalBearing(0, focalPoint.x, focalPoint.y, TIME_MAP_NORTH_ANIMATION); - } else { - mapboxMap.setFocalBearing(0, mapboxMap.getWidth() / 2, mapboxMap.getHeight() / 2, TIME_MAP_NORTH_ANIMATION); - } + //PointF focalPoint = compassView.getFocalPoint(); + //if (focalPoint != null) { + //mapboxMap.setFocalBearing(0, focalPoint.x, focalPoint.y, TIME_MAP_NORTH_ANIMATION); + //} else { + //mapboxMap.setFocalBearing(0, mapboxMap.getWidth() / 2, mapboxMap.getHeight() / 2, TIME_MAP_NORTH_ANIMATION); + //} + // FIXME: 10/02/2017 with animation and focal point + mapboxMap.resetNorth(); compassView.postDelayed(compassView, TIME_WAIT_IDLE + TIME_MAP_NORTH_ANIMATION); } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java index c9888c36d2..fdc9f2dcb0 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java @@ -32,6 +32,7 @@ import com.mapbox.mapboxsdk.constants.MyBearingTracking; import com.mapbox.mapboxsdk.constants.MyLocationTracking; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.location.LocationSource; +import com.mapbox.mapboxsdk.maps.Callback; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.Projection; import com.mapbox.services.android.telemetry.location.LocationEngine; @@ -60,6 +61,7 @@ public class MyLocationView extends View { private Location location; private long locationUpdateTimestamp; private float previousDirection; + private float metersPerPixel; private float accuracy; private Paint accuracyPaint; @@ -256,7 +258,6 @@ public class MyLocationView extends View { } final PointF pointF = screenLocation; - float metersPerPixel = (float) projection.getMetersPerPixelAtLatitude(location.getLatitude()); float accuracyPixels = (Float) accuracyAnimator.getAnimatedValue() / metersPerPixel / 2; float maxRadius = getWidth() / 2; accuracyPixels = accuracyPixels <= maxRadius ? accuracyPixels : maxRadius; @@ -333,6 +334,15 @@ public class MyLocationView extends View { setBearing(position.bearing); setTilt(position.tilt); } + + if (location != null) { + projection.getMetersPerPixelAtLatitude(location.getLatitude(), new Callback<Double>() { + @Override + public void onResult(Double aDouble) { + metersPerPixel = (float) aDouble.doubleValue(); + } + }); + } } public void onStart() { @@ -837,9 +847,14 @@ public class MyLocationView extends View { @Override void invalidate() { if (latLng != null) { - screenLocation = projection.toScreenLocation(latLng); + projection.toScreenLocation(latLng, new Callback<PointF>() { + @Override + public void onResult(PointF pointF) { + screenLocation = pointF; + MyLocationView.this.invalidate(); + } + }); } - MyLocationView.this.invalidate(); } } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java index e9d823ebda..81ae044777 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java @@ -21,7 +21,7 @@ public class MyLocationViewSettings { private FocalPointChangeListener focalPointChangeListener; // - // State + // MapThreadExecutor // private boolean enabled; |