diff options
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps')
27 files changed, 1580 insertions, 953 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationContainer.java new file mode 100644 index 0000000000..939fadc9c2 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationContainer.java @@ -0,0 +1,86 @@ +package com.mapbox.mapboxsdk.maps; + + +import android.support.annotation.NonNull; +import android.support.v4.util.LongSparseArray; + +import com.mapbox.mapboxsdk.annotations.Annotation; + +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates {@link Annotation}'s functionality.. + */ +class AnnotationContainer implements Annotations { + + private final NativeMapView nativeMapView; + private final LongSparseArray<Annotation> annotations; + + AnnotationContainer(NativeMapView nativeMapView, LongSparseArray<Annotation> annotations) { + this.nativeMapView = nativeMapView; + this.annotations = annotations; + } + + @Override + public Annotation obtainBy(long id) { + return annotations.get(id); + } + + @Override + public List<Annotation> obtainAll() { + List<Annotation> annotations = new ArrayList<>(); + for (int i = 0; i < this.annotations.size(); i++) { + annotations.add(this.annotations.get(this.annotations.keyAt(i))); + } + return annotations; + } + + @Override + public void removeBy(long id) { + if (nativeMapView != null) { + nativeMapView.removeAnnotation(id); + } + annotations.remove(id); + } + + @Override + public void removeBy(@NonNull Annotation annotation) { + long id = annotation.getId(); + removeBy(id); + } + + @Override + public void removeBy(@NonNull List<? extends Annotation> annotationList) { + int count = annotationList.size(); + long[] ids = new long[count]; + for (int i = 0; i < count; i++) { + ids[i] = annotationList.get(i).getId(); + } + + removeNativeAnnotations(ids); + + for (long id : ids) { + annotations.remove(id); + } + } + + @Override + public void removeAll() { + int count = annotations.size(); + long[] ids = new long[count]; + for (int i = 0; i < count; i++) { + ids[i] = annotations.keyAt(i); + } + + removeNativeAnnotations(ids); + + annotations.clear(); + } + + private void removeNativeAnnotations(long[] ids) { + if (nativeMapView != null) { + nativeMapView.removeAnnotations(ids); + } + } +}
\ No newline at end of file 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 d15d5eddf8..c09c926eb5 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 @@ -14,7 +14,6 @@ import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions; -import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.MarkerView; import com.mapbox.mapboxsdk.annotations.MarkerViewManager; @@ -45,12 +44,11 @@ class AnnotationManager { private static final String LAYER_ID_SHAPE_ANNOTATIONS = "com.mapbox.annotations.shape."; private static final long NO_ANNOTATION_ID = -1; - private final NativeMapView nativeMapView; private final MapView mapView; private final IconManager iconManager; private final InfoWindowManager infoWindowManager = new InfoWindowManager(); private final MarkerViewManager markerViewManager; - private final LongSparseArray<Annotation> annotations = new LongSparseArray<>(); + private final LongSparseArray<Annotation> annotationsArray; private final List<Marker> selectedMarkers = new ArrayList<>(); private final List<String> shapeAnnotationIds = new ArrayList<>(); @@ -59,11 +57,22 @@ class AnnotationManager { private MapboxMap.OnPolygonClickListener onPolygonClickListener; private MapboxMap.OnPolylineClickListener onPolylineClickListener; - AnnotationManager(NativeMapView view, MapView mapView, MarkerViewManager markerViewManager) { - this.nativeMapView = view; + private Annotations annotations; + private Markers markers; + private Polygons polygons; + private Polylines polylines; + + AnnotationManager(NativeMapView view, MapView mapView, LongSparseArray<Annotation> annotationsArray, + MarkerViewManager markerViewManager, IconManager iconManager, Annotations annotations, + Markers markers, Polygons polygons, Polylines polylines) { this.mapView = mapView; - this.iconManager = new IconManager(nativeMapView); + this.annotationsArray = annotationsArray; this.markerViewManager = markerViewManager; + this.iconManager = iconManager; + this.annotations = annotations; + this.markers = markers; + this.polygons = polygons; + this.polylines = polylines; if (view != null) { // null checking needed for unit tests view.addOnMapChangedListener(markerViewManager); @@ -88,15 +97,15 @@ class AnnotationManager { // Annotation getAnnotation(long id) { - return annotations.get(id); + return annotations.obtainBy(id); } List<Annotation> getAnnotations() { - List<Annotation> annotations = new ArrayList<>(); - for (int i = 0; i < this.annotations.size(); i++) { - annotations.add(this.annotations.get(this.annotations.keyAt(i))); - } - return annotations; + return annotations.obtainAll(); + } + + void removeAnnotation(long id) { + annotations.removeBy(id); } void removeAnnotation(@NonNull Annotation annotation) { @@ -109,30 +118,19 @@ class AnnotationManager { if (marker instanceof MarkerView) { markerViewManager.removeMarkerView((MarkerView) marker); + } else { + // do icon cleanup + iconManager.iconCleanup(marker.getIcon()); } } else { // instanceOf Polygon/Polyline shapeAnnotationIds.remove(annotation.getId()); } - long id = annotation.getId(); - if (nativeMapView != null) { - nativeMapView.removeAnnotation(id); - } - annotations.remove(id); - } - - void removeAnnotation(long id) { - if (nativeMapView != null) { - nativeMapView.removeAnnotation(id); - } - annotations.remove(id); + annotations.removeBy(annotation); } void removeAnnotations(@NonNull List<? extends Annotation> annotationList) { - int count = annotationList.size(); - long[] ids = new long[count]; - for (int i = 0; i < count; i++) { - Annotation annotation = annotationList.get(i); + for (Annotation annotation : annotationList) { if (annotation instanceof Marker) { Marker marker = (Marker) annotation; marker.hideInfoWindow(); @@ -142,48 +140,39 @@ class AnnotationManager { if (marker instanceof MarkerView) { markerViewManager.removeMarkerView((MarkerView) marker); + } else { + iconManager.iconCleanup(marker.getIcon()); } } else { // instanceOf Polygon/Polyline shapeAnnotationIds.remove(annotation.getId()); } - ids[i] = annotationList.get(i).getId(); - } - - if (nativeMapView != null) { - nativeMapView.removeAnnotations(ids); - } - - for (long id : ids) { - annotations.remove(id); } + annotations.removeBy(annotationList); } void removeAnnotations() { Annotation annotation; - int count = annotations.size(); + int count = annotationsArray.size(); long[] ids = new long[count]; selectedMarkers.clear(); for (int i = 0; i < count; i++) { - ids[i] = annotations.keyAt(i); - annotation = annotations.get(ids[i]); + ids[i] = annotationsArray.keyAt(i); + annotation = annotationsArray.get(ids[i]); if (annotation instanceof Marker) { Marker marker = (Marker) annotation; marker.hideInfoWindow(); if (marker instanceof MarkerView) { markerViewManager.removeMarkerView((MarkerView) marker); + } else { + iconManager.iconCleanup(marker.getIcon()); } } else { // instanceOf Polygon/Polyline shapeAnnotationIds.remove(annotation.getId()); } } - - if (nativeMapView != null) { - nativeMapView.removeAnnotations(ids); - } - - annotations.clear(); + annotations.removeAll(); } // @@ -191,134 +180,109 @@ class AnnotationManager { // 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; + return markers.addBy(markerOptions, mapboxMap); } 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); - } - - if (markers.size() > 0) { - long[] ids; - if (nativeMapView != null) { - ids = nativeMapView.addMarkers(markers); - } else { - ids = new long[markers.size()]; - } - - long id; - Marker m; - for (int i = 0; i < ids.length; i++) { - m = markers.get(i); - m.setMapboxMap(mapboxMap); - id = ids[i]; - m.setId(id); - annotations.put(id, m); - } + return markers.addBy(markerOptionsList, mapboxMap); + } - } + void updateMarker(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap) { + if (!isAddedToMap(updatedMarker)) { + logNonAdded(updatedMarker); + return; } - return markers; + markers.update(updatedMarker, mapboxMap); + } + + List<Marker> getMarkers() { + return markers.obtainAll(); } - private Marker prepareMarker(BaseMarkerOptions markerOptions) { - Marker marker = markerOptions.getMarker(); - Icon icon = iconManager.loadIconForMarker(marker); - marker.setTopOffsetPixels(iconManager.getTopOffsetPixelsForIcon(icon)); - return marker; + @NonNull + List<Marker> getMarkersInRect(@NonNull RectF rectangle) { + return markers.obtainAllIn(rectangle); } 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; + return markers.addViewBy(markerOptions, mapboxMap, onMarkerViewAddedListener); } 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); - } - // 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; + return markers.addViewsBy(markerViewOptions, mapboxMap); } - private MarkerView prepareViewMarker(BaseMarkerViewOptions markerViewOptions) { - MarkerView marker = markerViewOptions.getMarker(); - iconManager.loadIconForMarkerView(marker); - return marker; + List<MarkerView> getMarkerViewsInRect(@NonNull RectF rectangle) { + return markers.obtainViewsIn(rectangle); } - void updateMarker(@NonNull Marker updatedMarker) { - if (!isAddedToMap(updatedMarker)) { - Timber.w("Attempting to update non-added Marker with value %s", updatedMarker); + void reloadMarkers() { + markers.reload(); + } + + // + // Polygons + // + + Polygon addPolygon(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) { + Polygon polygon = polygons.addBy(polygonOptions, mapboxMap); + shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polygon.getId()); + return polygon; + } + + List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap) { + List<Polygon> polygonList = polygons.addBy(polygonOptionsList, mapboxMap); + for (Polygon polygon : polygonList) { + shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polygon.getId()); + } + return polygonList; + } + + void updatePolygon(Polygon polygon) { + if (!isAddedToMap(polygon)) { + logNonAdded(polygon); return; } - ensureIconLoaded(updatedMarker); - nativeMapView.updateMarker(updatedMarker); - annotations.setValueAt(annotations.indexOfKey(updatedMarker.getId()), updatedMarker); + polygons.update(polygon); } - private boolean isAddedToMap(Annotation annotation) { - return annotation != null && annotation.getId() != -1 && annotations.indexOfKey(annotation.getId()) > -1; + List<Polygon> getPolygons() { + return polygons.obtainAll(); + } + + // + // Polylines + // + + Polyline addPolyline(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) { + Polyline polyline = polylines.addBy(polylineOptions, mapboxMap); + shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polyline.getId()); + return polyline; } - private void ensureIconLoaded(Marker marker) { - if (!(marker instanceof MarkerView)) { - iconManager.ensureIconLoaded(marker, mapboxMap); + List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap) { + List<Polyline> polylineList = polylines.addBy(polylineOptionsList, mapboxMap); + for (Polyline polyline : polylineList) { + shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polyline.getId()); } + return polylineList; } - List<Marker> getMarkers() { - List<Marker> markers = new ArrayList<>(); - Annotation annotation; - for (int i = 0; i < annotations.size(); i++) { - annotation = annotations.get(annotations.keyAt(i)); - if (annotation instanceof Marker) { - markers.add((Marker) annotation); - } + void updatePolyline(Polyline polyline) { + if (!isAddedToMap(polyline)) { + logNonAdded(polyline); + return; } - return markers; + polylines.update(polyline); } + List<Polyline> getPolylines() { + return polylines.obtainAll(); + } + + // TODO Refactor from here still in progress void setOnMarkerClickListener(@Nullable MapboxMap.OnMarkerClickListener listener) { onMarkerClickListener = listener; } @@ -393,205 +357,6 @@ class AnnotationManager { return selectedMarkers; } - @NonNull - List<Marker> getMarkersInRect(@NonNull RectF rectangle) { - // convert Rectangle to be density dependent - 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); - - 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); - } - } - - return 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); - - long[] ids = nativeMapView.queryPointAnnotations(rect); - - 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); - } - } - - return new ArrayList<>(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); - shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + id); - 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(); - if (!polygon.getPoints().isEmpty()) { - polygons.add(polygon); - } - } - - long[] ids; - if (nativeMapView != null) { - ids = nativeMapView.addPolygons(polygons); - } else { - ids = new long[polygons.size()]; - } - - long id; - for (int i = 0; i < ids.length; i++) { - polygon = polygons.get(i); - polygon.setMapboxMap(mapboxMap); - id = ids[i]; - polygon.setId(id); - shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + id); - annotations.put(id, polygon); - } - } - return polygons; - } - - void updatePolygon(@NonNull Polygon polygon) { - if (!isAddedToMap(polygon)) { - Timber.w("Attempting to update non-added Polygon with value %s", polygon); - return; - } - - nativeMapView.updatePolygon(polygon); - annotations.setValueAt(annotations.indexOfKey(polygon.getId()), polygon); - } - - List<Polygon> getPolygons() { - List<Polygon> polygons = new ArrayList<>(); - Annotation annotation; - for (int i = 0; i < annotations.size(); i++) { - annotation = annotations.get(annotations.keyAt(i)); - if (annotation instanceof Polygon) { - polygons.add((Polygon) annotation); - } - } - return polygons; - } - - // - // 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); - shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + 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(); - if (!polyline.getPoints().isEmpty()) { - polylines.add(polyline); - } - } - - long[] ids; - if (nativeMapView != null) { - ids = nativeMapView.addPolylines(polylines); - } else { - ids = new long[polylines.size()]; - } - - long id; - Polyline p; - for (int i = 0; i < ids.length; i++) { - p = polylines.get(i); - p.setMapboxMap(mapboxMap); - id = ids[i]; - p.setId(id); - shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + id); - annotations.put(id, p); - } - } - return polylines; - } - - void updatePolyline(@NonNull Polyline polyline) { - if (!isAddedToMap(polyline)) { - Timber.w("Attempting to update non-added Polyline with value %s", polyline); - return; - } - - nativeMapView.updatePolyline(polyline); - annotations.setValueAt(annotations.indexOfKey(polyline.getId()), polyline); - } - - List<Polyline> getPolylines() { - List<Polyline> polylines = new ArrayList<>(); - Annotation annotation; - for (int i = 0; i < annotations.size(); i++) { - annotation = annotations.get(annotations.keyAt(i)); - if (annotation instanceof Polyline) { - polylines.add((Polyline) annotation); - } - } - return polylines; - } - InfoWindowManager getInfoWindowManager() { return infoWindowManager; } @@ -601,9 +366,9 @@ class AnnotationManager { } void adjustTopOffsetPixels(MapboxMap mapboxMap) { - int count = annotations.size(); + int count = annotationsArray.size(); for (int i = 0; i < count; i++) { - Annotation annotation = annotations.get(i); + Annotation annotation = annotationsArray.get(i); if (annotation instanceof Marker) { Marker marker = (Marker) annotation; marker.setTopOffsetPixels( @@ -619,18 +384,12 @@ 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); - } - } + private boolean isAddedToMap(Annotation annotation) { + return annotation != null && annotation.getId() != -1 && annotationsArray.indexOfKey(annotation.getId()) > -1; + } + + private void logNonAdded(Annotation annotation) { + Timber.w("Attempting to update non-added %s with value %s", annotation.getClass().getCanonicalName(), annotation); } // diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java new file mode 100644 index 0000000000..ae41cbb0cb --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java @@ -0,0 +1,25 @@ +package com.mapbox.mapboxsdk.maps; + + +import android.support.annotation.NonNull; + +import com.mapbox.mapboxsdk.annotations.Annotation; + +import java.util.List; + +/** + * Interface that defines convenient methods for working with a {@link Annotation}'s collection. + */ +interface Annotations { + Annotation obtainBy(long id); + + List<Annotation> obtainAll(); + + void removeBy(long id); + + void removeBy(@NonNull Annotation annotation); + + void removeBy(@NonNull List<? extends Annotation> annotationList); + + void removeAll(); +} 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 18eecfd9c3..b1d6df2103 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 @@ -7,10 +7,10 @@ import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.IconFactory; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.MarkerView; -import com.mapbox.mapboxsdk.exceptions.IconBitmapChangedException; -import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Responsible for managing icons added to the Map. @@ -25,15 +25,14 @@ import java.util.List; */ class IconManager { - private NativeMapView nativeMapView; - private List<Icon> icons; + private final Map<Icon, Integer> iconMap = new HashMap<>(); + private NativeMapView nativeMapView; private int highestIconWidth; private int highestIconHeight; IconManager(NativeMapView nativeMapView) { this.nativeMapView = nativeMapView; - this.icons = new ArrayList<>(); // load transparent icon for MarkerView to trace actual markers, see #6352 loadIcon(IconFactory.recreate(IconFactory.ICON_MARKERVIEW_ID, IconFactory.ICON_MARKERVIEW_BITMAP)); } @@ -83,13 +82,13 @@ class IconManager { } private void addIcon(Icon icon, boolean addIconToMap) { - if (!icons.contains(icon)) { - icons.add(icon); + if (!iconMap.keySet().contains(icon)) { + iconMap.put(icon, 1); if (addIconToMap) { loadIcon(icon); } } else { - validateIconChanged(icon); + iconMap.put(icon, iconMap.get(icon) + 1); } } @@ -121,18 +120,11 @@ class IconManager { } void reloadIcons() { - for (Icon icon : icons) { + for (Icon icon : iconMap.keySet()) { loadIcon(icon); } } - private void validateIconChanged(Icon icon) { - Icon oldIcon = icons.get(icons.indexOf(icon)); - if (!oldIcon.getBitmap().sameAs(icon.getBitmap())) { - throw new IconBitmapChangedException(); - } - } - void ensureIconLoaded(Marker marker, MapboxMap mapboxMap) { Icon icon = marker.getIcon(); if (icon == null) { @@ -149,4 +141,23 @@ class IconManager { marker.setTopOffsetPixels(getTopOffsetPixelsForIcon(icon)); } } + + void iconCleanup(Icon icon) { + int refCounter = iconMap.get(icon) - 1; + if (refCounter == 0) { + remove(icon); + } else { + updateIconRefCounter(icon, refCounter); + } + } + + private void remove(Icon icon) { + nativeMapView.removeAnnotationIcon(icon.getId()); + iconMap.remove(icon); + } + + private void updateIconRefCounter(Icon icon, int refCounter) { + iconMap.put(icon, refCounter); + } + } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java index 11100d6f17..af207204d9 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java @@ -21,7 +21,8 @@ import java.util.List; */ class InfoWindowManager { - private List<InfoWindow> infoWindows; + private final List<InfoWindow> infoWindows = new ArrayList<>(); + private MapboxMap.InfoWindowAdapter infoWindowAdapter; private boolean allowConcurrentMultipleInfoWindows; @@ -29,13 +30,11 @@ class InfoWindowManager { private MapboxMap.OnInfoWindowLongClickListener onInfoWindowLongClickListener; private MapboxMap.OnInfoWindowCloseListener onInfoWindowCloseListener; - InfoWindowManager() { - this.infoWindows = new ArrayList<>(); - } - void update() { - for (InfoWindow infoWindow : infoWindows) { - infoWindow.update(); + if (!infoWindows.isEmpty()) { + for (InfoWindow infoWindow : infoWindows) { + infoWindow.update(); + } } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java index 8b1ba7b771..01c6da4971 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java @@ -76,6 +76,9 @@ public final class MapFragment extends Fragment { public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); map.onCreate(savedInstanceState); + if (onMapReadyCallback != null) { + map.getMapAsync(onMapReadyCallback); + } } /** @@ -85,7 +88,6 @@ public final class MapFragment extends Fragment { public void onStart() { super.onStart(); map.onStart(); - map.getMapAsync(onMapReadyCallback); } /** @@ -150,6 +152,10 @@ public final class MapFragment extends Fragment { * @param onMapReadyCallback The callback to be invoked. */ public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) { - this.onMapReadyCallback = onMapReadyCallback; + if (map == null) { + this.onMapReadyCallback = onMapReadyCallback; + } else { + map.getMapAsync(onMapReadyCallback); + } } } 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 bff5e9bed2..2394e52193 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 @@ -693,4 +693,4 @@ final class MapGestureDetector { void setOnScrollListener(MapboxMap.OnScrollListener onScrollListener) { this.onScrollListener = onScrollListener; } -}
\ No newline at end of file +} 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 9fa3126523..12e4c675ee 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 @@ -1,34 +1,34 @@ package com.mapbox.mapboxsdk.maps; import android.content.Context; -import android.graphics.Canvas; import android.graphics.PointF; -import android.graphics.SurfaceTexture; +import android.os.Build; +import android.opengl.GLSurfaceView; import android.os.Bundle; -import android.os.Handler; import android.support.annotation.CallSuper; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.UiThread; +import android.support.v4.util.LongSparseArray; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; -import android.view.Surface; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.TextureView; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ZoomButtonsController; import com.mapbox.mapboxsdk.R; +import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.MarkerViewManager; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.constants.Style; +import com.mapbox.mapboxsdk.egl.EGLConfigChooser; +import com.mapbox.mapboxsdk.maps.renderer.MapRenderer; import com.mapbox.mapboxsdk.maps.widgets.CompassView; import com.mapbox.mapboxsdk.maps.widgets.MyLocationView; import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; @@ -40,6 +40,16 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION; +import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE; +import static android.opengl.GLSurfaceView.RENDERMODE_WHEN_DIRTY; /** * <p> @@ -57,17 +67,26 @@ import java.util.List; */ public class MapView extends FrameLayout { + private final MapCallback mapCallback = new MapCallback(); + private MapboxMap mapboxMap; + private NativeMapView nativeMapView; - private boolean textureMode; + private MapboxMapOptions mapboxMapOptions; private boolean destroyed; - private boolean hasSurface; - private MapboxMap mapboxMap; - private MapCallback mapCallback; + private MyLocationView myLocationView; + private CompassView compassView; + private PointF focalPoint; + private ImageView attrView; + private ImageView logoView; private MapGestureDetector mapGestureDetector; private MapKeyListener mapKeyListener; private MapZoomButtonController mapZoomButtonController; + private Bundle savedInstanceState; + private final CopyOnWriteArrayList<OnMapChangedListener> onMapChangedListeners = new CopyOnWriteArrayList<>(); + + private GLSurfaceView glSurfaceView; @UiThread public MapView(@NonNull Context context) { @@ -98,24 +117,39 @@ public class MapView extends FrameLayout { // in IDE layout editor, just return return; } - - // determine render surface - textureMode = options.getTextureMode(); + mapboxMapOptions = options; // inflate view View view = LayoutInflater.from(context).inflate(R.layout.mapbox_mapview_internal, this); - CompassView compassView = (CompassView) view.findViewById(R.id.compassView); - MyLocationView myLocationView = (MyLocationView) view.findViewById(R.id.userLocationView); - ImageView attrView = (ImageView) view.findViewById(R.id.attributionView); + compassView = (CompassView) view.findViewById(R.id.compassView); + myLocationView = (MyLocationView) view.findViewById(R.id.userLocationView); + attrView = (ImageView) view.findViewById(R.id.attributionView); + logoView = (ImageView) view.findViewById(R.id.logoView); // add accessibility support setContentDescription(context.getString(R.string.mapbox_mapActionDescription)); + setWillNotDraw(false); + + getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + getViewTreeObserver().removeOnGlobalLayoutListener(this); + } else { + getViewTreeObserver().removeGlobalOnLayoutListener(this); + } + initialiseDrawingSurface(); + } + }); + } - // create native Map object - nativeMapView = new NativeMapView(this); + private void initialiseMap() { + Context context = getContext(); + addOnMapChangedListener(mapCallback); // callback for focal point invalidation - FocalPointInvalidator focalPoint = new FocalPointInvalidator(compassView); + final FocalPointInvalidator focalPointInvalidator = new FocalPointInvalidator(); + focalPointInvalidator.addListener(createFocalPointChangeListener()); // callback for registering touch listeners RegisterTouchListener registerTouchListener = new RegisterTouchListener(); @@ -124,30 +158,45 @@ public class MapView extends FrameLayout { CameraZoomInvalidator zoomInvalidator = new CameraZoomInvalidator(); // callback for camera change events - CameraChangeDispatcher cameraChangeDispatcher = new CameraChangeDispatcher(); + final CameraChangeDispatcher cameraChangeDispatcher = new CameraChangeDispatcher(); // setup components for MapboxMap creation Projection proj = new Projection(nativeMapView); - UiSettings uiSettings = new UiSettings(proj, focalPoint, compassView, attrView, view.findViewById(R.id.logoView)); - TrackingSettings trackingSettings = new TrackingSettings(myLocationView, uiSettings, focalPoint, zoomInvalidator); - MyLocationViewSettings myLocationViewSettings = new MyLocationViewSettings(myLocationView, proj, focalPoint); + UiSettings uiSettings = new UiSettings(proj, focalPointInvalidator, compassView, attrView, logoView); + TrackingSettings trackingSettings = new TrackingSettings(myLocationView, uiSettings, focalPointInvalidator, + zoomInvalidator); + MyLocationViewSettings myLocationViewSettings = new MyLocationViewSettings(myLocationView, proj, + focalPointInvalidator); + LongSparseArray<Annotation> annotationsArray = new LongSparseArray<>(); MarkerViewManager markerViewManager = new MarkerViewManager((ViewGroup) findViewById(R.id.markerViewContainer)); - AnnotationManager annotations = new AnnotationManager(nativeMapView, this, markerViewManager); - Transform transform = new Transform(nativeMapView, annotations.getMarkerViewManager(), trackingSettings, + IconManager iconManager = new IconManager(nativeMapView); + Annotations annotations = new AnnotationContainer(nativeMapView, annotationsArray); + Markers markers = new MarkerContainer(nativeMapView, this, annotationsArray, iconManager, markerViewManager); + Polygons polygons = new PolygonContainer(nativeMapView, annotationsArray); + Polylines polylines = new PolylineContainer(nativeMapView, annotationsArray); + AnnotationManager annotationManager = new AnnotationManager(nativeMapView, this, annotationsArray, + markerViewManager, iconManager, annotations, markers, polygons, polylines); + Transform transform = new Transform(nativeMapView, annotationManager.getMarkerViewManager(), trackingSettings, cameraChangeDispatcher); + mapboxMap = new MapboxMap(nativeMapView, transform, uiSettings, trackingSettings, myLocationViewSettings, proj, - registerTouchListener, annotations, cameraChangeDispatcher); + registerTouchListener, annotationManager, cameraChangeDispatcher); + focalPointInvalidator.addListener(mapboxMap.createFocalPointChangeListener()); + + mapCallback.attachMapboxMap(mapboxMap); // user input - mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings, annotations, - cameraChangeDispatcher); + mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings, + annotationManager, cameraChangeDispatcher); mapKeyListener = new MapKeyListener(transform, trackingSettings, uiSettings); + mapZoomButtonController = new MapZoomButtonController(new ZoomButtonsController(this)); MapZoomControllerListener zoomListener = new MapZoomControllerListener(mapGestureDetector, uiSettings, transform); - mapZoomButtonController = new MapZoomButtonController(this, uiSettings, zoomListener); + mapZoomButtonController.bind(uiSettings, zoomListener); + compassView.injectCompassAnimationListener(createCompassAnimationListener(cameraChangeDispatcher)); + compassView.setOnClickListener(createCompassClickListener(cameraChangeDispatcher)); // inject widgets with MapboxMap - compassView.setMapboxMap(mapboxMap); myLocationView.setMapboxMap(mapboxMap); attrView.setOnClickListener(new AttributionDialogManager(context, mapboxMap)); @@ -158,14 +207,58 @@ public class MapView extends FrameLayout { setFocusableInTouchMode(true); requestDisallowInterceptTouchEvent(true); - // allow onDraw invocation - setWillNotDraw(false); - // notify Map object about current connectivity state nativeMapView.setReachability(ConnectivityReceiver.instance(context).isConnected(context)); // initialise MapboxMap - mapboxMap.initialise(context, options); + if (savedInstanceState == null) { + mapboxMap.initialise(context, mapboxMapOptions); + } else { + mapboxMap.onRestoreInstanceState(savedInstanceState); + } + } + + private FocalPointChangeListener createFocalPointChangeListener() { + return new FocalPointChangeListener() { + @Override + public void onFocalPointChanged(PointF pointF) { + focalPoint = pointF; + } + }; + } + + private MapboxMap.OnCompassAnimationListener createCompassAnimationListener(final CameraChangeDispatcher + cameraChangeDispatcher) { + return new MapboxMap.OnCompassAnimationListener() { + @Override + public void onCompassAnimation() { + cameraChangeDispatcher.onCameraMove(); + } + + @Override + public void onCompassAnimationFinished() { + compassView.isAnimating(false); + cameraChangeDispatcher.onCameraIdle(); + } + }; + } + + private OnClickListener createCompassClickListener(final CameraChangeDispatcher cameraChangeDispatcher) { + return new OnClickListener() { + @Override + public void onClick(View v) { + if (mapboxMap != null && compassView != null) { + 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); + } + cameraChangeDispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_ANIMATION); + compassView.isAnimating(true); + compassView.postDelayed(compassView, TIME_WAIT_IDLE + TIME_MAP_NORTH_ANIMATION); + } + } + }; } // @@ -188,25 +281,40 @@ public class MapView extends FrameLayout { if (savedInstanceState == null) { MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapLoadEvent()); } else if (savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) { - mapboxMap.onRestoreInstanceState(savedInstanceState); + this.savedInstanceState = savedInstanceState; } - - initialiseDrawingSurface(textureMode); - addOnMapChangedListener(mapCallback = new MapCallback(mapboxMap)); } - private void initialiseDrawingSurface(boolean textureMode) { - nativeMapView.initializeDisplay(); - nativeMapView.initializeContext(); - if (textureMode) { - TextureView textureView = new TextureView(getContext()); - textureView.setSurfaceTextureListener(new SurfaceTextureListener()); - addView(textureView, 0); - } else { - SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView); - surfaceView.getHolder().addCallback(new SurfaceCallback()); - surfaceView.setVisibility(View.VISIBLE); - } + private void initialiseDrawingSurface() { + glSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceView); + glSurfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop()); + glSurfaceView.setEGLContextClientVersion(2); + glSurfaceView.setEGLConfigChooser(new EGLConfigChooser()); + + MapRenderer mapRenderer = new MapRenderer(getContext(), glSurfaceView) { + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + MapView.this.post(new Runnable() { + @Override + public void run() { + // Initialise only once + if (mapboxMap == null) { + initialiseMap(); + mapboxMap.onStart(); + } + } + }); + + super.onSurfaceCreated(gl, config); + } + }; + + glSurfaceView.setRenderer(mapRenderer); + glSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY); + glSurfaceView.setVisibility(View.VISIBLE); + + nativeMapView = new NativeMapView(this, mapRenderer); + nativeMapView.resizeView(getMeasuredWidth(), getMeasuredHeight()); } /** @@ -226,8 +334,10 @@ public class MapView extends FrameLayout { */ @UiThread public void onStart() { - mapboxMap.onStart(); ConnectivityReceiver.instance(getContext()).activate(); + if (mapboxMap != null) { + mapboxMap.onStart(); + } } /** @@ -235,7 +345,9 @@ public class MapView extends FrameLayout { */ @UiThread public void onResume() { - // replaced by onStart in v5.0.0 + if (glSurfaceView != null) { + glSurfaceView.onResume(); + } } /** @@ -243,7 +355,9 @@ public class MapView extends FrameLayout { */ @UiThread public void onPause() { - // replaced by onStop in v5.0.0 + if (glSurfaceView != null) { + glSurfaceView.onPause(); + } } /** @@ -261,9 +375,6 @@ public class MapView extends FrameLayout { @UiThread public void onDestroy() { destroyed = true; - nativeMapView.terminateContext(); - nativeMapView.terminateDisplay(); - nativeMapView.destroySurface(); mapCallback.clearOnMapReadyCallbacks(); nativeMapView.destroy(); nativeMapView = null; @@ -328,21 +439,6 @@ public class MapView extends FrameLayout { nativeMapView.onLowMemory(); } - // Called when debug mode is enabled to update a FPS counter - // Called via JNI from NativeMapView - // Forward to any listener - protected void onFpsChanged(final double fps) { - final MapboxMap.OnFpsChangedListener listener = mapboxMap.getOnFpsChangedListener(); - if (listener != null) { - post(new Runnable() { - @Override - public void run() { - listener.onFpsChanged(fps); - } - }); - } - } - /** * <p> * Loads a new map style from the specified URL. @@ -375,7 +471,10 @@ public class MapView extends FrameLayout { if (destroyed) { return; } - + if (nativeMapView == null) { + mapboxMapOptions.styleUrl(url); + return; + } nativeMapView.setStyleUrl(url); } @@ -383,119 +482,17 @@ public class MapView extends FrameLayout { // Rendering // - // Called when the map needs to be rerendered - // Called via JNI from NativeMapView - protected void onInvalidate() { - postInvalidate(); - } - - @Override - public void onDraw(Canvas canvas) { - super.onDraw(canvas); - if (isInEditMode()) { - return; - } - - if (destroyed) { - return; - } - - if (!hasSurface) { - return; - } - - nativeMapView.render(); - } - @Override protected void onSizeChanged(int width, int height, int oldw, int oldh) { if (destroyed) { return; } - if (!isInEditMode()) { + if (!isInEditMode() && nativeMapView != null) { nativeMapView.resizeView(width, height); } } - private class SurfaceCallback implements SurfaceHolder.Callback { - - private Surface surface; - - @Override - public void surfaceCreated(SurfaceHolder holder) { - nativeMapView.createSurface(surface = holder.getSurface()); - hasSurface = true; - } - - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - if (destroyed) { - return; - } - nativeMapView.resizeFramebuffer(width, height); - } - - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - hasSurface = false; - - if (nativeMapView != null) { - nativeMapView.destroySurface(); - } - surface.release(); - } - } - - // This class handles TextureView callbacks - private class SurfaceTextureListener implements TextureView.SurfaceTextureListener { - - private Surface surface; - - // Called when the native surface texture has been created - // Must do all EGL/GL ES initialization here - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - nativeMapView.createSurface(this.surface = new Surface(surface)); - nativeMapView.resizeFramebuffer(width, height); - hasSurface = true; - } - - // Called when the native surface texture has been destroyed - // Must do all EGL/GL ES destruction here - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - hasSurface = false; - - if (nativeMapView != null) { - nativeMapView.destroySurface(); - } - this.surface.release(); - return true; - } - - // Called when the format or size of the native surface texture has been changed - // Must handle window resizing here. - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { - if (destroyed) { - return; - } - - nativeMapView.resizeFramebuffer(width, height); - } - - // Called when the SurfaceTexure frame is drawn to screen - // Must sync with UI here - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) { - if (destroyed) { - return; - } - mapboxMap.onUpdateRegionChange(); - } - } - // // View events // @@ -505,26 +502,37 @@ public class MapView extends FrameLayout { @CallSuper protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (mapZoomButtonController != null) { - mapZoomButtonController.setVisible(false); - } + mapZoomButtonController.setVisible(false); } // Called when view is hidden and shown @Override protected void onVisibilityChanged(@NonNull View changedView, int visibility) { - if (isInEditMode() || mapZoomButtonController == null) { + if (isInEditMode()) { return; } - mapZoomButtonController.setVisible(visibility == View.VISIBLE); + + if (mapZoomButtonController != null) { + mapZoomButtonController.setVisible(visibility == View.VISIBLE); + } } // // Map events // + void onMapChange(int rawChange) { + for (MapView.OnMapChangedListener onMapChangedListener : onMapChangedListeners) { + try { + onMapChangedListener.onMapChanged(rawChange); + } catch (RuntimeException err) { + Timber.e(err, "Exception in MapView.OnMapChangedListener"); + } + } + } + /** - * <p>P + * <p> * Add a callback that's invoked when the displayed map view changes. * </p> * To remove the callback, use {@link MapView#removeOnMapChangedListener(OnMapChangedListener)}. @@ -534,7 +542,7 @@ public class MapView extends FrameLayout { */ public void addOnMapChangedListener(@Nullable OnMapChangedListener listener) { if (listener != null) { - nativeMapView.addOnMapChangedListener(listener); + onMapChangedListeners.add(listener); } } @@ -546,7 +554,7 @@ public class MapView extends FrameLayout { */ public void removeOnMapChangedListener(@Nullable OnMapChangedListener listener) { if (listener != null) { - nativeMapView.removeOnMapChangedListener(listener); + onMapChangedListeners.remove(listener); } } @@ -813,10 +821,10 @@ public class MapView extends FrameLayout { private class FocalPointInvalidator implements FocalPointChangeListener { - private final FocalPointChangeListener[] focalPointChangeListeners; + private final List<FocalPointChangeListener> focalPointChangeListeners = new ArrayList<>(); - FocalPointInvalidator(FocalPointChangeListener... listeners) { - focalPointChangeListeners = listeners; + void addListener(FocalPointChangeListener focalPointChangeListener) { + focalPointChangeListeners.add(focalPointChangeListener); } @Override @@ -910,11 +918,11 @@ public class MapView extends FrameLayout { private static class MapCallback implements OnMapChangedListener { - private final MapboxMap mapboxMap; + private MapboxMap mapboxMap; private final List<OnMapReadyCallback> onMapReadyCallbackList = new ArrayList<>(); private boolean initialLoad = true; - MapCallback(MapboxMap mapboxMap) { + void attachMapboxMap(MapboxMap mapboxMap) { this.mapboxMap = mapboxMap; } @@ -922,14 +930,9 @@ public class MapView extends FrameLayout { public void onMapChanged(@MapChange int change) { if (change == DID_FINISH_LOADING_STYLE && initialLoad) { initialLoad = false; - new Handler().post(new Runnable() { - @Override - public void run() { - mapboxMap.onPreMapReady(); - onMapReady(); - mapboxMap.onPostMapReady(); - } - }); + mapboxMap.onPreMapReady(); + onMapReady(); + mapboxMap.onPostMapReady(); } else if (change == DID_FINISH_RENDERING_FRAME || change == DID_FINISH_RENDERING_FRAME_FULLY_RENDERED) { mapboxMap.onUpdateFullyRendered(); } else if (change == REGION_IS_CHANGING || change == REGION_DID_CHANGE || change == DID_FINISH_LOADING_MAP) { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java index 16513904c5..018c8eb5bb 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java @@ -1,7 +1,6 @@ package com.mapbox.mapboxsdk.maps; import android.support.annotation.NonNull; -import android.view.View; import android.widget.ZoomButtonsController; import com.mapbox.mapboxsdk.constants.MapboxConstants; @@ -12,21 +11,25 @@ import com.mapbox.mapboxsdk.constants.MapboxConstants; * Allows single touch only devices to zoom in and out. * </p> */ -final class MapZoomButtonController extends ZoomButtonsController { +final class MapZoomButtonController { private UiSettings uiSettings; + private ZoomButtonsController zoomButtonsController; - MapZoomButtonController(@NonNull View ownerView, @NonNull UiSettings uiSettings, @NonNull OnZoomListener listener) { - super(ownerView); + MapZoomButtonController(@NonNull ZoomButtonsController zoomButtonsController) { + this.zoomButtonsController = zoomButtonsController; + this.zoomButtonsController.setZoomSpeed(MapboxConstants.ANIMATION_DURATION); + } + + void bind(UiSettings uiSettings, ZoomButtonsController.OnZoomListener onZoomListener) { this.uiSettings = uiSettings; - setZoomSpeed(MapboxConstants.ANIMATION_DURATION); - setOnZoomListener(listener); + zoomButtonsController.setOnZoomListener(onZoomListener); } - @Override - public void setVisible(boolean visible) { - if (uiSettings.isZoomControlsEnabled()) { - super.setVisible(visible); + void setVisible(boolean visible) { + if (uiSettings != null && !uiSettings.isZoomControlsEnabled()) { + return; } + zoomButtonsController.setVisible(visible); } } 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 e074d938fd..0c820d844c 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 @@ -59,6 +59,7 @@ import timber.log.Timber; * Note: Similar to a View object, a MapboxMap should only be read and modified from the main thread. * </p> */ +@UiThread public final class MapboxMap { private final NativeMapView nativeMapView; @@ -74,6 +75,7 @@ public final class MapboxMap { private final OnRegisterTouchListener onRegisterTouchListener; private MapboxMap.OnFpsChangedListener onFpsChangedListener; + private PointF focalPoint; MapboxMap(NativeMapView map, Transform transform, UiSettings ui, TrackingSettings tracking, MyLocationViewSettings myLocationView, Projection projection, OnRegisterTouchListener listener, @@ -99,6 +101,7 @@ public final class MapboxMap { setDebugActive(options.getDebugActive()); setApiBaseUrl(options); setStyleUrl(options); + setPrefetchesTiles(options); } /** @@ -209,7 +212,6 @@ public final class MapboxMap { * * @return Duration in milliseconds */ - @UiThread public long getTransitionDuration() { return nativeMapView.getTransitionDuration(); } @@ -219,7 +221,6 @@ public final class MapboxMap { * * @param durationMs Duration in milliseconds */ - @UiThread public void setTransitionDuration(long durationMs) { nativeMapView.setTransitionDuration(durationMs); } @@ -232,7 +233,6 @@ public final class MapboxMap { * * @return Delay in milliseconds */ - @UiThread public long getTransitionDelay() { return nativeMapView.getTransitionDelay(); } @@ -242,17 +242,44 @@ public final class MapboxMap { * * @param delayMs Delay in milliseconds */ - @UiThread public void setTransitionDelay(long delayMs) { nativeMapView.setTransitionDelay(delayMs); } /** + * Sets tile pre-fetching from MapboxOptions. + * + * @param options the options object + */ + private void setPrefetchesTiles(@NonNull MapboxMapOptions options) { + setPrefetchesTiles(options.getPrefetchesTiles()); + } + + /** + * Enable or disable tile pre-fetching. Pre-fetching makes sure that a low-resolution + * tile is rendered as soon as possible at the expense of a little bandwidth. + * + * @param enable true to enable + */ + public void setPrefetchesTiles(boolean enable) { + nativeMapView.setPrefetchesTiles(enable); + } + + /** + * Check whether tile pre-fetching is enabled or not. + * + * @return true if enabled + * @see MapboxMap#setPrefetchesTiles(boolean) + */ + public boolean getPrefetchesTiles() { + return nativeMapView.getPrefetchesTiles(); + } + + /** * Retrieve all the layers in the style * * @return all the layers in the current style */ - @UiThread public List<Layer> getLayers() { return nativeMapView.getLayers(); } @@ -264,7 +291,6 @@ public final class MapboxMap { * @return the layer, if present in the style */ @Nullable - @UiThread public Layer getLayer(@NonNull String layerId) { return nativeMapView.getLayer(layerId); } @@ -277,13 +303,12 @@ public final class MapboxMap { * @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)); + Timber.e(exception, "Layer: %s is a different type: ", layerId); return null; } } @@ -293,7 +318,6 @@ public final class MapboxMap { * * @param layer the layer to add */ - @UiThread public void addLayer(@NonNull Layer layer) { nativeMapView.addLayer(layer); } @@ -304,7 +328,6 @@ public final class MapboxMap { * @param layer the layer to add * @param below the layer id to add this layer before */ - @UiThread public void addLayerBelow(@NonNull Layer layer, @NonNull String below) { nativeMapView.addLayerBelow(layer, below); } @@ -315,7 +338,6 @@ public final class MapboxMap { * @param layer the layer to add * @param above the layer id to add this layer above */ - @UiThread public void addLayerAbove(@NonNull Layer layer, @NonNull String above) { nativeMapView.addLayerAbove(layer, above); } @@ -327,7 +349,6 @@ public final class MapboxMap { * @param layer the layer to add * @param index the index to insert the layer at */ - @UiThread public void addLayerAt(@NonNull Layer layer, @IntRange(from = 0) int index) { nativeMapView.addLayerAt(layer, index); } @@ -338,7 +359,6 @@ public final class MapboxMap { * @param layerId the layer to remove * @return the removed layer or null if not found */ - @UiThread @Nullable public Layer removeLayer(@NonNull String layerId) { return nativeMapView.removeLayer(layerId); @@ -350,7 +370,6 @@ public final class MapboxMap { * @param layer the layer to remove * @return the layer */ - @UiThread @Nullable public Layer removeLayer(@NonNull Layer layer) { return nativeMapView.removeLayer(layer); @@ -362,7 +381,6 @@ public final class MapboxMap { * @param index the layer index * @return the removed layer or null if not found */ - @UiThread @Nullable public Layer removeLayerAt(@IntRange(from = 0) int index) { return nativeMapView.removeLayerAt(index); @@ -373,7 +391,6 @@ public final class MapboxMap { * * @return all the sources in the current style */ - @UiThread public List<Source> getSources() { return nativeMapView.getSources(); } @@ -385,7 +402,6 @@ public final class MapboxMap { * @return the source if present in the current style */ @Nullable - @UiThread public Source getSource(@NonNull String sourceId) { return nativeMapView.getSource(sourceId); } @@ -398,13 +414,12 @@ public final class MapboxMap { * @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)); + Timber.e(exception, "Source: %s is a different type: ", sourceId); return null; } } @@ -414,7 +429,6 @@ public final class MapboxMap { * * @param source the source to add */ - @UiThread public void addSource(@NonNull Source source) { nativeMapView.addSource(source); } @@ -425,7 +439,6 @@ public final class MapboxMap { * @param sourceId the source to remove * @return the source handle or null if the source was not present */ - @UiThread @Nullable public Source removeSource(@NonNull String sourceId) { return nativeMapView.removeSource(sourceId); @@ -437,7 +450,6 @@ public final class MapboxMap { * @param source the source to remove * @return the source */ - @UiThread @Nullable public Source removeSource(@NonNull Source source) { return nativeMapView.removeSource(source); @@ -449,7 +461,6 @@ public final class MapboxMap { * @param name the name of the image * @param image the pre-multiplied Bitmap */ - @UiThread public void addImage(@NonNull String name, @NonNull Bitmap image) { nativeMapView.addImage(name, image); } @@ -459,11 +470,14 @@ public final class MapboxMap { * * @param name the name of the image to remove */ - @UiThread public void removeImage(String name) { nativeMapView.removeImage(name); } + public Bitmap getImage(@NonNull String name) { + return nativeMapView.getImage(name); + } + // // MinZoom // @@ -475,7 +489,6 @@ public final class MapboxMap { * * @param minZoom The new minimum zoom level. */ - @UiThread public void setMinZoomPreference( @FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double minZoom) { transform.setMinZoom(minZoom); @@ -483,12 +496,11 @@ public final class MapboxMap { /** * <p> - * Gets the maximum zoom level the map can be displayed at. + * Gets the minimum zoom level the map can be displayed at. * </p> * * @return The minimum zoom level. */ - @UiThread public double getMinZoomLevel() { return transform.getMinZoom(); } @@ -501,10 +513,12 @@ public final class MapboxMap { * <p> * Sets the maximum zoom level the map can be displayed at. * </p> + * <p> + * The default maximum zoomn level is 22. The upper bound for this value is 25.5. + * </p> * * @param maxZoom The new maximum zoom level. */ - @UiThread public void setMaxZoomPreference(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double maxZoom) { transform.setMaxZoom(maxZoom); @@ -517,7 +531,6 @@ public final class MapboxMap { * * @return The maximum zoom level. */ - @UiThread public double getMaxZoomLevel() { return transform.getMaxZoom(); } @@ -543,7 +556,10 @@ public final class MapboxMap { * Gets the tracking interface settings for the map. * * @return the TrackingSettings asssociated with this map + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ + @Deprecated public TrackingSettings getTrackingSettings() { return trackingSettings; } @@ -556,7 +572,10 @@ public final class MapboxMap { * Gets the settings of the user location for the map. * * @return the MyLocationViewSettings associated with this map + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ + @Deprecated public MyLocationViewSettings getMyLocationViewSettings() { return myLocationViewSettings; } @@ -594,6 +613,47 @@ public final class MapboxMap { // /** + * Moves the center of the screen to a latitude and longitude specified by a LatLng object. This centers the + * camera on the LatLng object. + * + * @param latLng Target location to change to + */ + public void setLatLng(@NonNull LatLng latLng) { + nativeMapView.setLatLng(latLng); + } + + /** + * Moves the camera viewpoint to a particular zoom level. + * + * @param zoom Zoom level to change to + */ + public void setZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double zoom) { + if (focalPoint == null) { + focalPoint = new PointF(nativeMapView.getWidth() / 2, nativeMapView.getHeight() / 2); + } + nativeMapView.setZoom(zoom, focalPoint, 0); + } + + /** + * Moves the camera viewpoint angle to a particular angle in degrees. + * + * @param tilt Tilt angle to change to + */ + public void setTilt(@FloatRange(from = MapboxConstants.MINIMUM_TILT, to = MapboxConstants.MAXIMUM_TILT) double tilt) { + nativeMapView.setPitch(tilt, 0); + } + + /** + * Moves the camera viewpoint direction to a particular angle in degrees. + * + * @param bearing Direction angle to change to + */ + public void setBearing(@FloatRange(from = MapboxConstants.MINIMUM_DIRECTION, to = MapboxConstants.MAXIMUM_DIRECTION) + double bearing) { + nativeMapView.setBearing(bearing); + } + + /** * Cancels ongoing animations. * <p> * This invokes the {@link CancelableCallback} for ongoing camera updates. @@ -632,7 +692,6 @@ public final class MapboxMap { * * @param update The change that should be applied to the camera. */ - @UiThread public final void moveCamera(CameraUpdate update) { moveCamera(update, null); } @@ -645,7 +704,6 @@ public final class MapboxMap { * @param update The change that should be applied to the camera * @param callback the callback to be invoked when an animation finishes or is canceled */ - @UiThread public final void moveCamera(final CameraUpdate update, final MapboxMap.CancelableCallback callback) { new Handler().post(new Runnable() { @Override @@ -666,7 +724,6 @@ public final class MapboxMap { * @param update The change that should be applied to the camera. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - @UiThread public final void easeCamera(CameraUpdate update) { easeCamera(update, MapboxConstants.ANIMATION_DURATION); } @@ -681,7 +738,6 @@ public final class MapboxMap { * positive, otherwise an IllegalArgumentException will be thrown. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - @UiThread public final void easeCamera(CameraUpdate update, int durationMs) { easeCamera(update, durationMs, null); } @@ -705,7 +761,6 @@ public final class MapboxMap { * Do not update or ease the camera from within onCancel(). * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - @UiThread public final void easeCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) { easeCamera(update, durationMs, true, callback); } @@ -724,7 +779,6 @@ public final class MapboxMap { * positive, otherwise an IllegalArgumentException will be thrown. * @param easingInterpolator True for easing interpolator, false for linear. */ - @UiThread public final void easeCamera(CameraUpdate update, int durationMs, boolean easingInterpolator) { easeCamera(update, durationMs, easingInterpolator, null); } @@ -750,7 +804,6 @@ public final class MapboxMap { * by a later camera movement or a user gesture, onCancel() will be called. * Do not update or ease the camera from within onCancel(). */ - @UiThread public final void easeCamera(final CameraUpdate update, final int durationMs, final boolean easingInterpolator, final MapboxMap.CancelableCallback callback) { easeCamera(update, durationMs, easingInterpolator, callback, false); @@ -778,7 +831,6 @@ public final class MapboxMap { * Do not update or ease the camera from within onCancel(). * @param isDismissable true will allow animated camera changes dismiss a tracking mode. */ - @UiThread public final void easeCamera(final CameraUpdate update, final int durationMs, final boolean easingInterpolator, final MapboxMap.CancelableCallback callback, final boolean isDismissable) { new Handler().post(new Runnable() { @@ -798,7 +850,6 @@ public final class MapboxMap { * @param update The change that should be applied to the camera. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - @UiThread public final void animateCamera(CameraUpdate update) { animateCamera(update, MapboxConstants.ANIMATION_DURATION, null); } @@ -815,7 +866,6 @@ public final class MapboxMap { * called. Do not update or animate the camera from within onCancel(). * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - @UiThread public final void animateCamera(CameraUpdate update, MapboxMap.CancelableCallback callback) { animateCamera(update, MapboxConstants.ANIMATION_DURATION, callback); } @@ -831,7 +881,6 @@ public final class MapboxMap { * positive, otherwise an IllegalArgumentException will be thrown. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - @UiThread public final void animateCamera(CameraUpdate update, int durationMs) { animateCamera(update, durationMs, null); } @@ -854,7 +903,6 @@ public final class MapboxMap { * isn't required, leave it as null. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - @UiThread public final void animateCamera(final CameraUpdate update, final int durationMs, final MapboxMap.CancelableCallback callback) { new Handler().post(new Runnable() { @@ -925,7 +973,6 @@ public final class MapboxMap { * * @return If true, map debug information is currently shown. */ - @UiThread public boolean isDebugActive() { return nativeMapView.getDebug(); } @@ -938,7 +985,6 @@ public final class MapboxMap { * * @param debugActive If true, map debug information is shown. */ - @UiThread public void setDebugActive(boolean debugActive) { nativeMapView.setDebug(debugActive); } @@ -952,7 +998,6 @@ public final class MapboxMap { * * @see #isDebugActive() */ - @UiThread public void cycleDebugOptions() { nativeMapView.cycleDebugOptions(); } @@ -1002,7 +1047,6 @@ public final class MapboxMap { * @param url The URL of the map style * @see Style */ - @UiThread public void setStyleUrl(@NonNull String url) { setStyleUrl(url, null); } @@ -1035,7 +1079,6 @@ public final class MapboxMap { * @param callback The callback that is invoked when the style has loaded. * @see Style */ - @UiThread public void setStyleUrl(@NonNull final String url, @Nullable final OnStyleLoadedListener callback) { if (callback != null) { nativeMapView.addOnMapChangedListener(new MapView.OnMapChangedListener() { @@ -1067,7 +1110,6 @@ public final class MapboxMap { * @param style The bundled style. * @see Style */ - @UiThread public void setStyle(@Style.StyleUrl String style) { setStyleUrl(style); } @@ -1084,7 +1126,6 @@ public final class MapboxMap { * @param callback The callback to be invoked when the style has finished loading * @see Style */ - @UiThread public void setStyle(@Style.StyleUrl String style, @Nullable OnStyleLoadedListener callback) { setStyleUrl(style, callback); } @@ -1102,16 +1143,36 @@ public final class MapboxMap { } /** - * Returns the map style currently displayed in the map view. + * Returns the map style url currently displayed in the map view. * * @return The URL of the map style */ - @UiThread @Nullable public String getStyleUrl() { return nativeMapView.getStyleUrl(); } + /** + * Loads a new map style from a json string. + * <p> + * If the style fails to load or an invalid style URL is set, the map view will become blank. + * An error message will be logged in the Android logcat and {@link MapView#DID_FAIL_LOADING_MAP} event will be + * sent. + * </p> + */ + public void setStyleJson(@NonNull String styleJson) { + nativeMapView.setStyleJson(styleJson); + } + + /** + * Returns the map style json currently displayed in the map view. + * + * @return The json of the map style + */ + public String getStyleJson() { + return nativeMapView.getStyleJson(); + } + // // Annotations // @@ -1126,7 +1187,6 @@ public final class MapboxMap { * @param markerOptions A marker options object that defines how to render the marker * @return The {@code Marker} that was added to the map */ - @UiThread @NonNull public Marker addMarker(@NonNull MarkerOptions markerOptions) { return annotationManager.addMarker(markerOptions, this); @@ -1142,7 +1202,6 @@ public final class MapboxMap { * @param markerOptions A marker options object that defines how to render the marker * @return The {@code Marker} that was added to the map */ - @UiThread @NonNull public Marker addMarker(@NonNull BaseMarkerOptions markerOptions) { return annotationManager.addMarker(markerOptions, this); @@ -1157,9 +1216,11 @@ public final class MapboxMap { * * @param markerOptions A marker options object that defines how to render the marker * @return The {@code Marker} that was added to the map + * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android + * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java */ - @UiThread @NonNull + @Deprecated public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions) { return annotationManager.addMarker(markerOptions, this, null); } @@ -1174,8 +1235,10 @@ public final class MapboxMap { * @param markerOptions A marker options object that defines how to render the marker * @param onMarkerViewAddedListener Callback invoked when the View has been added to the map * @return The {@code Marker} that was added to the map + * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android + * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java */ - @UiThread + @Deprecated @NonNull public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions, final MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener) { @@ -1191,9 +1254,11 @@ public final class MapboxMap { * * @param markerViewOptions A list of markerView options objects that defines how to render the markers * @return A list of the {@code MarkerView}s that were added to the map + * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android + * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java */ - @UiThread @NonNull + @Deprecated public List<MarkerView> addMarkerViews(@NonNull List<? extends BaseMarkerViewOptions> markerViewOptions) { return annotationManager.addMarkerViews(markerViewOptions, this); @@ -1204,9 +1269,11 @@ public final class MapboxMap { * * @param rect the rectangular area on the map to query for markerViews * @return A list of the markerViews that were found in the rectangle + * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android + * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java */ - @UiThread @NonNull + @Deprecated public List<MarkerView> getMarkerViewsInRect(@NonNull RectF rect) { return annotationManager.getMarkerViewsInRect(rect); } @@ -1221,7 +1288,6 @@ public final class MapboxMap { * @param markerOptionsList A list of marker options objects that defines how to render the markers * @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) { @@ -1235,9 +1301,8 @@ public final class MapboxMap { * * @param updatedMarker An updated marker object */ - @UiThread public void updateMarker(@NonNull Marker updatedMarker) { - annotationManager.updateMarker(updatedMarker); + annotationManager.updateMarker(updatedMarker, this); } /** @@ -1246,7 +1311,6 @@ public final class MapboxMap { * @param polylineOptions A polyline options object that defines how to render the polyline * @return The {@code Polyine} that was added to the map */ - @UiThread @NonNull public Polyline addPolyline(@NonNull PolylineOptions polylineOptions) { return annotationManager.addPolyline(polylineOptions, this); @@ -1258,7 +1322,6 @@ public final class MapboxMap { * @param polylineOptionsList A list of polyline options objects that defines how to render the polylines. * @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); @@ -1269,7 +1332,6 @@ public final class MapboxMap { * * @param polyline An updated polyline object. */ - @UiThread public void updatePolyline(Polyline polyline) { annotationManager.updatePolyline(polyline); } @@ -1280,7 +1342,6 @@ public final class MapboxMap { * @param polygonOptions A polygon options object that defines how to render the polygon. * @return The {@code Polygon} that was added to the map. */ - @UiThread @NonNull public Polygon addPolygon(@NonNull PolygonOptions polygonOptions) { return annotationManager.addPolygon(polygonOptions, this); @@ -1292,7 +1353,6 @@ public final class MapboxMap { * @param polygonOptionsList A list of polygon options objects that defines how to render the polygons * @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); @@ -1303,7 +1363,6 @@ public final class MapboxMap { * * @param polygon An updated polygon object */ - @UiThread public void updatePolygon(Polygon polygon) { annotationManager.updatePolygon(polygon); } @@ -1316,7 +1375,6 @@ public final class MapboxMap { * * @param marker Marker to remove */ - @UiThread public void removeMarker(@NonNull Marker marker) { annotationManager.removeAnnotation(marker); } @@ -1329,7 +1387,6 @@ public final class MapboxMap { * * @param polyline Polyline to remove */ - @UiThread public void removePolyline(@NonNull Polyline polyline) { annotationManager.removeAnnotation(polyline); } @@ -1342,7 +1399,6 @@ public final class MapboxMap { * * @param polygon Polygon to remove */ - @UiThread public void removePolygon(@NonNull Polygon polygon) { annotationManager.removeAnnotation(polygon); } @@ -1352,7 +1408,6 @@ public final class MapboxMap { * * @param annotation The annotation object to remove. */ - @UiThread public void removeAnnotation(@NonNull Annotation annotation) { annotationManager.removeAnnotation(annotation); } @@ -1362,7 +1417,6 @@ public final class MapboxMap { * * @param id The identifier associated to the annotation to be removed */ - @UiThread public void removeAnnotation(long id) { annotationManager.removeAnnotation(id); } @@ -1372,7 +1426,6 @@ public final class MapboxMap { * * @param annotationList A list of annotation objects to remove. */ - @UiThread public void removeAnnotations(@NonNull List<? extends Annotation> annotationList) { annotationManager.removeAnnotations(annotationList); } @@ -1380,7 +1433,6 @@ public final class MapboxMap { /** * Removes all annotations from the map. */ - @UiThread public void removeAnnotations() { annotationManager.removeAnnotations(); } @@ -1388,7 +1440,6 @@ public final class MapboxMap { /** * Removes all markers, polylines, polygons, overlays, etc from the map. */ - @UiThread public void clear() { annotationManager.removeAnnotations(); } @@ -1454,7 +1505,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when the user clicks on a marker. * To unset the callback, use null. */ - @UiThread public void setOnMarkerClickListener(@Nullable OnMarkerClickListener listener) { annotationManager.setOnMarkerClickListener(listener); } @@ -1465,7 +1515,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when the user clicks on a polygon. * To unset the callback, use null. */ - @UiThread public void setOnPolygonClickListener(@Nullable OnPolygonClickListener listener) { annotationManager.setOnPolygonClickListener(listener); } @@ -1476,7 +1525,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when the user clicks on a polyline. * To unset the callback, use null. */ - @UiThread public void setOnPolylineClickListener(@Nullable OnPolylineClickListener listener) { annotationManager.setOnPolylineClickListener(listener); } @@ -1491,7 +1539,6 @@ public final class MapboxMap { * * @param marker The marker to select. */ - @UiThread public void selectMarker(@NonNull Marker marker) { if (marker == null) { Timber.w("marker was null, so just returning"); @@ -1503,7 +1550,6 @@ public final class MapboxMap { /** * Deselects any currently selected marker. All markers will have it's info window closed. */ - @UiThread public void deselectMarkers() { annotationManager.deselectMarkers(); } @@ -1513,7 +1559,6 @@ public final class MapboxMap { * * @param marker the marker to deselect */ - @UiThread public void deselectMarker(@NonNull Marker marker) { annotationManager.deselectMarker(marker); } @@ -1523,7 +1568,6 @@ public final class MapboxMap { * * @return The currently selected marker. */ - @UiThread public List<Marker> getSelectedMarkers() { return annotationManager.getSelectedMarkers(); } @@ -1551,7 +1595,6 @@ public final class MapboxMap { * @param infoWindowAdapter The callback to be invoked when an info window will be shown. * To unset the callback, use null. */ - @UiThread public void setInfoWindowAdapter(@Nullable InfoWindowAdapter infoWindowAdapter) { annotationManager.getInfoWindowManager().setInfoWindowAdapter(infoWindowAdapter); } @@ -1561,7 +1604,6 @@ public final class MapboxMap { * * @return The callback to be invoked when an info window will be shown. */ - @UiThread @Nullable public InfoWindowAdapter getInfoWindowAdapter() { return annotationManager.getInfoWindowManager().getInfoWindowAdapter(); @@ -1572,7 +1614,6 @@ public final class MapboxMap { * * @param allow If true, map allows concurrent multiple infowindows to be shown. */ - @UiThread public void setAllowConcurrentMultipleOpenInfoWindows(boolean allow) { annotationManager.getInfoWindowManager().setAllowConcurrentMultipleOpenInfoWindows(allow); } @@ -1582,7 +1623,6 @@ public final class MapboxMap { * * @return If true, map allows concurrent multiple infowindows to be shown. */ - @UiThread public boolean isAllowConcurrentMultipleOpenInfoWindows() { return annotationManager.getInfoWindowManager().isAllowConcurrentMultipleOpenInfoWindows(); } @@ -1676,7 +1716,6 @@ public final class MapboxMap { * @param listener The callback that's invoked on every camera change position. * To unset the callback, use null. */ - @UiThread @Deprecated public void setOnCameraChangeListener(@Nullable OnCameraChangeListener listener) { transform.setOnCameraChangeListener(listener); @@ -1687,7 +1726,6 @@ public final class MapboxMap { * * @param listener the listener to notify */ - @UiThread public void setOnCameraIdleListener(@Nullable OnCameraIdleListener listener) { cameraChangeDispatcher.setOnCameraIdleListener(listener); } @@ -1697,7 +1735,6 @@ public final class MapboxMap { * * @param listener the listener to notify */ - @UiThread public void setOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) { cameraChangeDispatcher.setOnCameraMoveCanceledListener(listener); } @@ -1707,7 +1744,6 @@ public final class MapboxMap { * * @param listener the listener to notify */ - @UiThread public void setOnCameraMoveStartedListener(@Nullable OnCameraMoveStartedListener listener) { cameraChangeDispatcher.setOnCameraMoveStartedListener(listener); } @@ -1717,7 +1753,6 @@ public final class MapboxMap { * * @param listener the listener to notify */ - @UiThread public void setOnCameraMoveListener(@Nullable OnCameraMoveListener listener) { cameraChangeDispatcher.setOnCameraMoveListener(listener); } @@ -1728,9 +1763,9 @@ public final class MapboxMap { * @param listener The callback that's invoked on every frame rendered to the map view. * To unset the callback, use null. */ - @UiThread public void setOnFpsChangedListener(@Nullable OnFpsChangedListener listener) { onFpsChangedListener = listener; + nativeMapView.setOnFpsChangedListener(listener); } // used by MapView @@ -1744,7 +1779,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when the map is scrolled. * To unset the callback, use null. */ - @UiThread public void setOnScrollListener(@Nullable OnScrollListener listener) { onRegisterTouchListener.onRegisterScrollListener(listener); } @@ -1755,7 +1789,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when the map is flinged. * To unset the callback, use null. */ - @UiThread public void setOnFlingListener(@Nullable OnFlingListener listener) { onRegisterTouchListener.onRegisterFlingListener(listener); } @@ -1766,7 +1799,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when the user clicks on the map view. * To unset the callback, use null. */ - @UiThread public void setOnMapClickListener(@Nullable OnMapClickListener listener) { onRegisterTouchListener.onRegisterMapClickListener(listener); } @@ -1777,7 +1809,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when the user long clicks on the map view. * To unset the callback, use null. */ - @UiThread public void setOnMapLongClickListener(@Nullable OnMapLongClickListener listener) { onRegisterTouchListener.onRegisterMapLongClickListener(listener); } @@ -1788,7 +1819,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when the user clicks on an info window. * To unset the callback, use null. */ - @UiThread public void setOnInfoWindowClickListener(@Nullable OnInfoWindowClickListener listener) { annotationManager.getInfoWindowManager().setOnInfoWindowClickListener(listener); } @@ -1798,7 +1828,6 @@ public final class MapboxMap { * * @return Current active InfoWindow Click Listener */ - @UiThread public OnInfoWindowClickListener getOnInfoWindowClickListener() { return annotationManager.getInfoWindowManager().getOnInfoWindowClickListener(); } @@ -1809,7 +1838,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when a marker's info window is long pressed. To unset the callback, * use null. */ - @UiThread public void setOnInfoWindowLongClickListener(@Nullable OnInfoWindowLongClickListener listener) { annotationManager.getInfoWindowManager().setOnInfoWindowLongClickListener(listener); @@ -1838,7 +1866,6 @@ public final class MapboxMap { * * @return Current active InfoWindow Close Listener */ - @UiThread public OnInfoWindowCloseListener getOnInfoWindowCloseListener() { return annotationManager.getInfoWindowManager().getOnInfoWindowCloseListener(); } @@ -1851,8 +1878,10 @@ public final class MapboxMap { * Returns the status of the my-location layer. * * @return True if the my-location layer is enabled, false otherwise. + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ - @UiThread + @Deprecated public boolean isMyLocationEnabled() { return trackingSettings.isMyLocationEnabled(); } @@ -1867,8 +1896,10 @@ public final class MapboxMap { * android.Manifest.permission#ACCESS_COARSE_LOCATION or android.Manifest.permission#ACCESS_FINE_LOCATION. * * @param enabled True to enable; false to disable. + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ - @UiThread + @Deprecated public void setMyLocationEnabled(boolean enabled) { trackingSettings.setMyLocationEnabled(enabled); } @@ -1877,9 +1908,11 @@ public final class MapboxMap { * Returns the currently displayed user location, or null if there is no location data available. * * @return The currently displayed user location. + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ - @UiThread @Nullable + @Deprecated public Location getMyLocation() { return trackingSettings.getMyLocation(); } @@ -1890,8 +1923,10 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the user clicks on a marker. * To unset the callback, use null. + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ - @UiThread + @Deprecated public void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener listener) { trackingSettings.setOnMyLocationChangeListener(listener); @@ -1901,8 +1936,10 @@ public final class MapboxMap { * Replaces the location source of the my-location layer. * * @param locationSource A {@link LocationEngine} location source to use in the my-location layer. + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ - @UiThread + @Deprecated public void setLocationSource(@Nullable LocationEngine locationSource) { trackingSettings.setLocationSource(locationSource); } @@ -1912,8 +1949,10 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the location tracking mode changes. * To unset the callback, use null. + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ - @UiThread + @Deprecated public void setOnMyLocationTrackingModeChangeListener( @Nullable MapboxMap.OnMyLocationTrackingModeChangeListener listener) { trackingSettings.setOnMyLocationTrackingModeChangeListener(listener); @@ -1924,8 +1963,10 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the bearing tracking mode changes. * To unset the callback, use null. + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ - @UiThread + @Deprecated public void setOnMyBearingTrackingModeChangeListener(@Nullable OnMyBearingTrackingModeChangeListener listener) { trackingSettings.setOnMyBearingTrackingModeChangeListener(listener); } @@ -1939,7 +1980,6 @@ public final class MapboxMap { * * @param callback Callback method invoked when the snapshot is taken. */ - @UiThread public void snapshot(@NonNull SnapshotReadyCallback callback) { nativeMapView.addSnapshotCallback(callback); } @@ -1951,7 +1991,6 @@ public final class MapboxMap { * @param layerIds optionally - only query these layers * @return the list of feature */ - @UiThread @NonNull public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates, @Nullable String... layerIds) { @@ -1966,7 +2005,6 @@ public final class MapboxMap { * @param layerIds optionally - only query these layers * @return the list of feature */ - @UiThread @NonNull public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates, @Nullable Filter.Statement filter, @@ -1981,7 +2019,6 @@ public final class MapboxMap { * @param layerIds optionally - only query these layers * @return the list of feature */ - @UiThread @NonNull public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates, @Nullable String... layerIds) { @@ -1996,7 +2033,6 @@ public final class MapboxMap { * @param layerIds optionally - only query these layers * @return the list of feature */ - @UiThread @NonNull public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates, @Nullable Filter.Statement filter, @@ -2004,6 +2040,15 @@ public final class MapboxMap { return nativeMapView.queryRenderedFeatures(coordinates, layerIds, filter); } + FocalPointChangeListener createFocalPointChangeListener() { + return new FocalPointChangeListener() { + @Override + public void onFocalPointChanged(PointF pointF) { + focalPoint = pointF; + } + }; + } + // // Interfaces // @@ -2102,6 +2147,21 @@ public final class MapboxMap { } /** + * Interface definition for a callback to be invoked for when the compass is animating. + */ + public interface OnCompassAnimationListener { + /** + * Called repeatedly as the compass continues to move after clicking on it. + */ + void onCompassAnimation(); + + /** + * Called when compass animation has ended. + */ + void onCompassAnimationFinished(); + } + + /** * Interface definition for a callback to be invoked when a frame is rendered to the map view. * * @see MapboxMap#setOnFpsChangedListener(OnFpsChangedListener) @@ -2266,7 +2326,10 @@ public final class MapboxMap { * Interface definition for a callback to be invoked when an MarkerView will be shown. * * @param <U> the instance type of MarkerView + * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android + * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java */ + @Deprecated public abstract static class MarkerViewAdapter<U extends MarkerView> { private Context context; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java index 80b25bf0de..7b979f5563 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java @@ -79,12 +79,12 @@ public class MapboxMapOptions implements Parcelable { private int[] myLocationBackgroundPadding; private int myLocationAccuracyTintColor; private int myLocationAccuracyAlpha; + private float myLocationAccuracyThreshold; + private boolean prefetchesTiles = true; + private boolean zMediaOverlay = false; private String apiBaseUrl; - @Deprecated - private boolean textureMode; - private String style; /** @@ -148,10 +148,13 @@ public class MapboxMapOptions implements Parcelable { myLocationBackgroundPadding = in.createIntArray(); myLocationAccuracyAlpha = in.readInt(); myLocationAccuracyTintColor = in.readInt(); + myLocationAccuracyThreshold = in.readFloat(); style = in.readString(); apiBaseUrl = in.readString(); - textureMode = in.readByte() != 0; + + prefetchesTiles = in.readByte() != 0; + zMediaOverlay = in.readByte() != 0; } static Bitmap getBitmapFromDrawable(Drawable drawable) { @@ -291,8 +294,12 @@ public class MapboxMapOptions implements Parcelable { mapboxMapOptions.myLocationAccuracyTint( typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyTintColor, ColorUtils.getPrimaryColor(context))); - mapboxMapOptions.textureMode( - typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_renderTextureMode, false)); + mapboxMapOptions.myLocationAccuracyThreshold( + typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyThreshold, 0)); + mapboxMapOptions.setPrefetchesTiles( + typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableTilePrefetch, true)); + mapboxMapOptions.renderSurfaceOnTop( + typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableZMediaOverlay, false)); } finally { typedArray.recycle(); } @@ -681,22 +688,59 @@ public class MapboxMapOptions implements Parcelable { } /** - * Enable TextureView as rendered surface. - * <p> - * Since the 4.2.0 release we replaced our TextureView with an SurfaceView implemenation. - * Enabling this option will use the deprecated TextureView instead. - * </p> + * Set accuracy circle threshold. Circle won't be displayed if accuracy is below set value. + * + * @param myLocationAccuracyThreshold Value of accuracy (in meters), below which circle won't be displayed + * @return This + */ + public MapboxMapOptions myLocationAccuracyThreshold(float myLocationAccuracyThreshold) { + this.myLocationAccuracyThreshold = myLocationAccuracyThreshold; + return this; + } + + /** + * Enable tile pre-fetching. Loads tiles at a lower zoom-level to pre-render + * a low resolution preview while more detailed tiles are loaded. + * Enabled by default + * + * @param enable true to enable * - * @param textureMode True to enable texture mode * @return This - * @deprecated As of the 4.2.0 release, using TextureView is deprecated. */ - public MapboxMapOptions textureMode(boolean textureMode) { - this.textureMode = textureMode; + public MapboxMapOptions setPrefetchesTiles(boolean enable) { + this.prefetchesTiles = enable; return this; } /** + * Check whether tile pre-fetching is enabled. + * + * @return true if enabled + */ + public boolean getPrefetchesTiles() { + return prefetchesTiles; + } + + + /** + * Set the flag to render the map surface on top of another surface. + * + * @param renderOnTop true if this map is shown on top of another one, false if bottom. + */ + public void renderSurfaceOnTop(boolean renderOnTop) { + this.zMediaOverlay = renderOnTop; + } + + /** + * Get the flag to render the map surface on top of another surface. + * + * @return true if this map is + */ + public boolean getRenderSurfaceOnTop() { + return zMediaOverlay; + } + + /** * Get the current configured API endpoint base URL. * * @return Base URL to be used API endpoint. @@ -988,22 +1032,21 @@ public class MapboxMapOptions implements Parcelable { } /** - * Get the current configured debug state for a map view. + * Returns current accuracy threshold value (in meters). * - * @return True indicates debug is enabled. + * @return Value of accuracy threshold (in meters), below which circle won't be displayed */ - public boolean getDebugActive() { - return debugActive; + public float getMyLocationAccuracyThreshold() { + return myLocationAccuracyThreshold; } /** - * Returns true if TextureView is being used a render view. + * Get the current configured debug state for a map view. * - * @return True if TextureView is used. - * @deprecated As of the 4.2.0 release, using TextureView is deprecated. + * @return True indicates debug is enabled. */ - public boolean getTextureMode() { - return textureMode; + public boolean getDebugActive() { + return debugActive; } public static final Parcelable.Creator<MapboxMapOptions> CREATOR = new Parcelable.Creator<MapboxMapOptions>() { @@ -1065,10 +1108,13 @@ public class MapboxMapOptions implements Parcelable { dest.writeIntArray(myLocationBackgroundPadding); dest.writeInt(myLocationAccuracyAlpha); dest.writeInt(myLocationAccuracyTintColor); + dest.writeFloat(myLocationAccuracyThreshold); dest.writeString(style); dest.writeString(apiBaseUrl); - dest.writeByte((byte) (textureMode ? 1 : 0)); + + dest.writeByte((byte) (prefetchesTiles ? 1 : 0)); + dest.writeByte((byte) (zMediaOverlay ? 1 : 0)); } @Override @@ -1153,6 +1199,9 @@ public class MapboxMapOptions implements Parcelable { if (myLocationAccuracyAlpha != options.myLocationAccuracyAlpha) { return false; } + if (myLocationAccuracyThreshold != options.myLocationAccuracyThreshold) { + return false; + } if (cameraPosition != null ? !cameraPosition.equals(options.cameraPosition) : options.cameraPosition != null) { return false; } @@ -1189,6 +1238,13 @@ public class MapboxMapOptions implements Parcelable { if (apiBaseUrl != null ? !apiBaseUrl.equals(options.apiBaseUrl) : options.apiBaseUrl != null) { return false; } + if (prefetchesTiles != options.prefetchesTiles) { + return false; + } + if (zMediaOverlay != options.zMediaOverlay) { + return false; + } + return false; } @@ -1230,9 +1286,12 @@ public class MapboxMapOptions implements Parcelable { result = 31 * result + Arrays.hashCode(myLocationBackgroundPadding); result = 31 * result + myLocationAccuracyTintColor; result = 31 * result + myLocationAccuracyAlpha; + result = 31 * result + (myLocationAccuracyThreshold != +0.0f + ? Float.floatToIntBits(myLocationAccuracyThreshold) : 0); result = 31 * result + (apiBaseUrl != null ? apiBaseUrl.hashCode() : 0); - result = 31 * result + (textureMode ? 1 : 0); result = 31 * result + (style != null ? style.hashCode() : 0); + result = 31 * result + (prefetchesTiles ? 1 : 0); + result = 31 * result + (zMediaOverlay ? 1 : 0); return result; } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java new file mode 100644 index 0000000000..072382ce07 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java @@ -0,0 +1,244 @@ +package com.mapbox.mapboxsdk.maps; + + +import android.graphics.RectF; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.util.LongSparseArray; + +import com.mapbox.mapboxsdk.annotations.Annotation; +import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; +import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions; +import com.mapbox.mapboxsdk.annotations.Icon; +import com.mapbox.mapboxsdk.annotations.IconFactory; +import com.mapbox.mapboxsdk.annotations.Marker; +import com.mapbox.mapboxsdk.annotations.MarkerView; +import com.mapbox.mapboxsdk.annotations.MarkerViewManager; + +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates {@link Marker}'s functionality. + */ +class MarkerContainer implements Markers { + + private final NativeMapView nativeMapView; + private final MapView mapView; + private final LongSparseArray<Annotation> annotations; + private final IconManager iconManager; + private final MarkerViewManager markerViewManager; + + MarkerContainer(NativeMapView nativeMapView, MapView mapView, LongSparseArray<Annotation> annotations, IconManager + iconManager, MarkerViewManager markerViewManager) { + this.nativeMapView = nativeMapView; + this.mapView = mapView; + this.annotations = annotations; + this.iconManager = iconManager; + this.markerViewManager = markerViewManager; + } + + @Override + public Marker addBy(@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; + } + + @Override + public List<Marker> addBy(@NonNull List<? extends BaseMarkerOptions> markerOptionsList, @NonNull MapboxMap + mapboxMap) { + int count = markerOptionsList.size(); + List<Marker> markers = new ArrayList<>(count); + if (nativeMapView != null && 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 = nativeMapView.addMarkers(markers); + for (int i = 0; i < ids.length; i++) { + Marker createdMarker = markers.get(i); + createdMarker.setMapboxMap(mapboxMap); + createdMarker.setId(ids[i]); + annotations.put(ids[i], createdMarker); + } + } + } + return markers; + } + + @Override + public void update(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap) { + ensureIconLoaded(updatedMarker, mapboxMap); + nativeMapView.updateMarker(updatedMarker); + annotations.setValueAt(annotations.indexOfKey(updatedMarker.getId()), updatedMarker); + } + + @Override + public List<Marker> obtainAll() { + List<Marker> markers = new ArrayList<>(); + Annotation annotation; + for (int i = 0; i < annotations.size(); i++) { + annotation = annotations.get(annotations.keyAt(i)); + if (annotation instanceof Marker) { + markers.add((Marker) annotation); + } + } + return markers; + } + + @NonNull + @Override + public List<Marker> obtainAllIn(@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); + + long[] ids = nativeMapView.queryPointAnnotations(rect); + + List<Long> idsList = new ArrayList<>(ids.length); + for (long id : ids) { + idsList.add(id); + } + + List<Marker> annotations = new ArrayList<>(ids.length); + List<Annotation> annotationList = obtainAnnotations(); + 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); + } + } + + return new ArrayList<>(annotations); + } + + @Override + public MarkerView addViewBy(@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; + } + + @Override + public List<MarkerView> addViewsBy(@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); + } + // 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; + } + + @Override + public List<MarkerView> obtainViewsIn(@NonNull RectF rectangle) { + 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); + + List<Long> idsList = new ArrayList<>(ids.length); + for (long id : ids) { + idsList.add(id); + } + + List<MarkerView> annotations = new ArrayList<>(ids.length); + List<Annotation> annotationList = obtainAnnotations(); + 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); + } + + @Override + public void reload() { + 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); + } + } + } + + private Marker prepareMarker(BaseMarkerOptions markerOptions) { + Marker marker = markerOptions.getMarker(); + Icon icon = iconManager.loadIconForMarker(marker); + marker.setTopOffsetPixels(iconManager.getTopOffsetPixelsForIcon(icon)); + return marker; + } + + private void ensureIconLoaded(Marker marker, MapboxMap mapboxMap) { + if (!(marker instanceof MarkerView)) { + iconManager.ensureIconLoaded(marker, mapboxMap); + } + } + + private List<Annotation> obtainAnnotations() { + List<Annotation> annotations = new ArrayList<>(); + for (int i = 0; i < this.annotations.size(); i++) { + annotations.add(this.annotations.get(this.annotations.keyAt(i))); + } + return annotations; + } + + private MarkerView prepareViewMarker(BaseMarkerViewOptions markerViewOptions) { + MarkerView marker = markerViewOptions.getMarker(); + Icon icon = markerViewOptions.getIcon(); + if (icon == null) { + icon = IconFactory.getInstance(mapView.getContext()).defaultMarkerView(); + } + iconManager.loadIconForMarkerView(marker); + marker.setIcon(icon); + return marker; + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java new file mode 100644 index 0000000000..d646e0ac49 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java @@ -0,0 +1,39 @@ +package com.mapbox.mapboxsdk.maps; + + +import android.graphics.RectF; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; +import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions; +import com.mapbox.mapboxsdk.annotations.Marker; +import com.mapbox.mapboxsdk.annotations.MarkerView; +import com.mapbox.mapboxsdk.annotations.MarkerViewManager; + +import java.util.List; + +/** + * Interface that defines convenient methods for working with a {@link Marker}'s collection. + */ +interface Markers { + Marker addBy(@NonNull BaseMarkerOptions markerOptions, @NonNull MapboxMap mapboxMap); + + List<Marker> addBy(@NonNull List<? extends BaseMarkerOptions> markerOptionsList, @NonNull MapboxMap mapboxMap); + + void update(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap); + + List<Marker> obtainAll(); + + List<Marker> obtainAllIn(@NonNull RectF rectangle); + + MarkerView addViewBy(@NonNull BaseMarkerViewOptions markerOptions, @NonNull MapboxMap mapboxMap, + @Nullable MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener); + + List<MarkerView> addViewsBy(@NonNull List<? extends BaseMarkerViewOptions> markerViewOptions, + @NonNull MapboxMap mapboxMap); + + List<MarkerView> obtainViewsIn(@NonNull RectF rectangle); + + void reload(); +} 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 af3b57151d..3ce6aab581 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 @@ -1,27 +1,25 @@ package com.mapbox.mapboxsdk.maps; -import android.app.ActivityManager; import android.content.Context; import android.graphics.Bitmap; import android.graphics.PointF; import android.graphics.RectF; -import android.os.Build; import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.DisplayMetrics; -import android.view.Surface; +import com.mapbox.mapboxsdk.LibraryLoader; import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.Polygon; import com.mapbox.mapboxsdk.annotations.Polyline; import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.geometry.ProjectedMeters; +import com.mapbox.mapboxsdk.maps.renderer.MapRenderer; import com.mapbox.mapboxsdk.storage.FileSource; import com.mapbox.mapboxsdk.style.layers.CannotAddLayerException; import com.mapbox.mapboxsdk.style.layers.Filter; @@ -36,7 +34,6 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; import timber.log.Timber; @@ -55,53 +52,32 @@ final class NativeMapView { //Hold a reference to prevent it from being GC'd as long as it's used on the native side private final FileSource fileSource; + // Used to schedule work on the MapRenderer Thread + private MapRenderer mapRenderer; + // Device density private final float pixelRatio; - // Listeners for Map change events - private CopyOnWriteArrayList<MapView.OnMapChangedListener> onMapChangedListeners; - // Listener invoked to return a bitmap of the map private MapboxMap.SnapshotReadyCallback snapshotReadyCallback; - // - // Static methods - // - static { - System.loadLibrary("mapbox-gl"); + LibraryLoader.load(); } // // Constructors // - public NativeMapView(MapView mapView) { + public NativeMapView(final MapView mapView, MapRenderer mapRenderer) { + this.mapRenderer = mapRenderer; + this.mapView = mapView; + Context context = mapView.getContext(); fileSource = FileSource.getInstance(context); - pixelRatio = context.getResources().getDisplayMetrics().density; - int availableProcessors = Runtime.getRuntime().availableProcessors(); - ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); - ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); - activityManager.getMemoryInfo(memoryInfo); - long totalMemory = memoryInfo.availMem; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - totalMemory = memoryInfo.totalMem; - } - - if (availableProcessors < 0) { - throw new IllegalArgumentException("availableProcessors cannot be negative."); - } - if (totalMemory < 0) { - throw new IllegalArgumentException("totalMemory cannot be negative."); - } - onMapChangedListeners = new CopyOnWriteArrayList<>(); - this.mapView = mapView; - - String programCacheDir = context.getCacheDir().getAbsolutePath(); - nativeInitialize(this, fileSource, pixelRatio, programCacheDir, availableProcessors, totalMemory); + nativeInitialize(this, fileSource, mapRenderer, pixelRatio); } // @@ -110,9 +86,10 @@ final class NativeMapView { private boolean isDestroyedOn(String callingMethod) { if (destroyed && !TextUtils.isEmpty(callingMethod)) { - Timber.e(String.format(MapboxConstants.MAPBOX_LOCALE, + Timber.e( "You're calling `%s` after the `MapView` was destroyed, were you invoking it after `onDestroy()`?", - callingMethod)); + callingMethod + ); } return destroyed; } @@ -123,60 +100,12 @@ final class NativeMapView { destroyed = true; } - public void initializeDisplay() { - if (isDestroyedOn("initializeDisplay")) { - return; - } - nativeInitializeDisplay(); - } - - public void terminateDisplay() { - if (isDestroyedOn("terminateDisplay")) { - return; - } - nativeTerminateDisplay(); - } - - public void initializeContext() { - if (isDestroyedOn("initializeContext")) { - return; - } - nativeInitializeContext(); - } - - public void terminateContext() { - if (isDestroyedOn("terminateContext")) { - return; - } - nativeTerminateContext(); - } - - public void createSurface(Surface surface) { - if (isDestroyedOn("createSurface")) { - return; - } - nativeCreateSurface(surface); - } - - public void destroySurface() { - if (isDestroyedOn("destroySurface")) { - return; - } - nativeDestroySurface(); - } - public void update() { if (isDestroyedOn("update")) { return; } - nativeUpdate(); - } - public void render() { - if (isDestroyedOn("render")) { - return; - } - nativeRender(); + mapRenderer.requestRender(); } public void resizeView(int width, int height) { @@ -197,41 +126,18 @@ final class NativeMapView { if (width > 65535) { // we have seen edge cases where devices return incorrect values #6111 Timber.e("Device returned an out of range width size, " - + "capping value at 65535 instead of " + width); + + "capping value at 65535 instead of %s", width); width = 65535; } if (height > 65535) { // we have seen edge cases where devices return incorrect values #6111 Timber.e("Device returned an out of range height size, " - + "capping value at 65535 instead of " + height); + + "capping value at 65535 instead of %s", height); height = 65535; } - nativeResizeView(width, height); - } - - public void resizeFramebuffer(int fbWidth, int fbHeight) { - if (isDestroyedOn("resizeFramebuffer")) { - return; - } - if (fbWidth < 0) { - throw new IllegalArgumentException("fbWidth cannot be negative."); - } - - if (fbHeight < 0) { - throw new IllegalArgumentException("fbHeight cannot be negative."); - } - - if (fbWidth > 65535) { - throw new IllegalArgumentException( - "fbWidth cannot be greater than 65535."); - } - if (fbHeight > 65535) { - throw new IllegalArgumentException( - "fbHeight cannot be greater than 65535."); - } - nativeResizeFramebuffer(fbWidth, fbHeight); + nativeResizeView(width, height); } public void setStyleUrl(String url) { @@ -561,6 +467,13 @@ final class NativeMapView { nativeAddAnnotationIcon(symbol, width, height, scale, pixels); } + public void removeAnnotationIcon(String symbol) { + if (isDestroyedOn("removeAnnotationIcon")) { + return; + } + nativeRemoveAnnotationIcon(symbol); + } + public void setVisibleCoordinateBounds(LatLng[] coordinates, RectF padding, double direction, long duration) { if (isDestroyedOn("setVisibleCoordinateBounds")) { return; @@ -596,13 +509,6 @@ final class NativeMapView { return nativeGetDebug(); } - public void setEnableFps(boolean enable) { - if (isDestroyedOn("setEnableFps")) { - return; - } - nativeSetEnableFps(enable); - } - public boolean isFullyLoaded() { if (isDestroyedOn("isFullyLoaded")) { return false; @@ -621,7 +527,7 @@ final class NativeMapView { if (isDestroyedOn("getMetersPerPixelAtLatitude")) { return 0; } - return nativeGetMetersPerPixelAtLatitude(lat, getZoom()); + return nativeGetMetersPerPixelAtLatitude(lat, getZoom()) / pixelRatio; } public ProjectedMeters projectedMetersForLatLng(LatLng latLng) { @@ -692,6 +598,20 @@ final class NativeMapView { return nativeGetCameraPosition(); } + public void setPrefetchesTiles(boolean enable) { + if (isDestroyedOn("setPrefetchesTiles")) { + return; + } + nativeSetPrefetchesTiles(enable); + } + + public boolean getPrefetchesTiles() { + if (isDestroyedOn("getPrefetchesTiles")) { + return false; + } + return nativeGetPrefetchesTiles(); + } + // Runtime style Api public long getTransitionDuration() { @@ -841,6 +761,13 @@ final class NativeMapView { nativeRemoveImage(name); } + public Bitmap getImage(String name) { + if (isDestroyedOn("getImage")) { + return null; + } + return nativeGetImage(name); + } + // Feature querying @NonNull @@ -872,13 +799,6 @@ final class NativeMapView { return features != null ? Arrays.asList(features) : new ArrayList<Feature>(); } - public void scheduleTakeSnapshot() { - if (isDestroyedOn("scheduleTakeSnapshot")) { - return; - } - nativeTakeSnapshot(); - } - public void setApiBaseUrl(String baseUrl) { if (isDestroyedOn("setApiBaseUrl")) { return; @@ -901,29 +821,10 @@ final class NativeMapView { // Callbacks // - protected void onInvalidate() { - if (mapView != null) { - mapView.onInvalidate(); - } - } - protected void onMapChanged(int rawChange) { - if (onMapChangedListeners != null) { - for (MapView.OnMapChangedListener onMapChangedListener : onMapChangedListeners) { - try { - onMapChangedListener.onMapChanged(rawChange); - } catch (RuntimeException err) { - Timber.e("Exception (%s) in MapView.OnMapChangedListener: %s", err.getClass(), err.getMessage()); - } - } - } - } - - protected void onFpsChanged(double fps) { - if (isDestroyedOn("OnFpsChanged")) { - return; + if (mapView != null) { + mapView.onMapChange(rawChange); } - mapView.onFpsChanged(fps); } protected void onSnapshotReady(Bitmap mapContent) { @@ -943,33 +844,13 @@ final class NativeMapView { private native void nativeInitialize(NativeMapView nativeMapView, FileSource fileSource, - float pixelRatio, - String programCacheDir, - int availableProcessors, - long totalMemory); + MapRenderer mapRenderer, + float pixelRatio); private native void nativeDestroy(); - private native void nativeInitializeDisplay(); - - private native void nativeTerminateDisplay(); - - private native void nativeInitializeContext(); - - private native void nativeTerminateContext(); - - private native void nativeCreateSurface(Object surface); - - private native void nativeDestroySurface(); - - private native void nativeUpdate(); - - private native void nativeRender(); - private native void nativeResizeView(int width, int height); - private native void nativeResizeFramebuffer(int fbWidth, int fbHeight); - private native void nativeSetStyleUrl(String url); private native String nativeGetStyleUrl(); @@ -1038,6 +919,8 @@ final class NativeMapView { private native void nativeAddAnnotationIcon(String symbol, int width, int height, float scale, byte[] pixels); + private native void nativeRemoveAnnotationIcon(String symbol); + private native void nativeSetVisibleCoordinateBounds(LatLng[] coordinates, RectF padding, double direction, long duration); @@ -1049,8 +932,6 @@ final class NativeMapView { private native boolean nativeGetDebug(); - private native void nativeSetEnableFps(boolean enable); - private native boolean nativeIsFullyLoaded(); private native void nativeSetReachability(boolean status); @@ -1117,6 +998,8 @@ final class NativeMapView { private native void nativeRemoveImage(String name); + private native Bitmap nativeGetImage(String name); + private native void nativeUpdatePolygon(long polygonId, Polygon polygon); private native void nativeUpdatePolyline(long polylineId, Polyline polyline); @@ -1134,6 +1017,10 @@ final class NativeMapView { private native Light nativeGetLight(); + private native void nativeSetPrefetchesTiles(boolean enable); + + private native boolean nativeGetPrefetchesTiles(); + int getWidth() { if (isDestroyedOn("")) { return 0; @@ -1153,11 +1040,11 @@ final class NativeMapView { // void addOnMapChangedListener(@NonNull MapView.OnMapChangedListener listener) { - onMapChangedListeners.add(listener); + mapView.addOnMapChangedListener(listener); } void removeOnMapChangedListener(@NonNull MapView.OnMapChangedListener listener) { - onMapChangedListeners.remove(listener); + mapView.removeOnMapChangedListener(listener); } // @@ -1165,8 +1052,35 @@ final class NativeMapView { // void addSnapshotCallback(@NonNull MapboxMap.SnapshotReadyCallback callback) { + if (isDestroyedOn("addSnapshotCallback")) { + return; + } snapshotReadyCallback = callback; - scheduleTakeSnapshot(); - render(); + nativeTakeSnapshot(); + } + + public void setOnFpsChangedListener(final MapboxMap.OnFpsChangedListener listener) { + mapRenderer.queueEvent(new Runnable() { + + @Override + public void run() { + mapRenderer.setOnFpsChangedListener(new MapboxMap.OnFpsChangedListener() { + + @Override + public void onFpsChanged(final double fps) { + mapView.post(new Runnable() { + + @Override + public void run() { + listener.onFpsChanged(fps); + } + + }); + } + + }); + } + + }); } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java new file mode 100644 index 0000000000..016862bddc --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java @@ -0,0 +1,82 @@ +package com.mapbox.mapboxsdk.maps; + + +import android.support.annotation.NonNull; +import android.support.v4.util.LongSparseArray; + +import com.mapbox.mapboxsdk.annotations.Annotation; +import com.mapbox.mapboxsdk.annotations.Polygon; +import com.mapbox.mapboxsdk.annotations.PolygonOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates {@link Polygon}'s functionality. + */ +class PolygonContainer implements Polygons { + + private final NativeMapView nativeMapView; + private final LongSparseArray<Annotation> annotations; + + PolygonContainer(NativeMapView nativeMapView, LongSparseArray<Annotation> annotations) { + this.nativeMapView = nativeMapView; + this.annotations = annotations; + } + + @Override + public Polygon addBy(@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; + } + + @Override + public List<Polygon> addBy(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap) { + int count = polygonOptionsList.size(); + + Polygon polygon; + List<Polygon> polygons = new ArrayList<>(count); + if (nativeMapView != null && count > 0) { + for (PolygonOptions polygonOptions : polygonOptionsList) { + polygon = polygonOptions.getPolygon(); + if (!polygon.getPoints().isEmpty()) { + polygons.add(polygon); + } + } + + long[] ids = nativeMapView.addPolygons(polygons); + for (int i = 0; i < ids.length; i++) { + polygon = polygons.get(i); + polygon.setMapboxMap(mapboxMap); + polygon.setId(ids[i]); + annotations.put(ids[i], polygon); + } + } + return polygons; + } + + @Override + public void update(Polygon polygon) { + nativeMapView.updatePolygon(polygon); + annotations.setValueAt(annotations.indexOfKey(polygon.getId()), polygon); + } + + @Override + public List<Polygon> obtainAll() { + List<Polygon> polygons = new ArrayList<>(); + Annotation annotation; + for (int i = 0; i < annotations.size(); i++) { + annotation = annotations.get(annotations.keyAt(i)); + if (annotation instanceof Polygon) { + polygons.add((Polygon) annotation); + } + } + return polygons; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java new file mode 100644 index 0000000000..2a0190b5ba --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java @@ -0,0 +1,22 @@ +package com.mapbox.mapboxsdk.maps; + + +import android.support.annotation.NonNull; + +import com.mapbox.mapboxsdk.annotations.Polygon; +import com.mapbox.mapboxsdk.annotations.PolygonOptions; + +import java.util.List; + +/** + * Interface that defines convenient methods for working with a {@link Polygon}'s collection. + */ +interface Polygons { + Polygon addBy(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap); + + List<Polygon> addBy(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap); + + void update(Polygon polygon); + + List<Polygon> obtainAll(); +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java new file mode 100644 index 0000000000..303b25fb55 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java @@ -0,0 +1,81 @@ +package com.mapbox.mapboxsdk.maps; + + +import android.support.annotation.NonNull; +import android.support.v4.util.LongSparseArray; + +import com.mapbox.mapboxsdk.annotations.Annotation; +import com.mapbox.mapboxsdk.annotations.Polyline; +import com.mapbox.mapboxsdk.annotations.PolylineOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates {@link Polyline}'s functionality. + */ +class PolylineContainer implements Polylines { + + private final NativeMapView nativeMapView; + private final LongSparseArray<Annotation> annotations; + + PolylineContainer(NativeMapView nativeMapView, LongSparseArray<Annotation> annotations) { + this.nativeMapView = nativeMapView; + this.annotations = annotations; + } + + @Override + public Polyline addBy(@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; + } + + @Override + public List<Polyline> addBy(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap) { + int count = polylineOptionsList.size(); + Polyline polyline; + List<Polyline> polylines = new ArrayList<>(count); + if (nativeMapView != null && count > 0) { + for (PolylineOptions options : polylineOptionsList) { + polyline = options.getPolyline(); + if (!polyline.getPoints().isEmpty()) { + polylines.add(polyline); + } + } + + long[] ids = nativeMapView.addPolylines(polylines); + for (int i = 0; i < ids.length; i++) { + Polyline polylineCreated = polylines.get(i); + polylineCreated.setMapboxMap(mapboxMap); + polylineCreated.setId(ids[i]); + annotations.put(ids[i], polylineCreated); + } + } + return polylines; + } + + @Override + public void update(Polyline polyline) { + nativeMapView.updatePolyline(polyline); + annotations.setValueAt(annotations.indexOfKey(polyline.getId()), polyline); + } + + @Override + public List<Polyline> obtainAll() { + List<Polyline> polylines = new ArrayList<>(); + Annotation annotation; + for (int i = 0; i < annotations.size(); i++) { + annotation = annotations.get(annotations.keyAt(i)); + if (annotation instanceof Polyline) { + polylines.add((Polyline) annotation); + } + } + return polylines; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java new file mode 100644 index 0000000000..c9a865cdd0 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java @@ -0,0 +1,22 @@ +package com.mapbox.mapboxsdk.maps; + + +import android.support.annotation.NonNull; + +import com.mapbox.mapboxsdk.annotations.Polyline; +import com.mapbox.mapboxsdk.annotations.PolylineOptions; + +import java.util.List; + +/** + * Interface that defines convenient methods for working with a {@link Polyline}'s collection. + */ +interface Polylines { + Polyline addBy(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap); + + List<Polyline> addBy(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap); + + void update(Polyline polyline); + + List<Polyline> obtainAll(); +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java index 77fea1a14a..6c90cd95ec 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java @@ -76,6 +76,9 @@ public class SupportMapFragment extends Fragment { public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); map.onCreate(savedInstanceState); + if (onMapReadyCallback != null) { + map.getMapAsync(onMapReadyCallback); + } } /** @@ -85,7 +88,6 @@ public class SupportMapFragment extends Fragment { public void onStart() { super.onStart(); map.onStart(); - map.getMapAsync(onMapReadyCallback); } /** @@ -150,6 +152,10 @@ public class SupportMapFragment extends Fragment { * @param onMapReadyCallback The callback to be invoked. */ public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) { - this.onMapReadyCallback = onMapReadyCallback; + if (map == null) { + this.onMapReadyCallback = onMapReadyCallback; + } else { + map.getMapAsync(onMapReadyCallback); + } } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java index bd0bf7c83b..6881ca067b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java @@ -20,7 +20,11 @@ import timber.log.Timber; /** * Settings for the user location and bearing tracking of a MapboxMap. + * + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ +@Deprecated public final class TrackingSettings { private final MyLocationView myLocationView; 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 d788b7772b..6f63c2eba8 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 @@ -346,7 +346,7 @@ final class Transform implements MapView.OnMapChangedListener { void setMinZoom(double minZoom) { if ((minZoom < MapboxConstants.MINIMUM_ZOOM) || (minZoom > MapboxConstants.MAXIMUM_ZOOM)) { - Timber.e("Not setting minZoomPreference, value is in unsupported range: " + minZoom); + Timber.e("Not setting minZoomPreference, value is in unsupported range: %s", minZoom); return; } mapView.setMinZoom(minZoom); @@ -358,7 +358,7 @@ final class Transform implements MapView.OnMapChangedListener { void setMaxZoom(double maxZoom) { if ((maxZoom < MapboxConstants.MINIMUM_ZOOM) || (maxZoom > MapboxConstants.MAXIMUM_ZOOM)) { - Timber.e("Not setting maxZoomPreference, value is in unsupported range: " + maxZoom); + Timber.e("Not setting maxZoomPreference, value is in unsupported range: %s", maxZoom); return; } mapView.setMaxZoom(maxZoom); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java new file mode 100644 index 0000000000..3f43522e01 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java @@ -0,0 +1,145 @@ +package com.mapbox.mapboxsdk.maps.renderer; + +import android.content.Context; +import android.opengl.GLSurfaceView; + +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.storage.FileSource; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +/** + * The {@link MapRenderer} encapsulates the GL thread. + * <p> + * Performs actions on the GL thread to manage the GL resources and + * render on the one end and acts as a scheduler to request work to + * be performed on the GL thread on the other. + */ +public class MapRenderer implements GLSurfaceView.Renderer, MapRendererScheduler { + + // Holds the pointer to the native peer after initialisation + private long nativePtr = 0; + + private final GLSurfaceView glSurfaceView; + + private MapboxMap.OnFpsChangedListener onFpsChangedListener; + + public MapRenderer(Context context, GLSurfaceView glSurfaceView) { + this.glSurfaceView = glSurfaceView; + + FileSource fileSource = FileSource.getInstance(context); + float pixelRatio = context.getResources().getDisplayMetrics().density; + String programCacheDir = context.getCacheDir().getAbsolutePath(); + + // Initialise native peer + nativeInitialize(this, fileSource, pixelRatio, programCacheDir); + } + + public void setOnFpsChangedListener(MapboxMap.OnFpsChangedListener listener) { + onFpsChangedListener = listener; + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + nativeOnSurfaceCreated(); + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + if (width < 0) { + throw new IllegalArgumentException("fbWidth cannot be negative."); + } + + if (height < 0) { + throw new IllegalArgumentException("fbHeight cannot be negative."); + } + + if (width > 65535) { + throw new IllegalArgumentException( + "fbWidth cannot be greater than 65535."); + } + + if (height > 65535) { + throw new IllegalArgumentException( + "fbHeight cannot be greater than 65535."); + } + + gl.glViewport(0, 0, width, height); + nativeOnSurfaceChanged(width, height); + } + + @Override + public void onDrawFrame(GL10 gl) { + nativeRender(); + + if (onFpsChangedListener != null) { + updateFps(); + } + } + + /** + * May be called from any thread. + * <p> + * Called from the renderer frontend to schedule a render. + */ + @Override + public void requestRender() { + glSurfaceView.requestRender(); + } + + /** + * May be called from any thread. + * <p> + * Schedules work to be performed on the MapRenderer thread. + * + * @param runnable the runnable to execute + */ + @Override + public void queueEvent(Runnable runnable) { + glSurfaceView.queueEvent(runnable); + } + + /** + * May be called from any thread. + * <p> + * Called from the native peer to schedule work on the GL + * thread. Explicit override for easier to read jni code. + * + * @param runnable the runnable to execute + * @see MapRendererRunnable + */ + void queueEvent(MapRendererRunnable runnable) { + this.queueEvent((Runnable) runnable); + } + + private native void nativeInitialize(MapRenderer self, + FileSource fileSource, + float pixelRatio, + String programCacheDir); + + @Override + protected native void finalize() throws Throwable; + + private native void nativeOnSurfaceCreated(); + + private native void nativeOnSurfaceChanged(int width, int height); + + private native void nativeRender(); + + private long frames; + private long timeElapsed; + + private void updateFps() { + frames++; + long currentTime = System.nanoTime(); + double fps = 0; + if (currentTime - timeElapsed >= 1) { + fps = frames / ((currentTime - timeElapsed) / 1E9); + onFpsChangedListener.onFpsChanged(fps); + timeElapsed = currentTime; + frames = 0; + } + } + +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable.java new file mode 100644 index 0000000000..28246fe578 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable.java @@ -0,0 +1,29 @@ +package com.mapbox.mapboxsdk.maps.renderer; + +/** + * Peer class for {@link Runnable}s to be scheduled on the {@link MapRenderer} thread. + * The actual work is performed in the native peer. + */ +class MapRendererRunnable implements Runnable { + + // Holds the pointer to the native peer after initialisation + private final long nativePtr; + + /** + * Constructed from the native peer constructor + * + * @param nativePtr the native peer's memory address + */ + MapRendererRunnable(long nativePtr) { + this.nativePtr = nativePtr; + } + + @Override + public native void run(); + + @Override + protected native void finalize() throws Throwable; + + private native void nativeInitialize(); + +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererScheduler.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererScheduler.java new file mode 100644 index 0000000000..7ad4f124d8 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererScheduler.java @@ -0,0 +1,13 @@ +package com.mapbox.mapboxsdk.maps.renderer; + +/** + * Can be used to schedule work on the map renderer + * thread or request a render. + */ +public interface MapRendererScheduler { + + void requestRender(); + + void queueEvent(Runnable runnable); + +} 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 2b327409ae..45f72af1c5 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 @@ -1,10 +1,8 @@ package com.mapbox.mapboxsdk.maps.widgets; import android.content.Context; -import android.graphics.PointF; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPropertyAnimatorCompat; import android.support.v4.view.ViewPropertyAnimatorListenerAdapter; @@ -13,11 +11,8 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; -import com.mapbox.mapboxsdk.maps.FocalPointChangeListener; import com.mapbox.mapboxsdk.maps.MapboxMap; -import java.lang.ref.WeakReference; - /** * UI element overlaid on a map to show the map's bearing when it isn't true north (0.0). Tapping * the compass resets the bearing to true north and hides the compass. @@ -27,16 +22,17 @@ import java.lang.ref.WeakReference; * use {@link com.mapbox.mapboxsdk.maps.UiSettings}. * </p> */ -public final class CompassView extends AppCompatImageView implements Runnable, FocalPointChangeListener { +public final class CompassView extends AppCompatImageView implements Runnable { - private static final long TIME_WAIT_IDLE = 500; + public static final long TIME_WAIT_IDLE = 500; + public static final long TIME_MAP_NORTH_ANIMATION = 150; private static final long TIME_FADE_ANIMATION = TIME_WAIT_IDLE; - private static final long TIME_MAP_NORTH_ANIMATION = 150; private float rotation = 0.0f; private boolean fadeCompassViewFacingNorth = true; private ViewPropertyAnimatorCompat fadeAnimator; - private PointF focalPoint; + private MapboxMap.OnCompassAnimationListener compassAnimationListener; + private boolean isAnimating = false; public CompassView(Context context) { super(context); @@ -62,9 +58,12 @@ public final class CompassView extends AppCompatImageView implements Runnable, F setLayoutParams(lp); } - // TODO refactor MapboxMap and replace with interface - public void setMapboxMap(@NonNull MapboxMap mapboxMap) { - setOnClickListener(new CompassClickListener(mapboxMap, this)); + public void injectCompassAnimationListener(@NonNull MapboxMap.OnCompassAnimationListener compassAnimationListener) { + this.compassAnimationListener = compassAnimationListener; + } + + public void isAnimating(boolean isAnimating) { + this.isAnimating = isAnimating; } private void resetAnimation() { @@ -97,11 +96,6 @@ public final class CompassView extends AppCompatImageView implements Runnable, F } } - @Nullable - PointF getFocalPoint() { - return focalPoint; - } - /** * Updates the direction of the compass. * @@ -126,6 +120,7 @@ public final class CompassView extends AppCompatImageView implements Runnable, F setVisibility(View.VISIBLE); } + notifyCompassAnimationListenerWhenAnimating(); setRotation(rotation); } @@ -157,7 +152,8 @@ public final class CompassView extends AppCompatImageView implements Runnable, F @Override public void run() { - if (isFacingNorth() && fadeCompassViewFacingNorth) { + if (isHidden()) { + compassAnimationListener.onCompassAnimationFinished(); resetAnimation(); setLayerType(View.LAYER_TYPE_HARDWARE, null); fadeAnimator = ViewCompat.animate(CompassView.this).alpha(0.0f).setDuration(TIME_FADE_ANIMATION); @@ -172,34 +168,9 @@ public final class CompassView extends AppCompatImageView implements Runnable, F } } - @Override - public void onFocalPointChanged(PointF pointF) { - focalPoint = pointF; - } - - static class CompassClickListener implements View.OnClickListener { - - private WeakReference<MapboxMap> mapboxMap; - private WeakReference<CompassView> compassView; - - CompassClickListener(final MapboxMap mapboxMap, CompassView compassView) { - this.mapboxMap = new WeakReference<>(mapboxMap); - this.compassView = new WeakReference<>(compassView); - } - - @Override - public void onClick(View view) { - 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); - } - compassView.postDelayed(compassView, TIME_WAIT_IDLE + TIME_MAP_NORTH_ANIMATION); - } + private void notifyCompassAnimationListenerWhenAnimating() { + if (isAnimating) { + compassAnimationListener.onCompassAnimation(); } } } 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 f74286705c..983ba2550f 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 @@ -1,6 +1,7 @@ package com.mapbox.mapboxsdk.maps.widgets; import android.animation.ValueAnimator; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Camera; import android.graphics.Canvas; @@ -48,7 +49,10 @@ import timber.log.Timber; * <p> * Use {@link MyLocationViewSettings} to manipulate the state of this view. * </p> + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ +@Deprecated public class MyLocationView extends View { private static final int UNDEFINED_TINT_COLOR = -1; @@ -71,6 +75,7 @@ public class MyLocationView extends View { private float accuracy; private Paint accuracyPaint; + private float accuracyThreshold; private ValueAnimator locationChangeAnimator; private ValueAnimator accuracyAnimator; @@ -592,6 +597,16 @@ public class MyLocationView extends View { } /** + * Set accuracy circle threshold. Circle won't be displayed if accuracy is below set value. + * For internal use only. + * + * @param accuracyThreshold Value below which circle won't be displayed + */ + public void setAccuracyThreshold(float accuracyThreshold) { + this.accuracyThreshold = accuracyThreshold; + } + + /** * Set the bearing tracking mode, for internal use only. * * @param myBearingTrackingMode The bearing tracking mode @@ -764,6 +779,7 @@ public class MyLocationView extends View { locationSource = new WeakReference<>(locationEngine); } + @SuppressLint("MissingPermission") @Override public void onConnected() { MyLocationView locationView = userLocationView.get(); @@ -952,10 +968,11 @@ public class MyLocationView extends View { accuracyAnimator.end(); } - accuracyAnimator = ValueAnimator.ofFloat(accuracy, location.getAccuracy()); + float newAccuracy = location.getAccuracy() >= accuracyThreshold ? location.getAccuracy() : 0f; + accuracyAnimator = ValueAnimator.ofFloat(accuracy, newAccuracy); accuracyAnimator.setDuration(750); accuracyAnimator.start(); - accuracy = location.getAccuracy(); + accuracy = newAccuracy; } abstract void 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 fe2f18e4dd..a1d5b13b8b 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 @@ -13,7 +13,10 @@ import com.mapbox.mapboxsdk.maps.Projection; /** * Settings to configure the visual appearance of the MyLocationView. + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ +@Deprecated public class MyLocationViewSettings { private Projection projection; @@ -51,6 +54,7 @@ public class MyLocationViewSettings { // private int accuracyAlpha; + private float accuracyThreshold = 0f; @ColorInt private int accuracyTintColor; @@ -93,6 +97,7 @@ public class MyLocationViewSettings { setBackgroundTintColor(options.getMyLocationBackgroundTintColor()); setAccuracyAlpha(options.getMyLocationAccuracyAlpha()); setAccuracyTintColor(options.getMyLocationAccuracyTintColor()); + setAccuracyThreshold(options.getMyLocationAccuracyThreshold()); } /** @@ -293,6 +298,25 @@ public class MyLocationViewSettings { myLocationView.setAccuracyTint(accuracyTintColor); } + /** + * Returns current accuracy threshold value (in meters). + * + * @return Value of accuracy threshold (in meters), below which circle won't be displayed + */ + public float getAccuracyThreshold() { + return accuracyThreshold; + } + + /** + * Set accuracy circle threshold. Circle won't be displayed if accuracy is below set value. + * + * @param accuracyThreshold Value of accuracy (in meters), below which circle won't be displayed + */ + public void setAccuracyThreshold(float accuracyThreshold) { + this.accuracyThreshold = accuracyThreshold; + myLocationView.setAccuracyThreshold(accuracyThreshold); + } + public void setTilt(double tilt) { myLocationView.setTilt(tilt); } |