diff options
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps')
20 files changed, 8910 insertions, 8693 deletions
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 eb90ae764b..bffcf2fa2f 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 @@ -30,656 +30,659 @@ import java.util.List; * Responsible for referencing {@link InfoWindowManager} and {@link MarkerViewManager}. * </p> * <p> - * Exposes convenience methods to add/remove/update all subtypes of annotations found in com.mapbox.mapboxsdk.annotations. + * Exposes convenience methods to add/remove/update all subtypes of annotations found in + * com.mapbox.mapboxsdk.annotations. * </p> */ class AnnotationManager { - 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 List<Marker> selectedMarkers = new ArrayList<>(); - - private MapboxMap mapboxMap; - - private HashMap<MarkerView, MarkerViewManager.OnMarkerViewAddedListener> markerMap = new HashMap<>(); - private MapboxMap.OnMarkerClickListener onMarkerClickListener; - - AnnotationManager(NativeMapView view, MapView mapView, MarkerViewManager markerViewManager) { - this.nativeMapView = view; - this.mapView = mapView; - this.iconManager = new IconManager(nativeMapView); - this.markerViewManager = markerViewManager; - if (view != null) { - // null checking needed for unit tests - nativeMapView.addOnMapChangedListener(markerViewManager); + 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 List<Marker> selectedMarkers = new ArrayList<>(); + + private MapboxMap mapboxMap; + + private HashMap<MarkerView, MarkerViewManager.OnMarkerViewAddedListener> markerMap = new HashMap<>(); + private MapboxMap.OnMarkerClickListener onMarkerClickListener; + + AnnotationManager(NativeMapView view, MapView mapView, MarkerViewManager markerViewManager) { + this.nativeMapView = view; + this.mapView = mapView; + this.iconManager = new IconManager(nativeMapView); + this.markerViewManager = markerViewManager; + if (view != null) { + // null checking needed for unit tests + nativeMapView.addOnMapChangedListener(markerViewManager); + } + } + + // TODO refactor MapboxMap out for Projection and Transform + // Requires removing MapboxMap from Annotations by using Peer model from #6912 + AnnotationManager bind(MapboxMap mapboxMap) { + this.mapboxMap = mapboxMap; + this.markerViewManager.bind(mapboxMap); + return this; + } + + // + // Annotations + // + + Annotation getAnnotation(long id) { + return annotations.get(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; + } + + void removeAnnotation(@NonNull Annotation annotation) { + if (annotation instanceof Marker) { + Marker marker = (Marker) annotation; + marker.hideInfoWindow(); + if (marker instanceof MarkerView) { + markerViewManager.removeMarkerView((MarkerView) marker); + } + } + 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); + } + + 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); + if (annotation instanceof Marker) { + Marker marker = (Marker) annotation; + marker.hideInfoWindow(); + if (marker instanceof MarkerView) { + markerViewManager.removeMarkerView((MarkerView) marker); } + } + ids[i] = annotationList.get(i).getId(); } - // TODO refactor MapboxMap out for Projection and Transform - // Requires removing MapboxMap from Annotations by using Peer model from #6912 - AnnotationManager bind(MapboxMap mapboxMap) { - this.mapboxMap = mapboxMap; - this.markerViewManager.bind(mapboxMap); - return this; + if (nativeMapView != null) { + nativeMapView.removeAnnotations(ids); } - // - // Annotations - // - - Annotation getAnnotation(long id) { - return annotations.get(id); + for (long id : ids) { + annotations.remove(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))); + void removeAnnotations() { + Annotation annotation; + int count = annotations.size(); + long[] ids = new long[count]; + for (int i = 0; i < count; i++) { + ids[i] = annotations.keyAt(i); + annotation = annotations.get(ids[i]); + if (annotation instanceof Marker) { + Marker marker = (Marker) annotation; + marker.hideInfoWindow(); + if (marker instanceof MarkerView) { + markerViewManager.removeMarkerView((MarkerView) marker); } - return annotations; + } } - void removeAnnotation(@NonNull Annotation annotation) { - if (annotation instanceof Marker) { - Marker marker = (Marker) annotation; - marker.hideInfoWindow(); - if (marker instanceof MarkerView) { - markerViewManager.removeMarkerView((MarkerView) marker); - } - } - long id = annotation.getId(); - if (nativeMapView != null) { - nativeMapView.removeAnnotation(id); - } - annotations.remove(id); + if (nativeMapView != null) { + nativeMapView.removeAnnotations(ids); } - void removeAnnotation(long id) { - if (nativeMapView != null) { - nativeMapView.removeAnnotation(id); - } - annotations.remove(id); - } - - 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); - if (annotation instanceof Marker) { - Marker marker = (Marker) annotation; - marker.hideInfoWindow(); - if (marker instanceof MarkerView) { - markerViewManager.removeMarkerView((MarkerView) marker); - } - } - ids[i] = annotationList.get(i).getId(); - } - - if (nativeMapView != null) { - nativeMapView.removeAnnotations(ids); - } + annotations.clear(); + } - for (long id : ids) { - annotations.remove(id); - } - } + // + // Markers + // - void removeAnnotations() { - Annotation annotation; - int count = annotations.size(); - long[] ids = new long[count]; - for (int i = 0; i < count; i++) { - ids[i] = annotations.keyAt(i); - annotation = annotations.get(ids[i]); - if (annotation instanceof Marker) { - Marker marker = (Marker) annotation; - marker.hideInfoWindow(); - if (marker instanceof MarkerView) { - markerViewManager.removeMarkerView((MarkerView) marker); - } - } - } + List<Marker> addMarkers(@NonNull List<? extends BaseMarkerOptions> markerOptionsList, @NonNull MapboxMap mapboxMap) { + int count = markerOptionsList.size(); + List<Marker> markers = new ArrayList<>(count); + if (count > 0) { + BaseMarkerOptions markerOptions; + Marker marker; + for (int i = 0; i < count; i++) { + markerOptions = markerOptionsList.get(i); + marker = prepareMarker(markerOptions); + markers.add(marker); + } + if (markers.size() > 0) { + long[] ids = null; if (nativeMapView != null) { - nativeMapView.removeAnnotations(ids); - } - - annotations.clear(); - } - - // - // Markers - // - - Marker addMarker(@NonNull BaseMarkerOptions markerOptions, @NonNull MapboxMap mapboxMap) { - Marker marker = prepareMarker(markerOptions); - long id = nativeMapView != null ? nativeMapView.addMarker(marker) : 0; - marker.setMapboxMap(mapboxMap); - marker.setId(id); - annotations.put(id, marker); - return marker; + ids = nativeMapView.addMarkers(markers); + } + + long id = 0; + Marker m; + for (int i = 0; i < markers.size(); i++) { + m = markers.get(i); + m.setMapboxMap(mapboxMap); + if (ids != null) { + id = ids[i]; + } else { + //unit test + id++; + } + m.setId(id); + annotations.put(id, m); + } + + } + } + return markers; + } + + private Marker prepareMarker(BaseMarkerOptions markerOptions) { + Marker marker = markerOptions.getMarker(); + Icon icon = iconManager.loadIconForMarker(marker); + marker.setTopOffsetPixels(iconManager.getTopOffsetPixelsForIcon(icon)); + return marker; + } + + 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; + } + + MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions, @NonNull MapboxMap mapboxMap) { + MarkerView marker = prepareViewMarker(markerOptions); + marker.setMapboxMap(mapboxMap); + long id = nativeMapView.addMarker(marker); + marker.setId(id); + annotations.put(id, marker); + return marker; + } + + public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions, @NonNull MapboxMap mapboxMap, + final 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); + + markerViewManager.addOnMarkerViewAddedListener(marker, onMarkerViewAddedListener); + markerViewManager.setWaitingForRenderInvoke(true); + return marker; + } + + + 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.invalidateViewMarkersInVisibleRegion(); + return markers; + } - 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 = null; - if (nativeMapView != null) { - ids = nativeMapView.addMarkers(markers); - } - - long id = 0; - Marker m; - for (int i = 0; i < markers.size(); i++) { - m = markers.get(i); - m.setMapboxMap(mapboxMap); - if (ids != null) { - id = ids[i]; - } else { - //unit test - id++; - } - m.setId(id); - annotations.put(id, m); - } - - } - } - return markers; - } + private MarkerView prepareViewMarker(BaseMarkerViewOptions markerViewOptions) { + MarkerView marker = markerViewOptions.getMarker(); + iconManager.loadIconForMarkerView(marker); + return marker; + } - private Marker prepareMarker(BaseMarkerOptions markerOptions) { - Marker marker = markerOptions.getMarker(); - Icon icon = iconManager.loadIconForMarker(marker); - marker.setTopOffsetPixels(iconManager.getTopOffsetPixelsForIcon(icon)); - return marker; + void updateMarker(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap) { + if (updatedMarker == null) { + return; } - MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions, @NonNull MapboxMap mapboxMap) { - MarkerView marker = prepareViewMarker(markerOptions); - marker.setMapboxMap(mapboxMap); - long id = nativeMapView.addMarker(marker); - marker.setId(id); - annotations.put(id, marker); - return marker; + if (updatedMarker.getId() == -1) { + return; } - public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions, @NonNull MapboxMap mapboxMap, final 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); - - markerViewManager.addOnMarkerViewAddedListener(marker, onMarkerViewAddedListener); - markerViewManager.setWaitingForRenderInvoke(true); - return marker; + if (!(updatedMarker instanceof MarkerView)) { + iconManager.ensureIconLoaded(updatedMarker, mapboxMap); } + nativeMapView.updateMarker(updatedMarker); - 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.invalidateViewMarkersInVisibleRegion(); - return markers; + int index = annotations.indexOfKey(updatedMarker.getId()); + if (index > -1) { + annotations.setValueAt(index, updatedMarker); } + } - private MarkerView prepareViewMarker(BaseMarkerViewOptions markerViewOptions) { - MarkerView marker = markerViewOptions.getMarker(); - iconManager.loadIconForMarkerView(marker); - return marker; + 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); + } } + return markers; + } - void updateMarker(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap) { - if (updatedMarker == null) { - return; - } - - if (updatedMarker.getId() == -1) { - return; - } + void setOnMarkerClickListener(@Nullable MapboxMap.OnMarkerClickListener listener) { + onMarkerClickListener = listener; + } - if (!(updatedMarker instanceof MarkerView)) { - iconManager.ensureIconLoaded(updatedMarker, mapboxMap); - } - - nativeMapView.updateMarker(updatedMarker); - - int index = annotations.indexOfKey(updatedMarker.getId()); - if (index > -1) { - annotations.setValueAt(index, updatedMarker); - } + void selectMarker(@NonNull Marker marker) { + if (selectedMarkers.contains(marker)) { + return; } - 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); - } - } - return markers; + // Need to deselect any currently selected annotation first + if (!infoWindowManager.isAllowConcurrentMultipleOpenInfoWindows()) { + deselectMarkers(); } - void setOnMarkerClickListener(@Nullable MapboxMap.OnMarkerClickListener listener) { - onMarkerClickListener = listener; + if (marker instanceof MarkerView) { + markerViewManager.select((MarkerView) marker, false); + markerViewManager.ensureInfoWindowOffset((MarkerView) marker); } - void selectMarker(@NonNull Marker marker) { - if (selectedMarkers.contains(marker)) { - return; - } - - // Need to deselect any currently selected annotation first - if (!infoWindowManager.isAllowConcurrentMultipleOpenInfoWindows()) { - deselectMarkers(); - } - - if (marker instanceof MarkerView) { - markerViewManager.select((MarkerView) marker, false); - markerViewManager.ensureInfoWindowOffset((MarkerView) marker); - } + if (infoWindowManager.isInfoWindowValidForMarker(marker) || infoWindowManager.getInfoWindowAdapter() != null) { + infoWindowManager.add(marker.showInfoWindow(mapboxMap, mapView)); + } - if (infoWindowManager.isInfoWindowValidForMarker(marker) || infoWindowManager.getInfoWindowAdapter() != null) { - infoWindowManager.add(marker.showInfoWindow(mapboxMap, mapView)); - } + // only add to selected markers if user didn't handle the click event themselves #3176 + selectedMarkers.add(marker); + } - // only add to selected markers if user didn't handle the click event themselves #3176 - selectedMarkers.add(marker); + void deselectMarkers() { + if (selectedMarkers.isEmpty()) { + return; } - void deselectMarkers() { - if (selectedMarkers.isEmpty()) { - return; - } + for (Marker marker : selectedMarkers) { + if (marker.isInfoWindowShown()) { + marker.hideInfoWindow(); + } - for (Marker marker : selectedMarkers) { - if (marker.isInfoWindowShown()) { - marker.hideInfoWindow(); - } - - if (marker instanceof MarkerView) { - markerViewManager.deselect((MarkerView) marker, false); - } - } - - // Removes all selected markers from the list - selectedMarkers.clear(); + if (marker instanceof MarkerView) { + markerViewManager.deselect((MarkerView) marker, false); + } } - void deselectMarker(@NonNull Marker marker) { - if (!selectedMarkers.contains(marker)) { - return; - } - - if (marker.isInfoWindowShown()) { - marker.hideInfoWindow(); - } + // Removes all selected markers from the list + selectedMarkers.clear(); + } - if (marker instanceof MarkerView) { - markerViewManager.deselect((MarkerView) marker, false); - } + void deselectMarker(@NonNull Marker marker) { + if (!selectedMarkers.contains(marker)) { + return; + } - selectedMarkers.remove(marker); + if (marker.isInfoWindowShown()) { + marker.hideInfoWindow(); } - List<Marker> getSelectedMarkers() { - return selectedMarkers; + if (marker instanceof MarkerView) { + markerViewManager.deselect((MarkerView) marker, false); } - public List<Marker> getMarkersInRect(@NonNull RectF rectangle) { - // convert Rectangle to be density depedent - float pixelRatio = nativeMapView.getPixelRatio(); - RectF rect = new RectF(rectangle.left / pixelRatio, - rectangle.top / pixelRatio, - rectangle.right / pixelRatio, - rectangle.bottom / pixelRatio); + selectedMarkers.remove(marker); + } - long[] ids = nativeMapView.queryPointAnnotations(rect); + List<Marker> getSelectedMarkers() { + return selectedMarkers; + } - List<Long> idsList = new ArrayList<>(ids.length); - for (long id : ids) { - idsList.add(id); - } + public List<Marker> getMarkersInRect(@NonNull RectF rectangle) { + // convert Rectangle to be density depedent + float pixelRatio = nativeMapView.getPixelRatio(); + RectF rect = new RectF(rectangle.left / pixelRatio, + rectangle.top / pixelRatio, + rectangle.right / pixelRatio, + rectangle.bottom / pixelRatio); - 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); - } - } + long[] ids = nativeMapView.queryPointAnnotations(rect); - return new ArrayList<>(annotations); + List<Long> idsList = new ArrayList<>(ids.length); + for (long id : ids) { + idsList.add(id); } - public 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); + 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); + } + } - long[] ids = nativeMapView.queryPointAnnotations(rect); + return new ArrayList<>(annotations); + } - List<Long> idsList = new ArrayList<>(ids.length); - for (long id : ids) { - idsList.add(id); - } + public 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); - 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); - } - } + long[] ids = nativeMapView.queryPointAnnotations(rect); - return new ArrayList<>(annotations); + List<Long> idsList = new ArrayList<>(ids.length); + for (long id : ids) { + idsList.add(id); } - // - // Polygons - // - - Polygon addPolygon(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) { - Polygon polygon = polygonOptions.getPolygon(); - if (!polygon.getPoints().isEmpty()) { - long id = nativeMapView != null ? nativeMapView.addPolygon(polygon) : 0; - polygon.setId(id); - polygon.setMapboxMap(mapboxMap); - annotations.put(id, polygon); - } - return polygon; + List<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); + } } - 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); - } - } + return new ArrayList<>(annotations); + } - long[] ids = null; - if (nativeMapView != null) { - ids = nativeMapView.addPolygons(polygons); - } + // + // Polygons + // - long id = 0; - for (int i = 0; i < polygons.size(); i++) { - polygon = polygons.get(i); - polygon.setMapboxMap(mapboxMap); - if (ids != null) { - id = ids[i]; - } else { - // unit test - id++; - } - polygon.setId(id); - annotations.put(id, polygon); - } - } - return polygons; + Polygon addPolygon(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) { + Polygon polygon = polygonOptions.getPolygon(); + if (!polygon.getPoints().isEmpty()) { + long id = nativeMapView != null ? nativeMapView.addPolygon(polygon) : 0; + polygon.setId(id); + polygon.setMapboxMap(mapboxMap); + annotations.put(id, polygon); } + return polygon; + } - void updatePolygon(Polygon polygon) { - if (polygon == null) { - return; - } + List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap) { + int count = polygonOptionsList.size(); - if (polygon.getId() == -1) { - return; + 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); } + } - nativeMapView.updatePolygon(polygon); + long[] ids = null; + if (nativeMapView != null) { + ids = nativeMapView.addPolygons(polygons); + } - int index = annotations.indexOfKey(polygon.getId()); - if (index > -1) { - annotations.setValueAt(index, polygon); + long id = 0; + for (int i = 0; i < polygons.size(); i++) { + polygon = polygons.get(i); + polygon.setMapboxMap(mapboxMap); + if (ids != null) { + id = ids[i]; + } else { + // unit test + id++; } + polygon.setId(id); + annotations.put(id, polygon); + } } + return polygons; + } - 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; + void updatePolygon(Polygon polygon) { + if (polygon == null) { + return; } - // - // Polylines - // - - Polyline addPolyline(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) { - Polyline polyline = polylineOptions.getPolyline(); - if (!polyline.getPoints().isEmpty()) { - long id = nativeMapView != null ? nativeMapView.addPolyline(polyline) : 0; - polyline.setMapboxMap(mapboxMap); - polyline.setId(id); - annotations.put(id, polyline); - } - return polyline; + if (polygon.getId() == -1) { + return; } - List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap) { - int count = polylineOptionsList.size(); - Polyline polyline; - List<Polyline> polylines = new ArrayList<>(count); + nativeMapView.updatePolygon(polygon); - if (count > 0) { - for (PolylineOptions options : polylineOptionsList) { - polyline = options.getPolyline(); - if (!polyline.getPoints().isEmpty()) { - polylines.add(polyline); - } - } - - long[] ids = null; - if (nativeMapView != null) { - ids = nativeMapView.addPolylines(polylines); - } - - long id = 0; - Polyline p; - - for (int i = 0; i < polylines.size(); i++) { - p = polylines.get(i); - p.setMapboxMap(mapboxMap); - if (ids != null) { - id = ids[i]; - } else { - // unit test - id++; - } - p.setId(id); - annotations.put(id, p); - } - } - return polylines; + int index = annotations.indexOfKey(polygon.getId()); + if (index > -1) { + annotations.setValueAt(index, polygon); } + } - void updatePolyline(Polyline polyline) { - if (polyline == null) { - return; - } - - if (polyline.getId() == -1) { - return; - } - - nativeMapView.updatePolyline(polyline); - - int index = annotations.indexOfKey(polyline.getId()); - if (index > -1) { - annotations.setValueAt(index, polyline); - } + 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; + } - 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; - } + // + // Polylines + // - InfoWindowManager getInfoWindowManager() { - return infoWindowManager; + Polyline addPolyline(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) { + Polyline polyline = polylineOptions.getPolyline(); + if (!polyline.getPoints().isEmpty()) { + long id = nativeMapView != null ? nativeMapView.addPolyline(polyline) : 0; + polyline.setMapboxMap(mapboxMap); + polyline.setId(id); + annotations.put(id, polyline); } + return polyline; + } - MarkerViewManager getMarkerViewManager() { - return markerViewManager; - } - - void adjustTopOffsetPixels(MapboxMap mapboxMap) { - int count = annotations.size(); - for (int i = 0; i < count; i++) { - Annotation annotation = annotations.get(i); - if (annotation instanceof Marker) { - Marker marker = (Marker) annotation; - marker.setTopOffsetPixels( - iconManager.getTopOffsetPixelsForIcon(marker.getIcon())); - } - } + List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap) { + int count = polylineOptionsList.size(); + Polyline polyline; + List<Polyline> polylines = new ArrayList<>(count); - for (Marker marker : selectedMarkers) { - if (marker.isInfoWindowShown()) { - marker.hideInfoWindow(); - marker.showInfoWindow(mapboxMap, mapView); - } + if (count > 0) { + for (PolylineOptions options : polylineOptionsList) { + polyline = options.getPolyline(); + if (!polyline.getPoints().isEmpty()) { + polylines.add(polyline); } - } + } + + long[] ids = null; + if (nativeMapView != null) { + ids = nativeMapView.addPolylines(polylines); + } + + long id = 0; + Polyline p; + + for (int i = 0; i < polylines.size(); i++) { + p = polylines.get(i); + p.setMapboxMap(mapboxMap); + if (ids != null) { + id = ids[i]; + } else { + // unit test + id++; + } + p.setId(id); + annotations.put(id, p); + } + } + return polylines; + } + + void updatePolyline(Polyline polyline) { + if (polyline == null) { + return; + } + + if (polyline.getId() == -1) { + return; + } + + nativeMapView.updatePolyline(polyline); + + int index = annotations.indexOfKey(polyline.getId()); + if (index > -1) { + annotations.setValueAt(index, 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; + } + + MarkerViewManager getMarkerViewManager() { + return markerViewManager; + } + + void adjustTopOffsetPixels(MapboxMap mapboxMap) { + int count = annotations.size(); + for (int i = 0; i < count; i++) { + Annotation annotation = annotations.get(i); + if (annotation instanceof Marker) { + Marker marker = (Marker) annotation; + marker.setTopOffsetPixels( + iconManager.getTopOffsetPixelsForIcon(marker.getIcon())); + } + } + + for (Marker marker : selectedMarkers) { + if (marker.isInfoWindowShown()) { + marker.hideInfoWindow(); + marker.showInfoWindow(mapboxMap, mapView); + } + } + } + + 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); + } + } + } + + // + // Click event + // + + boolean onTap(PointF tapPoint, float screenDensity) { + float toleranceSides = 4 * screenDensity; + float toleranceTopBottom = 10 * screenDensity; + + RectF tapRect = new RectF(tapPoint.x - iconManager.getAverageIconWidth() / 2 - toleranceSides, + tapPoint.y - iconManager.getAverageIconHeight() / 2 - toleranceTopBottom, + tapPoint.x + iconManager.getAverageIconWidth() / 2 + toleranceSides, + tapPoint.y + iconManager.getAverageIconHeight() / 2 + toleranceTopBottom); + + List<Marker> nearbyMarkers = getMarkersInRect(tapRect); + long newSelectedMarkerId = -1; + + if (nearbyMarkers != null && nearbyMarkers.size() > 0) { + Collections.sort(nearbyMarkers); + for (Marker nearbyMarker : nearbyMarkers) { + boolean found = false; + for (Marker selectedMarker : selectedMarkers) { + if (selectedMarker.equals(nearbyMarker)) { + found = true; + } + } + if (!found) { + newSelectedMarkerId = nearbyMarker.getId(); + break; + } + } + } + + if (newSelectedMarkerId >= 0) { + List<Annotation> annotations = getAnnotations(); + int count = annotations.size(); + for (int i = 0; i < count; i++) { + Annotation annotation = annotations.get(i); + if (annotation instanceof Marker) { + if (annotation.getId() == newSelectedMarkerId) { + Marker marker = (Marker) annotation; + boolean handledDefaultClick = false; - 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); + if (marker instanceof MarkerView) { + handledDefaultClick = markerViewManager.onClickMarkerView((MarkerView) marker); + } else { + if (onMarkerClickListener != null) { + // end developer has provided a custom click listener + handledDefaultClick = onMarkerClickListener.onMarkerClick(marker); + } } - } - } - // - // Click event - // - - boolean onTap(PointF tapPoint, float screenDensity) { - float toleranceSides = 4 * screenDensity; - float toleranceTopBottom = 10 * screenDensity; - - RectF tapRect = new RectF(tapPoint.x - iconManager.getAverageIconWidth() / 2 - toleranceSides, - tapPoint.y - iconManager.getAverageIconHeight() / 2 - toleranceTopBottom, - tapPoint.x + iconManager.getAverageIconWidth() / 2 + toleranceSides, - tapPoint.y + iconManager.getAverageIconHeight() / 2 + toleranceTopBottom); - - List<Marker> nearbyMarkers = getMarkersInRect(tapRect); - long newSelectedMarkerId = -1; - - if (nearbyMarkers != null && nearbyMarkers.size() > 0) { - Collections.sort(nearbyMarkers); - for (Marker nearbyMarker : nearbyMarkers) { - boolean found = false; - for (Marker selectedMarker : selectedMarkers) { - if (selectedMarker.equals(nearbyMarker)) { - found = true; - } - } - if (!found) { - newSelectedMarkerId = nearbyMarker.getId(); - break; - } + if (annotation instanceof MarkerView) { + markerViewManager.onClickMarkerView((MarkerView) annotation); + } else { + if (!handledDefaultClick) { + // only select marker if user didn't handle the click event themselves + selectMarker(marker); + } } - } - if (newSelectedMarkerId >= 0) { - List<Annotation> annotations = getAnnotations(); - int count = annotations.size(); - for (int i = 0; i < count; i++) { - Annotation annotation = annotations.get(i); - if (annotation instanceof Marker) { - if (annotation.getId() == newSelectedMarkerId) { - Marker marker = (Marker) annotation; - boolean handledDefaultClick = false; - - if (marker instanceof MarkerView) { - handledDefaultClick = markerViewManager.onClickMarkerView((MarkerView) marker); - } else { - if (onMarkerClickListener != null) { - // end developer has provided a custom click listener - handledDefaultClick = onMarkerClickListener.onMarkerClick(marker); - } - } - - if (annotation instanceof MarkerView) { - markerViewManager.onClickMarkerView((MarkerView) annotation); - } else { - if (!handledDefaultClick) { - // only select marker if user didn't handle the click event themselves - selectMarker(marker); - } - } - - return true; - } - } - } + return true; + } } - return false; + } } + return false; + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/FocalPointChangeListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/FocalPointChangeListener.java index 5f20155119..006122a4e2 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/FocalPointChangeListener.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/FocalPointChangeListener.java @@ -4,5 +4,5 @@ import android.graphics.PointF; public interface FocalPointChangeListener { - void onFocalPointChanged(PointF pointF); + void onFocalPointChanged(PointF pointF); } 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 71a9d4a2da..c9d81a88bc 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 @@ -26,133 +26,133 @@ import java.util.List; */ class IconManager { - private NativeMapView nativeMapView; - private List<Icon> icons; - - private int averageIconHeight; - private int averageIconWidth; - - 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)); + private NativeMapView nativeMapView; + private List<Icon> icons; + + private int averageIconHeight; + private int averageIconWidth; + + 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)); + } + + Icon loadIconForMarker(Marker marker) { + Icon icon = marker.getIcon(); + + // calculating average before adding + int iconSize = icons.size() + 1; + + // TODO replace former if case with anchor implementation, + // current workaround for having extra pixels is diving height by 2 + if (icon == null) { + icon = IconFactory.getInstance(nativeMapView.getContext()).defaultMarker(); + Bitmap bitmap = icon.getBitmap(); + averageIconHeight = averageIconHeight + (bitmap.getHeight() / 2 - averageIconHeight) / iconSize; + averageIconWidth = averageIconWidth + (bitmap.getWidth() - averageIconWidth) / iconSize; + marker.setIcon(icon); + } else { + Bitmap bitmap = icon.getBitmap(); + averageIconHeight = averageIconHeight + (bitmap.getHeight() - averageIconHeight) / iconSize; + averageIconWidth = averageIconWidth + (bitmap.getWidth() - averageIconWidth) / iconSize; } - Icon loadIconForMarker(Marker marker) { - Icon icon = marker.getIcon(); - - // calculating average before adding - int iconSize = icons.size() + 1; - - // TODO replace former if case with anchor implementation, - // current workaround for having extra pixels is diving height by 2 - if (icon == null) { - icon = IconFactory.getInstance(nativeMapView.getContext()).defaultMarker(); - Bitmap bitmap = icon.getBitmap(); - averageIconHeight = averageIconHeight + (bitmap.getHeight() / 2 - averageIconHeight) / iconSize; - averageIconWidth = averageIconWidth + (bitmap.getWidth() - averageIconWidth) / iconSize; - marker.setIcon(icon); - } else { - Bitmap bitmap = icon.getBitmap(); - averageIconHeight = averageIconHeight + (bitmap.getHeight() - averageIconHeight) / iconSize; - averageIconWidth = averageIconWidth + (bitmap.getWidth() - averageIconWidth) / iconSize; - } - - if (!icons.contains(icon)) { - icons.add(icon); - loadIcon(icon); - } else { - Icon oldIcon = icons.get(icons.indexOf(icon)); - if (!oldIcon.getBitmap().sameAs(icon.getBitmap())) { - throw new IconBitmapChangedException(); - } - } - return icon; + if (!icons.contains(icon)) { + icons.add(icon); + loadIcon(icon); + } else { + Icon oldIcon = icons.get(icons.indexOf(icon)); + if (!oldIcon.getBitmap().sameAs(icon.getBitmap())) { + throw new IconBitmapChangedException(); + } } - - Icon loadIconForMarkerView(MarkerView marker) { - Icon icon = marker.getIcon(); - int iconSize = icons.size() + 1; - if (icon == null) { - icon = IconFactory.getInstance(nativeMapView.getContext()).defaultMarkerView(); - marker.setIcon(icon); - } - Bitmap bitmap = icon.getBitmap(); - averageIconHeight = averageIconHeight + (bitmap.getHeight() - averageIconHeight) / iconSize; - averageIconWidth = averageIconWidth + (bitmap.getWidth() - averageIconWidth) / iconSize; - if (!icons.contains(icon)) { - icons.add(icon); - } else { - Icon oldIcon = icons.get(icons.indexOf(icon)); - if (!oldIcon.getBitmap().sameAs(icon.getBitmap())) { - throw new IconBitmapChangedException(); - } - } - return icon; + return icon; + } + + Icon loadIconForMarkerView(MarkerView marker) { + Icon icon = marker.getIcon(); + int iconSize = icons.size() + 1; + if (icon == null) { + icon = IconFactory.getInstance(nativeMapView.getContext()).defaultMarkerView(); + marker.setIcon(icon); } - - int getTopOffsetPixelsForIcon(Icon icon) { - return (int) (nativeMapView.getTopOffsetPixelsForAnnotationSymbol(icon.getId()) * nativeMapView.getPixelRatio()); + Bitmap bitmap = icon.getBitmap(); + averageIconHeight = averageIconHeight + (bitmap.getHeight() - averageIconHeight) / iconSize; + averageIconWidth = averageIconWidth + (bitmap.getWidth() - averageIconWidth) / iconSize; + if (!icons.contains(icon)) { + icons.add(icon); + } else { + Icon oldIcon = icons.get(icons.indexOf(icon)); + if (!oldIcon.getBitmap().sameAs(icon.getBitmap())) { + throw new IconBitmapChangedException(); + } } - - void loadIcon(Icon icon) { - Bitmap bitmap = icon.getBitmap(); - String id = icon.getId(); - if (bitmap.getConfig() != Bitmap.Config.ARGB_8888) { - bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false); - } - ByteBuffer buffer = ByteBuffer.allocate(bitmap.getRowBytes() * bitmap.getHeight()); - bitmap.copyPixelsToBuffer(buffer); - - float density = bitmap.getDensity(); - if (density == Bitmap.DENSITY_NONE) { - density = DisplayMetrics.DENSITY_DEFAULT; - } - float scale = density / DisplayMetrics.DENSITY_DEFAULT; - nativeMapView.addAnnotationIcon( - id, - bitmap.getWidth(), - bitmap.getHeight(), - scale, buffer.array()); + return icon; + } + + int getTopOffsetPixelsForIcon(Icon icon) { + return (int) (nativeMapView.getTopOffsetPixelsForAnnotationSymbol(icon.getId()) * nativeMapView.getPixelRatio()); + } + + void loadIcon(Icon icon) { + Bitmap bitmap = icon.getBitmap(); + String id = icon.getId(); + if (bitmap.getConfig() != Bitmap.Config.ARGB_8888) { + bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false); } + ByteBuffer buffer = ByteBuffer.allocate(bitmap.getRowBytes() * bitmap.getHeight()); + bitmap.copyPixelsToBuffer(buffer); - void reloadIcons() { - int count = icons.size(); - for (int i = 0; i < count; i++) { - Icon icon = icons.get(i); - loadIcon(icon); - } + float density = bitmap.getDensity(); + if (density == Bitmap.DENSITY_NONE) { + density = DisplayMetrics.DENSITY_DEFAULT; } - - void ensureIconLoaded(Marker marker, MapboxMap mapboxMap) { - Icon icon = marker.getIcon(); - if (icon == null) { - icon = IconFactory.getInstance(nativeMapView.getContext()).defaultMarker(); - marker.setIcon(icon); - } - if (!icons.contains(icon)) { - icons.add(icon); - loadIcon(icon); - } else { - Icon oldIcon = icons.get(icons.indexOf(icon)); - if (!oldIcon.getBitmap().sameAs(icon.getBitmap())) { - throw new IconBitmapChangedException(); - } - } - - // this seems to be a costly operation according to the profiler so I'm trying to save some calls - Marker previousMarker = marker.getId() != -1 ? (Marker) mapboxMap.getAnnotation(marker.getId()) : null; - if (previousMarker == null || previousMarker.getIcon() == null || previousMarker.getIcon() != marker.getIcon()) { - marker.setTopOffsetPixels(getTopOffsetPixelsForIcon(icon)); - } + float scale = density / DisplayMetrics.DENSITY_DEFAULT; + nativeMapView.addAnnotationIcon( + id, + bitmap.getWidth(), + bitmap.getHeight(), + scale, buffer.array()); + } + + void reloadIcons() { + int count = icons.size(); + for (int i = 0; i < count; i++) { + Icon icon = icons.get(i); + loadIcon(icon); } + } - int getAverageIconHeight() { - return averageIconHeight; + void ensureIconLoaded(Marker marker, MapboxMap mapboxMap) { + Icon icon = marker.getIcon(); + if (icon == null) { + icon = IconFactory.getInstance(nativeMapView.getContext()).defaultMarker(); + marker.setIcon(icon); + } + if (!icons.contains(icon)) { + icons.add(icon); + loadIcon(icon); + } else { + Icon oldIcon = icons.get(icons.indexOf(icon)); + if (!oldIcon.getBitmap().sameAs(icon.getBitmap())) { + throw new IconBitmapChangedException(); + } } - int getAverageIconWidth() { - return averageIconWidth; + // this seems to be a costly operation according to the profiler so I'm trying to save some calls + Marker previousMarker = marker.getId() != -1 ? (Marker) mapboxMap.getAnnotation(marker.getId()) : null; + if (previousMarker == null || previousMarker.getIcon() == null || previousMarker.getIcon() != marker.getIcon()) { + marker.setTopOffsetPixels(getTopOffsetPixelsForIcon(icon)); } + } + + int getAverageIconHeight() { + return averageIconHeight; + } + + int getAverageIconWidth() { + return averageIconWidth; + } } 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 791f310b4c..fc7d19c325 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 @@ -15,73 +15,74 @@ import java.util.List; * <p> * Maintains a {@link List} of opened {@link InfoWindow} and tracks configurations as * allowConcurrentMultipleInfoWindows which allows to have multiple {@link InfoWindow} open at the - * same time. Responsible for managing listeners as {@link com.mapbox.mapboxsdk.maps.MapboxMap.OnInfoWindowClickListener} + * same time. Responsible for managing listeners as + * {@link com.mapbox.mapboxsdk.maps.MapboxMap.OnInfoWindowClickListener} * and {@link com.mapbox.mapboxsdk.maps.MapboxMap.OnInfoWindowLongClickListener}. * </p> */ class InfoWindowManager { - private List<InfoWindow> infoWindows; - private MapboxMap.InfoWindowAdapter infoWindowAdapter; - private boolean allowConcurrentMultipleInfoWindows; + private List<InfoWindow> infoWindows; + private MapboxMap.InfoWindowAdapter infoWindowAdapter; + private boolean allowConcurrentMultipleInfoWindows; - private MapboxMap.OnInfoWindowClickListener onInfoWindowClickListener; - private MapboxMap.OnInfoWindowLongClickListener onInfoWindowLongClickListener; - private MapboxMap.OnInfoWindowCloseListener onInfoWindowCloseListener; + private MapboxMap.OnInfoWindowClickListener onInfoWindowClickListener; + private MapboxMap.OnInfoWindowLongClickListener onInfoWindowLongClickListener; + private MapboxMap.OnInfoWindowCloseListener onInfoWindowCloseListener; - InfoWindowManager() { - this.infoWindows = new ArrayList<>(); - } + InfoWindowManager() { + this.infoWindows = new ArrayList<>(); + } - void setInfoWindowAdapter(@Nullable MapboxMap.InfoWindowAdapter infoWindowAdapter) { - this.infoWindowAdapter = infoWindowAdapter; - } + void setInfoWindowAdapter(@Nullable MapboxMap.InfoWindowAdapter infoWindowAdapter) { + this.infoWindowAdapter = infoWindowAdapter; + } - MapboxMap.InfoWindowAdapter getInfoWindowAdapter() { - return infoWindowAdapter; - } + MapboxMap.InfoWindowAdapter getInfoWindowAdapter() { + return infoWindowAdapter; + } - void setAllowConcurrentMultipleOpenInfoWindows(boolean allow) { - allowConcurrentMultipleInfoWindows = allow; - } + void setAllowConcurrentMultipleOpenInfoWindows(boolean allow) { + allowConcurrentMultipleInfoWindows = allow; + } - boolean isAllowConcurrentMultipleOpenInfoWindows() { - return allowConcurrentMultipleInfoWindows; - } + boolean isAllowConcurrentMultipleOpenInfoWindows() { + return allowConcurrentMultipleInfoWindows; + } - List<InfoWindow> getInfoWindows() { - return infoWindows; - } + List<InfoWindow> getInfoWindows() { + return infoWindows; + } - boolean isInfoWindowValidForMarker(@NonNull Marker marker) { - return !TextUtils.isEmpty(marker.getTitle()) || !TextUtils.isEmpty(marker.getSnippet()); - } + boolean isInfoWindowValidForMarker(@NonNull Marker marker) { + return !TextUtils.isEmpty(marker.getTitle()) || !TextUtils.isEmpty(marker.getSnippet()); + } - void setOnInfoWindowClickListener(@Nullable MapboxMap.OnInfoWindowClickListener listener) { - onInfoWindowClickListener = listener; - } + void setOnInfoWindowClickListener(@Nullable MapboxMap.OnInfoWindowClickListener listener) { + onInfoWindowClickListener = listener; + } - MapboxMap.OnInfoWindowClickListener getOnInfoWindowClickListener() { - return onInfoWindowClickListener; - } + MapboxMap.OnInfoWindowClickListener getOnInfoWindowClickListener() { + return onInfoWindowClickListener; + } - void setOnInfoWindowLongClickListener(@Nullable MapboxMap.OnInfoWindowLongClickListener listener) { - onInfoWindowLongClickListener = listener; - } + void setOnInfoWindowLongClickListener(@Nullable MapboxMap.OnInfoWindowLongClickListener listener) { + onInfoWindowLongClickListener = listener; + } - MapboxMap.OnInfoWindowLongClickListener getOnInfoWindowLongClickListener() { - return onInfoWindowLongClickListener; - } + MapboxMap.OnInfoWindowLongClickListener getOnInfoWindowLongClickListener() { + return onInfoWindowLongClickListener; + } - void setOnInfoWindowCloseListener(@Nullable MapboxMap.OnInfoWindowCloseListener listener) { - onInfoWindowCloseListener = listener; - } + void setOnInfoWindowCloseListener(@Nullable MapboxMap.OnInfoWindowCloseListener listener) { + onInfoWindowCloseListener = listener; + } - MapboxMap.OnInfoWindowCloseListener getOnInfoWindowCloseListener() { - return onInfoWindowCloseListener; - } + MapboxMap.OnInfoWindowCloseListener getOnInfoWindowCloseListener() { + return onInfoWindowCloseListener; + } - public void add(InfoWindow infoWindow) { - infoWindows.add(infoWindow); - } + public void add(InfoWindow infoWindow) { + infoWindows.add(infoWindow); + } } 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 4524a59f0d..18b4c294e5 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 @@ -35,196 +35,197 @@ import com.mapbox.mapboxsdk.exceptions.InvalidAccessTokenException; */ public final class MapFragment extends Fragment { - private MapView map; - private OnMapReadyCallback onMapReadyCallback; - - /** - * Creates a MapFragment instance - * - * @param mapboxMapOptions The configuration options to be used. - * @return MapFragment created. - */ - public static MapFragment newInstance(@Nullable MapboxMapOptions mapboxMapOptions) { - MapFragment mapFragment = new MapFragment(); - Bundle bundle = new Bundle(); - bundle.putParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS, mapboxMapOptions); - mapFragment.setArguments(bundle); - return mapFragment; + private MapView map; + private OnMapReadyCallback onMapReadyCallback; + + /** + * Creates a MapFragment instance + * + * @param mapboxMapOptions The configuration options to be used. + * @return MapFragment created. + */ + public static MapFragment newInstance(@Nullable MapboxMapOptions mapboxMapOptions) { + MapFragment mapFragment = new MapFragment(); + Bundle bundle = new Bundle(); + bundle.putParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS, mapboxMapOptions); + mapFragment.setArguments(bundle); + return mapFragment; + } + + /** + * Creates the fragment view hierarchy. + * + * @param inflater Inflater used to inflate content. + * @param container The parent layout for the map fragment. + * @param savedInstanceState The saved instance state for the map fragment. + * @return The view created + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + Context context = inflater.getContext(); + MapboxMapOptions options = null; + + // Get bundle + Bundle bundle = getArguments(); + if (bundle != null && bundle.containsKey(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS)) { + options = bundle.getParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS); } - /** - * Creates the fragment view hierarchy. - * - * @param inflater Inflater used to inflate content. - * @param container The parent layout for the map fragment. - * @param savedInstanceState The saved instance state for the map fragment. - * @return The view created - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - super.onCreateView(inflater, container, savedInstanceState); - Context context = inflater.getContext(); - MapboxMapOptions options = null; - - // Get bundle - Bundle bundle = getArguments(); - if (bundle != null && bundle.containsKey(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS)) { - options = bundle.getParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS); - } - - // Assign an AccessToken if needed - if (options == null || options.getAccessToken() == null) { - String token = null; - if (MapboxAccountManager.getInstance() != null) { - token = MapboxAccountManager.getInstance().getAccessToken(); - } else { - token = getToken(inflater.getContext()); - } - if (TextUtils.isEmpty(token)) { - throw new InvalidAccessTokenException(); - } - if (options == null) { - options = new MapboxMapOptions().accessToken(token); - } else { - options.accessToken(token); - } - } - - Drawable foregroundDrawable = options.getMyLocationForegroundDrawable(); - Drawable foregroundBearingDrawable = options.getMyLocationForegroundBearingDrawable(); - if (foregroundDrawable == null || foregroundBearingDrawable == null) { - if (foregroundDrawable == null) { - foregroundDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_default); - } - if (foregroundBearingDrawable == null) { - foregroundBearingDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_bearing); - } - options.myLocationForegroundDrawables(foregroundDrawable, foregroundBearingDrawable); - } - - if (options.getMyLocationBackgroundDrawable() == null) { - options.myLocationBackgroundDrawable(ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_bg_shape)); - } - - return map = new MapView(inflater.getContext(), options); + // Assign an AccessToken if needed + if (options == null || options.getAccessToken() == null) { + String token = null; + if (MapboxAccountManager.getInstance() != null) { + token = MapboxAccountManager.getInstance().getAccessToken(); + } else { + token = getToken(inflater.getContext()); + } + if (TextUtils.isEmpty(token)) { + throw new InvalidAccessTokenException(); + } + if (options == null) { + options = new MapboxMapOptions().accessToken(token); + } else { + options.accessToken(token); + } } - /** - * <p> - * Returns the Mapbox access token set in the app resources. - * </p> - * It will first search the application manifest for a {@link MapboxConstants#KEY_META_DATA_MANIFEST} - * meta-data value. If not found it will then attempt to load the access token from the - * {@code res/raw/token.txt} development file. - * - * @param context The {@link Context} of the {@link android.app.Activity} or {@link android.app.Fragment}. - * @return The Mapbox access token or null if not found. - * @see MapboxConstants#KEY_META_DATA_MANIFEST - * - * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)} - */ - @Deprecated - private String getToken(@NonNull Context context) { - try { - // read out AndroidManifest - PackageManager packageManager = context.getPackageManager(); - ApplicationInfo appInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); - String token = appInfo.metaData.getString(MapboxConstants.KEY_META_DATA_MANIFEST); - if (token == null || token.isEmpty()) { - throw new IllegalArgumentException(); - } - return token; - } catch (Exception e) { - // use fallback on string resource, used for development - int tokenResId = context.getResources().getIdentifier("mapbox_access_token", "string", context.getPackageName()); - return tokenResId != 0 ? context.getString(tokenResId) : null; - } + Drawable foregroundDrawable = options.getMyLocationForegroundDrawable(); + Drawable foregroundBearingDrawable = options.getMyLocationForegroundBearingDrawable(); + if (foregroundDrawable == null || foregroundBearingDrawable == null) { + if (foregroundDrawable == null) { + foregroundDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_default); + } + if (foregroundBearingDrawable == null) { + foregroundBearingDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_bearing); + } + options.myLocationForegroundDrawables(foregroundDrawable, foregroundBearingDrawable); } - /** - * Called when the fragment view hierarchy is created. - * - * @param view The content view of the fragment - * @param savedInstanceState THe saved instance state of the framgnt - */ - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - map.onCreate(savedInstanceState); + if (options.getMyLocationBackgroundDrawable() == null) { + options.myLocationBackgroundDrawable(ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_bg_shape)); } - /** - * Called when the fragment is visible for the users. - */ - @Override - public void onStart() { - super.onStart(); - map.onStart(); - map.getMapAsync(onMapReadyCallback); - } - - /** - * Called when the fragment is ready to be interacted with. - */ - @Override - public void onResume() { - super.onResume(); - map.onResume(); - } - - /** - * Called when the fragment is pausing. - */ - @Override - public void onPause() { - super.onPause(); - map.onPause(); - } - - /** - * Called when the fragment state needs to be saved. - * - * @param outState The saved state - */ - @Override - public void onSaveInstanceState(@NonNull Bundle outState) { - super.onSaveInstanceState(outState); - map.onSaveInstanceState(outState); - } - - /** - * Called when the fragment is no longer visible for the user. - */ - @Override - public void onStop() { - super.onStop(); - map.onStop(); - } - - /** - * Called when the fragment receives onLowMemory call from the hosting Activity. - */ - @Override - public void onLowMemory() { - super.onLowMemory(); - map.onLowMemory(); - } - - /** - * Called when the fragment is view hiearchy is being destroyed. - */ - @Override - public void onDestroyView() { - super.onDestroyView(); - map.onDestroy(); - } - - /** - * Sets a callback object which will be triggered when the MapboxMap instance is ready to be used. - * - * @param onMapReadyCallback The callback to be invoked. - */ - public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) { - this.onMapReadyCallback = onMapReadyCallback; + return map = new MapView(inflater.getContext(), options); + } + + /** + * <p> + * Returns the Mapbox access token set in the app resources. + * </p> + * It will first search the application manifest for a {@link MapboxConstants#KEY_META_DATA_MANIFEST} + * meta-data value. If not found it will then attempt to load the access token from the + * {@code res/raw/token.txt} development file. + * + * @param context The {@link Context} of the {@link android.app.Activity} or {@link android.app.Fragment}. + * @return The Mapbox access token or null if not found. + * @see MapboxConstants#KEY_META_DATA_MANIFEST + * @deprecated As of release 4.1.0, replaced by + * {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)} + */ + @Deprecated + private String getToken(@NonNull Context context) { + try { + // read out AndroidManifest + PackageManager packageManager = context.getPackageManager(); + ApplicationInfo appInfo = packageManager.getApplicationInfo(context.getPackageName(), + PackageManager.GET_META_DATA); + String token = appInfo.metaData.getString(MapboxConstants.KEY_META_DATA_MANIFEST); + if (token == null || token.isEmpty()) { + throw new IllegalArgumentException(); + } + return token; + } catch (Exception exception) { + // use fallback on string resource, used for development + int tokenResId = context.getResources().getIdentifier("mapbox_access_token", "string", context.getPackageName()); + return tokenResId != 0 ? context.getString(tokenResId) : null; } + } + + /** + * Called when the fragment view hierarchy is created. + * + * @param view The content view of the fragment + * @param savedInstanceState THe saved instance state of the framgnt + */ + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + map.onCreate(savedInstanceState); + } + + /** + * Called when the fragment is visible for the users. + */ + @Override + public void onStart() { + super.onStart(); + map.onStart(); + map.getMapAsync(onMapReadyCallback); + } + + /** + * Called when the fragment is ready to be interacted with. + */ + @Override + public void onResume() { + super.onResume(); + map.onResume(); + } + + /** + * Called when the fragment is pausing. + */ + @Override + public void onPause() { + super.onPause(); + map.onPause(); + } + + /** + * Called when the fragment state needs to be saved. + * + * @param outState The saved state + */ + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + map.onSaveInstanceState(outState); + } + + /** + * Called when the fragment is no longer visible for the user. + */ + @Override + public void onStop() { + super.onStop(); + map.onStop(); + } + + /** + * Called when the fragment receives onLowMemory call from the hosting Activity. + */ + @Override + public void onLowMemory() { + super.onLowMemory(); + map.onLowMemory(); + } + + /** + * Called when the fragment is view hiearchy is being destroyed. + */ + @Override + public void onDestroyView() { + super.onDestroyView(); + map.onDestroy(); + } + + /** + * Sets a callback object which will be triggered when the MapboxMap instance is ready to be used. + * + * @param onMapReadyCallback The callback to be invoked. + */ + public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) { + this.onMapReadyCallback = 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 157a1c4a19..398093344b 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 @@ -24,577 +24,585 @@ import com.mapbox.mapboxsdk.telemetry.MapboxEvent; */ final class MapGestureDetector { - private final Transform transform; - private final Projection projection; - private final UiSettings uiSettings; - private final TrackingSettings trackingSettings; - private final AnnotationManager annotationManager; - - private final GestureDetectorCompat gestureDetector; - private final ScaleGestureDetector scaleGestureDetector; - private final RotateGestureDetector rotateGestureDetector; - private final ShoveGestureDetector shoveGestureDetector; - - private MapboxMap.OnMapClickListener onMapClickListener; - private MapboxMap.OnMapLongClickListener onMapLongClickListener; - private MapboxMap.OnFlingListener onFlingListener; - private MapboxMap.OnScrollListener onScrollListener; - - private PointF focalPoint; - - private boolean twoTap = false; - private boolean zoomStarted = false; - private boolean dragStarted = false; - private boolean quickZoom = false; - private boolean scrollInProgress = false; - - MapGestureDetector(Context context, Transform transform, Projection projection, UiSettings uiSettings, - TrackingSettings trackingSettings, AnnotationManager annotationManager) { - this.annotationManager = annotationManager; - this.transform = transform; - this.projection = projection; - this.uiSettings = uiSettings; - this.trackingSettings = trackingSettings; - - // Touch gesture detectors - gestureDetector = new GestureDetectorCompat(context, new GestureListener()); - gestureDetector.setIsLongpressEnabled(true); - scaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureListener()); - ScaleGestureDetectorCompat.setQuickScaleEnabled(scaleGestureDetector, true); - rotateGestureDetector = new RotateGestureDetector(context, new RotateGestureListener()); - shoveGestureDetector = new ShoveGestureDetector(context, new ShoveGestureListener()); + private final Transform transform; + private final Projection projection; + private final UiSettings uiSettings; + private final TrackingSettings trackingSettings; + private final AnnotationManager annotationManager; + + private final GestureDetectorCompat gestureDetector; + private final ScaleGestureDetector scaleGestureDetector; + private final RotateGestureDetector rotateGestureDetector; + private final ShoveGestureDetector shoveGestureDetector; + + private MapboxMap.OnMapClickListener onMapClickListener; + private MapboxMap.OnMapLongClickListener onMapLongClickListener; + private MapboxMap.OnFlingListener onFlingListener; + private MapboxMap.OnScrollListener onScrollListener; + + private PointF focalPoint; + + private boolean twoTap = false; + private boolean zoomStarted = false; + private boolean dragStarted = false; + private boolean quickZoom = false; + private boolean scrollInProgress = false; + + MapGestureDetector(Context context, Transform transform, Projection projection, UiSettings uiSettings, + TrackingSettings trackingSettings, AnnotationManager annotationManager) { + this.annotationManager = annotationManager; + this.transform = transform; + this.projection = projection; + this.uiSettings = uiSettings; + this.trackingSettings = trackingSettings; + + // Touch gesture detectors + gestureDetector = new GestureDetectorCompat(context, new GestureListener()); + gestureDetector.setIsLongpressEnabled(true); + scaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureListener()); + ScaleGestureDetectorCompat.setQuickScaleEnabled(scaleGestureDetector, true); + rotateGestureDetector = new RotateGestureDetector(context, new RotateGestureListener()); + shoveGestureDetector = new ShoveGestureDetector(context, new ShoveGestureListener()); + } + + /** + * Set the gesture focal point. + * <p> + * this is the center point used for calculate transformations from gestures, value is + * overridden if end user provides his own through {@link UiSettings#setFocalPoint(PointF)}. + * </p> + * + * @param focalPoint the center point for gestures + */ + void setFocalPoint(PointF focalPoint) { + if (focalPoint == null) { + // resetting focal point, + if (uiSettings.getFocalPoint() != null) { + // using user provided one to reset + focalPoint = uiSettings.getFocalPoint(); + } + } + this.focalPoint = focalPoint; + } + + + /** + * Called when user touches the screen, all positions are absolute. + * <p> + * Forwards event to the related gesture detectors. + * </p> + * + * @param event the MotionEvent + * @return True if touch event is handled + */ + boolean onTouchEvent(@NonNull MotionEvent event) { + // Check and ignore non touch or left clicks + if ((event.getButtonState() != 0) && (event.getButtonState() != MotionEvent.BUTTON_PRIMARY)) { + return false; } - /** - * Set the gesture focal point. - * <p> - * this is the center point used for calculate transformations from gestures, value is - * overridden if end user provides his own through {@link UiSettings#setFocalPoint(PointF)}. - * </p> - * - * @param focalPoint the center point for gestures - */ - void setFocalPoint(PointF focalPoint) { - if (focalPoint == null) { - // resetting focal point, - if (uiSettings.getFocalPoint() != null) { - // using user provided one to reset - focalPoint = uiSettings.getFocalPoint(); - } + // Check two finger gestures first + rotateGestureDetector.onTouchEvent(event); + scaleGestureDetector.onTouchEvent(event); + shoveGestureDetector.onTouchEvent(event); + + // Handle two finger tap + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + // First pointer down + transform.setGestureInProgress(true); + break; + + case MotionEvent.ACTION_POINTER_DOWN: + // Second pointer down + twoTap = event.getPointerCount() == 2 + && uiSettings.isZoomGesturesEnabled(); + if (twoTap) { + // Confirmed 2nd Finger Down + MapboxEvent.trackGestureEvent(projection, MapboxEvent.GESTURE_TWO_FINGER_SINGLETAP, event.getX(), + event.getY(), transform.getZoom()); + } + break; + + case MotionEvent.ACTION_POINTER_UP: + // Second pointer up + break; + + case MotionEvent.ACTION_UP: + // First pointer up + long tapInterval = event.getEventTime() - event.getDownTime(); + boolean isTap = tapInterval <= ViewConfiguration.getTapTimeout(); + boolean inProgress = rotateGestureDetector.isInProgress() + || scaleGestureDetector.isInProgress() + || shoveGestureDetector.isInProgress(); + + if (twoTap && isTap && !inProgress) { + if (focalPoint != null) { + transform.zoom(false, focalPoint.x, focalPoint.y); + } else { + PointF focalPoint = TwoFingerGestureDetector.determineFocalPoint(event); + transform.zoom(false, focalPoint.x, focalPoint.y); + } + twoTap = false; + return true; + } + + // Scroll / Pan Has Stopped + if (scrollInProgress) { + MapboxEvent.trackGestureDragEndEvent(projection, event.getX(), event.getY(), transform.getZoom()); + scrollInProgress = false; } - this.focalPoint = focalPoint; - } + twoTap = false; + transform.setGestureInProgress(false); + break; - /** - * Called when user touches the screen, all positions are absolute. - * <p> - * Forwards event to the related gesture detectors. - * </p> - * - * @param event the MotionEvent - * @return True if touch event is handled - */ - boolean onTouchEvent(@NonNull MotionEvent event) { - // Check and ignore non touch or left clicks - if ((event.getButtonState() != 0) && (event.getButtonState() != MotionEvent.BUTTON_PRIMARY)) { + case MotionEvent.ACTION_CANCEL: + twoTap = false; + transform.setGestureInProgress(false); + break; + } + + return gestureDetector.onTouchEvent(event); + } + + /** + * Called for events that don't fit the other handlers. + * <p> + * Examples of such events are mouse scroll events, mouse moves, joystick & trackpad. + * </p> + * + * @param event The MotionEvent occured + * @return True is the event is handled + */ + boolean onGenericMotionEvent(MotionEvent event) { + // Mouse events + //if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { // this is not available before API 18 + if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == InputDevice.SOURCE_CLASS_POINTER) { + // Choose the action + switch (event.getActionMasked()) { + // Mouse scrolls + case MotionEvent.ACTION_SCROLL: + if (!uiSettings.isZoomGesturesEnabled()) { return false; - } + } - // Check two finger gestures first - rotateGestureDetector.onTouchEvent(event); - scaleGestureDetector.onTouchEvent(event); - shoveGestureDetector.onTouchEvent(event); - - // Handle two finger tap - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - // First pointer down - transform.setGestureInProgress(true); - break; - - case MotionEvent.ACTION_POINTER_DOWN: - // Second pointer down - twoTap = event.getPointerCount() == 2 - && uiSettings.isZoomGesturesEnabled(); - if (twoTap) { - // Confirmed 2nd Finger Down - MapboxEvent.trackGestureEvent(projection, MapboxEvent.GESTURE_TWO_FINGER_SINGLETAP, event.getX(), event.getY(), transform.getZoom()); - } - break; - - case MotionEvent.ACTION_POINTER_UP: - // Second pointer up - break; - - case MotionEvent.ACTION_UP: - // First pointer up - long tapInterval = event.getEventTime() - event.getDownTime(); - boolean isTap = tapInterval <= ViewConfiguration.getTapTimeout(); - boolean inProgress = rotateGestureDetector.isInProgress() - || scaleGestureDetector.isInProgress() - || shoveGestureDetector.isInProgress(); - - if (twoTap && isTap && !inProgress) { - if (focalPoint != null) { - transform.zoom(false, focalPoint.x, focalPoint.y); - } else { - PointF focalPoint = TwoFingerGestureDetector.determineFocalPoint(event); - transform.zoom(false, focalPoint.x, focalPoint.y); - } - twoTap = false; - return true; - } - - // Scroll / Pan Has Stopped - if (scrollInProgress) { - MapboxEvent.trackGestureDragEndEvent(projection, event.getX(), event.getY(), transform.getZoom()); - scrollInProgress = false; - } - - twoTap = false; - transform.setGestureInProgress(false); - break; - - case MotionEvent.ACTION_CANCEL: - twoTap = false; - transform.setGestureInProgress(false); - break; - } + // Cancel any animation + transform.cancelTransitions(); + + // Get the vertical scroll amount, one click = 1 + float scrollDist = event.getAxisValue(MotionEvent.AXIS_VSCROLL); - return gestureDetector.onTouchEvent(event); + // Scale the map by the appropriate power of two factor + transform.zoomBy(Math.pow(2.0, scrollDist), event.getX(), event.getY()); + + return true; + + default: + // We are not interested in this event + return false; + } } - /** - * Called for events that don't fit the other handlers. - * <p> - * Examples of such events are mouse scroll events, mouse moves, joystick & trackpad. - * </p> - * - * @param event The MotionEvent occured - * @return True is the event is handled - */ - boolean onGenericMotionEvent(MotionEvent event) { - // Mouse events - //if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { // this is not available before API 18 - if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == InputDevice.SOURCE_CLASS_POINTER) { - // Choose the action - switch (event.getActionMasked()) { - // Mouse scrolls - case MotionEvent.ACTION_SCROLL: - if (!uiSettings.isZoomGesturesEnabled()) { - return false; - } - - // Cancel any animation - transform.cancelTransitions(); - - // Get the vertical scroll amount, one click = 1 - float scrollDist = event.getAxisValue(MotionEvent.AXIS_VSCROLL); - - // Scale the map by the appropriate power of two factor - transform.zoomBy(Math.pow(2.0, scrollDist), event.getX(), event.getY()); - - return true; - - default: - // We are not interested in this event - return false; - } - } + // We are not interested in this event + return false; + } + + + /** + * Responsible for handling one finger gestures. + */ + private class GestureListener extends android.view.GestureDetector.SimpleOnGestureListener { - // We are not interested in this event + @Override + public boolean onDown(MotionEvent event) { + return true; + } + + @Override + public boolean onDoubleTapEvent(MotionEvent motionEvent) { + if (!uiSettings.isZoomGesturesEnabled()) { return false; + } + + switch (motionEvent.getAction()) { + case MotionEvent.ACTION_DOWN: + break; + case MotionEvent.ACTION_MOVE: + break; + case MotionEvent.ACTION_UP: + if (quickZoom) { + // insert here? + quickZoom = false; + break; + } + + // Single finger double tap + if (focalPoint != null) { + // User provided focal point + transform.zoom(true, focalPoint.x, focalPoint.y); + } else { + // Zoom in on gesture + transform.zoom(true, motionEvent.getX(), motionEvent.getY()); + } + break; + } + + MapboxEvent.trackGestureEvent(projection, MapboxEvent.GESTURE_DOUBLETAP, motionEvent.getX(), motionEvent.getY(), + transform.getZoom()); + + return true; } + @Override + public boolean onSingleTapUp(MotionEvent motionEvent) { + // Cancel any animation + transform.cancelTransitions(); + return true; + } - /** - * Responsible for handling one finger gestures. - */ - private class GestureListener extends android.view.GestureDetector.SimpleOnGestureListener { + @Override + public boolean onSingleTapConfirmed(MotionEvent motionEvent) { + PointF tapPoint = new PointF(motionEvent.getX(), motionEvent.getY()); + boolean tapHandled = annotationManager.onTap(tapPoint, uiSettings.getPixelRatio()); - @Override - public boolean onDown(MotionEvent event) { - return true; + if (!tapHandled) { + if (uiSettings.isDeselectMarkersOnTap()) { + // deselect any selected marker + annotationManager.deselectMarkers(); } - @Override - public boolean onDoubleTapEvent(MotionEvent e) { - if (!uiSettings.isZoomGesturesEnabled()) { - return false; - } - - switch (e.getAction()) { - case MotionEvent.ACTION_DOWN: - break; - case MotionEvent.ACTION_MOVE: - break; - case MotionEvent.ACTION_UP: - if (quickZoom) { - // insert here? - quickZoom = false; - break; - } - - // Single finger double tap - if (focalPoint != null) { - // User provided focal point - transform.zoom(true, focalPoint.x, focalPoint.y); - } else { - // Zoom in on gesture - transform.zoom(true, e.getX(), e.getY()); - } - break; - } - - MapboxEvent.trackGestureEvent(projection, MapboxEvent.GESTURE_DOUBLETAP, e.getX(), e.getY(), transform.getZoom()); - - return true; + // notify app of map click + if (onMapClickListener != null) { + onMapClickListener.onMapClick(projection.fromScreenLocation(tapPoint)); } + } - @Override - public boolean onSingleTapUp(MotionEvent motionEvent) { - // Cancel any animation - transform.cancelTransitions(); - return true; - } + MapboxEvent.trackGestureEvent(projection, MapboxEvent.GESTURE_SINGLETAP, motionEvent.getX(), motionEvent.getY(), + transform.getZoom()); + return true; + } - @Override - public boolean onSingleTapConfirmed(MotionEvent motionEvent) { - PointF tapPoint = new PointF(motionEvent.getX(), motionEvent.getY()); - boolean tapHandled = annotationManager.onTap(tapPoint, uiSettings.getPixelRatio()); - - if (!tapHandled) { - if (uiSettings.isDeselectMarkersOnTap()) { - // deselect any selected marker - annotationManager.deselectMarkers(); - } - - // notify app of map click - if (onMapClickListener != null) { - onMapClickListener.onMapClick(projection.fromScreenLocation(tapPoint)); - } - } - - MapboxEvent.trackGestureEvent(projection, MapboxEvent.GESTURE_SINGLETAP, motionEvent.getX(), motionEvent.getY(), transform.getZoom()); - return true; - } + @Override + public void onLongPress(MotionEvent motionEvent) { + if (onMapLongClickListener != null && !quickZoom) { + onMapLongClickListener.onMapLongClick(projection.fromScreenLocation(new PointF(motionEvent.getX(), + motionEvent.getY()))); + } + } - @Override - public void onLongPress(MotionEvent motionEvent) { - if (onMapLongClickListener != null && !quickZoom) { - onMapLongClickListener.onMapLongClick(projection.fromScreenLocation(new PointF(motionEvent.getX(), motionEvent.getY()))); - } - } + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { + return false; + } - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { - return false; - } + trackingSettings.resetTrackingModesIfRequired(true, false); - trackingSettings.resetTrackingModesIfRequired(true, false); + // Cancel any animation + transform.cancelTransitions(); - // Cancel any animation - transform.cancelTransitions(); + float screenDensity = uiSettings.getPixelRatio(); - float screenDensity = uiSettings.getPixelRatio(); + double tilt = transform.getTilt(); + // tilt results in a bigger translation, need to limit input #5281, limitFactor ranges from 2 -> 8 + double limitFactor = 2 + ((tilt != 0) ? (tilt / 10) : 0); + double offsetX = velocityX / limitFactor / screenDensity; + double offsetY = velocityY / limitFactor / screenDensity; - double tilt = transform.getTilt(); - // tilt results in a bigger translation, need to limit input #5281, limitFactor ranges from 2 -> 8 - double limitFactor = 2 + ((tilt != 0) ? (tilt / 10) : 0); - double offsetX = velocityX / limitFactor / screenDensity; - double offsetY = velocityY / limitFactor / screenDensity; + transform.setGestureInProgress(true); + transform.moveBy(offsetX, offsetY, MapboxConstants.ANIMATION_DURATION_FLING); + transform.setGestureInProgress(false); - transform.setGestureInProgress(true); - transform.moveBy(offsetX, offsetY, MapboxConstants.ANIMATION_DURATION_FLING); - transform.setGestureInProgress(false); + if (onFlingListener != null) { + onFlingListener.onFling(); + } - if (onFlingListener != null) { - onFlingListener.onFling(); - } + MapboxEvent.trackGestureEvent(projection, MapboxEvent.GESTURE_PAN_START, e1.getX(), e1.getY(), + transform.getZoom()); + return true; + } - MapboxEvent.trackGestureEvent(projection, MapboxEvent.GESTURE_PAN_START, e1.getX(), e1.getY(), transform.getZoom()); - return true; - } + // Called for drags + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + if (!scrollInProgress) { + scrollInProgress = true; + } + if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { + return false; + } - // Called for drags - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - if (!scrollInProgress) { - scrollInProgress = true; - } - if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { - return false; - } - - if (dragStarted) { - return false; - } - - // reset tracking if needed - trackingSettings.resetTrackingModesIfRequired(true, false); - // Cancel any animation - transform.cancelTransitions(); - - // Scroll the map - transform.moveBy(-distanceX, -distanceY, 0 /*no duration*/); - - if (onScrollListener != null) { - onScrollListener.onScroll(); - } - return true; - } + if (dragStarted) { + return false; + } + + // reset tracking if needed + trackingSettings.resetTrackingModesIfRequired(true, false); + // Cancel any animation + transform.cancelTransitions(); + + // Scroll the map + transform.moveBy(-distanceX, -distanceY, 0 /*no duration*/); + + if (onScrollListener != null) { + onScrollListener.onScroll(); + } + return true; } + } - /** - * Responsible for handling two finger gestures and double-tap drag gestures. - */ - private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { + /** + * Responsible for handling two finger gestures and double-tap drag gestures. + */ + private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { - long beginTime = 0; - float scaleFactor = 1.0f; + long beginTime = 0; + float scaleFactor = 1.0f; - // Called when two fingers first touch the screen - @Override - public boolean onScaleBegin(ScaleGestureDetector detector) { - if (!uiSettings.isZoomGesturesEnabled()) { - return false; - } + // Called when two fingers first touch the screen + @Override + public boolean onScaleBegin(ScaleGestureDetector detector) { + if (!uiSettings.isZoomGesturesEnabled()) { + return false; + } - beginTime = detector.getEventTime(); - MapboxEvent.trackGestureEvent(projection, MapboxEvent.GESTURE_PINCH_START, detector.getFocusX(), detector.getFocusY(), transform.getZoom()); - return true; - } + beginTime = detector.getEventTime(); + MapboxEvent.trackGestureEvent(projection, MapboxEvent.GESTURE_PINCH_START, detector.getFocusX(), + detector.getFocusY(), transform.getZoom()); + return true; + } - // Called when fingers leave screen - @Override - public void onScaleEnd(ScaleGestureDetector detector) { - beginTime = 0; - scaleFactor = 1.0f; - zoomStarted = false; - } + // Called when fingers leave screen + @Override + public void onScaleEnd(ScaleGestureDetector detector) { + beginTime = 0; + scaleFactor = 1.0f; + zoomStarted = false; + } - // Called each time a finger moves - // Called for pinch zooms and quickzooms/quickscales - @Override - public boolean onScale(ScaleGestureDetector detector) { - if (!uiSettings.isZoomGesturesEnabled()) { - return super.onScale(detector); - } - - // If scale is large enough ignore a tap - scaleFactor *= detector.getScaleFactor(); - if ((scaleFactor > 1.05f) || (scaleFactor < 0.95f)) { - zoomStarted = true; - } - - // Ignore short touches in case it is a tap - // Also ignore small scales - long time = detector.getEventTime(); - long interval = time - beginTime; - if (!zoomStarted && (interval <= ViewConfiguration.getTapTimeout())) { - return false; - } - - if (!zoomStarted) { - return false; - } - - if (dragStarted) { - return false; - } - - // Cancel any animation - transform.cancelTransitions(); - - // Gesture is a quickzoom if there aren't two fingers - quickZoom = !twoTap; - - // make an assumption here; if the zoom center is specified by the gesture, it's NOT going - // to be in the center of the map. Therefore the zoom will translate the map center, so tracking - // should be disabled. - - trackingSettings.resetTrackingModesIfRequired(!quickZoom, false); - // Scale the map - if (focalPoint != null) { - // arround user provided focal point - transform.zoomBy(detector.getScaleFactor(), focalPoint.x, focalPoint.y); - } else if (quickZoom) { - // around center map - transform.zoomBy(detector.getScaleFactor(), uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - } else { - // around gesture - transform.zoomBy(detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY()); - } - - return true; - } + // Called each time a finger moves + // Called for pinch zooms and quickzooms/quickscales + @Override + public boolean onScale(ScaleGestureDetector detector) { + if (!uiSettings.isZoomGesturesEnabled()) { + return super.onScale(detector); + } + + // If scale is large enough ignore a tap + scaleFactor *= detector.getScaleFactor(); + if ((scaleFactor > 1.05f) || (scaleFactor < 0.95f)) { + zoomStarted = true; + } + + // Ignore short touches in case it is a tap + // Also ignore small scales + long time = detector.getEventTime(); + long interval = time - beginTime; + if (!zoomStarted && (interval <= ViewConfiguration.getTapTimeout())) { + return false; + } + + if (!zoomStarted) { + return false; + } + + if (dragStarted) { + return false; + } + + // Cancel any animation + transform.cancelTransitions(); + + // Gesture is a quickzoom if there aren't two fingers + quickZoom = !twoTap; + + // make an assumption here; if the zoom center is specified by the gesture, it's NOT going + // to be in the center of the map. Therefore the zoom will translate the map center, so tracking + // should be disabled. + + trackingSettings.resetTrackingModesIfRequired(!quickZoom, false); + // Scale the map + if (focalPoint != null) { + // arround user provided focal point + transform.zoomBy(detector.getScaleFactor(), focalPoint.x, focalPoint.y); + } else if (quickZoom) { + // around center map + transform.zoomBy(detector.getScaleFactor(), uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); + } else { + // around gesture + transform.zoomBy(detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY()); + } + + return true; } + } - /** - * Responsible for handling rotation gestures. - */ - private class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener { - - long beginTime = 0; - float totalAngle = 0.0f; - boolean started = false; - - // Called when two fingers first touch the screen - @Override - public boolean onRotateBegin(RotateGestureDetector detector) { - if (!trackingSettings.isRotateGestureCurrentlyEnabled()) { - return false; - } - - beginTime = detector.getEventTime(); - MapboxEvent.trackGestureEvent(projection, MapboxEvent.GESTURE_ROTATION_START, detector.getFocusX(), detector.getFocusY(), transform.getZoom()); - return true; - } + /** + * Responsible for handling rotation gestures. + */ + private class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener { - // Called when the fingers leave the screen - @Override - public void onRotateEnd(RotateGestureDetector detector) { - beginTime = 0; - totalAngle = 0.0f; - started = false; - } + long beginTime = 0; + float totalAngle = 0.0f; + boolean started = false; - // Called each time one of the two fingers moves - // Called for rotation - @Override - public boolean onRotate(RotateGestureDetector detector) { - if (!trackingSettings.isRotateGestureCurrentlyEnabled() || dragStarted) { - return false; - } - - // If rotate is large enough ignore a tap - // Also is zoom already started, don't rotate - totalAngle += detector.getRotationDegreesDelta(); - if (!zoomStarted && ((totalAngle > 20.0f) || (totalAngle < -20.0f))) { - started = true; - } - - // Ignore short touches in case it is a tap - // Also ignore small rotate - long time = detector.getEventTime(); - long interval = time - beginTime; - if (!started && (interval <= ViewConfiguration.getTapTimeout())) { - return false; - } - - if (!started) { - return false; - } - - // Cancel any animation - transform.cancelTransitions(); - - // rotation constitutes translation of anything except the center of - // rotation, so cancel both location and bearing tracking if required - - trackingSettings.resetTrackingModesIfRequired(true, true); - - // Get rotate value - double bearing = transform.getRawBearing(); - bearing += detector.getRotationDegreesDelta(); - - // Rotate the map - if (focalPoint != null) { - // User provided focal point - transform.setBearing(bearing, focalPoint.x, focalPoint.y); - } else { - // around gesture - transform.setBearing(bearing, detector.getFocusX(), detector.getFocusY()); - } - return true; - } + // Called when two fingers first touch the screen + @Override + public boolean onRotateBegin(RotateGestureDetector detector) { + if (!trackingSettings.isRotateGestureCurrentlyEnabled()) { + return false; + } + + beginTime = detector.getEventTime(); + MapboxEvent.trackGestureEvent(projection, MapboxEvent.GESTURE_ROTATION_START, detector.getFocusX(), + detector.getFocusY(), transform.getZoom()); + return true; } - /** - * Responsible for handling 2 finger shove gestures. - */ - private class ShoveGestureListener implements ShoveGestureDetector.OnShoveGestureListener { + // Called when the fingers leave the screen + @Override + public void onRotateEnd(RotateGestureDetector detector) { + beginTime = 0; + totalAngle = 0.0f; + started = false; + } - long beginTime = 0; - float totalDelta = 0.0f; - boolean started = false; + // Called each time one of the two fingers moves + // Called for rotation + @Override + public boolean onRotate(RotateGestureDetector detector) { + if (!trackingSettings.isRotateGestureCurrentlyEnabled() || dragStarted) { + return false; + } + + // If rotate is large enough ignore a tap + // Also is zoom already started, don't rotate + totalAngle += detector.getRotationDegreesDelta(); + if (!zoomStarted && ((totalAngle > 20.0f) || (totalAngle < -20.0f))) { + started = true; + } + + // Ignore short touches in case it is a tap + // Also ignore small rotate + long time = detector.getEventTime(); + long interval = time - beginTime; + if (!started && (interval <= ViewConfiguration.getTapTimeout())) { + return false; + } - @Override - public boolean onShoveBegin(ShoveGestureDetector detector) { - if (!uiSettings.isTiltGesturesEnabled()) { - return false; - } + if (!started) { + return false; + } - beginTime = detector.getEventTime(); - MapboxEvent.trackGestureEvent(projection, MapboxEvent.GESTURE_PITCH_START, detector.getFocusX(), detector.getFocusY(), transform.getZoom()); - return true; - } + // Cancel any animation + transform.cancelTransitions(); - @Override - public void onShoveEnd(ShoveGestureDetector detector) { - beginTime = 0; - totalDelta = 0.0f; - started = false; - dragStarted = false; - } + // rotation constitutes translation of anything except the center of + // rotation, so cancel both location and bearing tracking if required - @Override - public boolean onShove(ShoveGestureDetector detector) { - if (!uiSettings.isTiltGesturesEnabled()) { - return false; - } - - // If tilt is large enough ignore a tap - // Also if zoom already started, don't tilt - totalDelta += detector.getShovePixelsDelta(); - if (!zoomStarted && ((totalDelta > 10.0f) || (totalDelta < -10.0f))) { - started = true; - } - - // Ignore short touches in case it is a tap - // Also ignore small tilt - long time = detector.getEventTime(); - long interval = time - beginTime; - if (!started && (interval <= ViewConfiguration.getTapTimeout())) { - return false; - } - - if (!started) { - return false; - } - - // Cancel any animation - transform.cancelTransitions(); - - // Get tilt value (scale and clamp) - double pitch = transform.getTilt(); - pitch -= 0.1 * detector.getShovePixelsDelta(); - pitch = Math.max(MapboxConstants.MINIMUM_TILT, Math.min(MapboxConstants.MAXIMUM_TILT, pitch)); - - // Tilt the map - transform.setTilt(pitch); - - dragStarted = true; - - return true; - } - } + trackingSettings.resetTrackingModesIfRequired(true, true); - void setOnMapClickListener(MapboxMap.OnMapClickListener onMapClickListener) { - this.onMapClickListener = onMapClickListener; + // Get rotate value + double bearing = transform.getRawBearing(); + bearing += detector.getRotationDegreesDelta(); + + // Rotate the map + if (focalPoint != null) { + // User provided focal point + transform.setBearing(bearing, focalPoint.x, focalPoint.y); + } else { + // around gesture + transform.setBearing(bearing, detector.getFocusX(), detector.getFocusY()); + } + return true; } + } + + /** + * Responsible for handling 2 finger shove gestures. + */ + private class ShoveGestureListener implements ShoveGestureDetector.OnShoveGestureListener { + + long beginTime = 0; + float totalDelta = 0.0f; + boolean started = false; + + @Override + public boolean onShoveBegin(ShoveGestureDetector detector) { + if (!uiSettings.isTiltGesturesEnabled()) { + return false; + } - void setOnMapLongClickListener(MapboxMap.OnMapLongClickListener onMapLongClickListener) { - this.onMapLongClickListener = onMapLongClickListener; + beginTime = detector.getEventTime(); + MapboxEvent.trackGestureEvent(projection, MapboxEvent.GESTURE_PITCH_START, detector.getFocusX(), + detector.getFocusY(), transform.getZoom()); + return true; } - void setOnFlingListener(MapboxMap.OnFlingListener onFlingListener) { - this.onFlingListener = onFlingListener; + @Override + public void onShoveEnd(ShoveGestureDetector detector) { + beginTime = 0; + totalDelta = 0.0f; + started = false; + dragStarted = false; } - void setOnScrollListener(MapboxMap.OnScrollListener onScrollListener) { - this.onScrollListener = onScrollListener; + @Override + public boolean onShove(ShoveGestureDetector detector) { + if (!uiSettings.isTiltGesturesEnabled()) { + return false; + } + + // If tilt is large enough ignore a tap + // Also if zoom already started, don't tilt + totalDelta += detector.getShovePixelsDelta(); + if (!zoomStarted && ((totalDelta > 10.0f) || (totalDelta < -10.0f))) { + started = true; + } + + // Ignore short touches in case it is a tap + // Also ignore small tilt + long time = detector.getEventTime(); + long interval = time - beginTime; + if (!started && (interval <= ViewConfiguration.getTapTimeout())) { + return false; + } + + if (!started) { + return false; + } + + // Cancel any animation + transform.cancelTransitions(); + + // Get tilt value (scale and clamp) + double pitch = transform.getTilt(); + pitch -= 0.1 * detector.getShovePixelsDelta(); + pitch = Math.max(MapboxConstants.MINIMUM_TILT, Math.min(MapboxConstants.MAXIMUM_TILT, pitch)); + + // Tilt the map + transform.setTilt(pitch); + + dragStarted = true; + + return true; } + } + + void setOnMapClickListener(MapboxMap.OnMapClickListener onMapClickListener) { + this.onMapClickListener = onMapClickListener; + } + + void setOnMapLongClickListener(MapboxMap.OnMapLongClickListener onMapLongClickListener) { + this.onMapLongClickListener = onMapLongClickListener; + } + + void setOnFlingListener(MapboxMap.OnFlingListener onFlingListener) { + this.onFlingListener = onFlingListener; + } + + 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/MapKeyListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java index 09b4a2c9ca..c993cd3ec6 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java @@ -18,247 +18,248 @@ import android.view.ViewConfiguration; */ final class MapKeyListener { - private final TrackingSettings trackingSettings; - private final Transform transform; - private final UiSettings uiSettings; + private final TrackingSettings trackingSettings; + private final Transform transform; + private final UiSettings uiSettings; + + private TrackballLongPressTimeOut currentTrackballLongPressTimeOut; + + MapKeyListener(@NonNull Transform transform, @NonNull TrackingSettings trackingSettings, + @NonNull UiSettings uiSettings) { + this.transform = transform; + this.trackingSettings = trackingSettings; + this.uiSettings = uiSettings; + } + + /** + * Called when the user presses a key, alse called for repeated keys held down. + * + * @param keyCode the id of the pressed key + * @param event the related key event + * @return true if the wevent is handled + */ + boolean onKeyDown(int keyCode, @NonNull KeyEvent event) { + // If the user has held the scroll key down for a while then accelerate + // the scroll speed + double scrollDist = event.getRepeatCount() >= 5 ? 50.0 : 10.0; + + // Check which key was pressed via hardware/real key code + switch (keyCode) { + // Tell the system to track these keys for long presses on + // onKeyLongPress is fired + case KeyEvent.KEYCODE_ENTER: + case KeyEvent.KEYCODE_DPAD_CENTER: + event.startTracking(); + return true; + + case KeyEvent.KEYCODE_DPAD_LEFT: + if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { + return false; + } - private TrackballLongPressTimeOut currentTrackballLongPressTimeOut; + // Cancel any animation + transform.cancelTransitions(); - MapKeyListener(@NonNull Transform transform, @NonNull TrackingSettings trackingSettings, @NonNull UiSettings uiSettings) { - this.transform = transform; - this.trackingSettings = trackingSettings; - this.uiSettings = uiSettings; - } + // Move left + transform.moveBy(scrollDist, 0.0, 0 /*no animation*/); + return true; - /** - * Called when the user presses a key, alse called for repeated keys held down. - * - * @param keyCode the id of the pressed key - * @param event the related key event - * @return true if the wevent is handled - */ - boolean onKeyDown(int keyCode, @NonNull KeyEvent event) { - // If the user has held the scroll key down for a while then accelerate - // the scroll speed - double scrollDist = event.getRepeatCount() >= 5 ? 50.0 : 10.0; - - // Check which key was pressed via hardware/real key code - switch (keyCode) { - // Tell the system to track these keys for long presses on - // onKeyLongPress is fired - case KeyEvent.KEYCODE_ENTER: - case KeyEvent.KEYCODE_DPAD_CENTER: - event.startTracking(); - return true; - - case KeyEvent.KEYCODE_DPAD_LEFT: - if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { - return false; - } - - // Cancel any animation - transform.cancelTransitions(); - - // Move left - transform.moveBy(scrollDist, 0.0, 0 /*no animation*/); - return true; - - case KeyEvent.KEYCODE_DPAD_RIGHT: - if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { - return false; - } - - // Cancel any animation - transform.cancelTransitions(); - - // Move right - transform.moveBy(-scrollDist, 0.0, 0 /*no animation*/); - return true; - - case KeyEvent.KEYCODE_DPAD_UP: - if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { - return false; - } - - // Cancel any animation - transform.cancelTransitions(); - - // Move up - transform.moveBy(0.0, scrollDist, 0 /*no animation*/); - return true; - - case KeyEvent.KEYCODE_DPAD_DOWN: - if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { - return false; - } - - // Cancel any animation - transform.cancelTransitions(); - - // Move down - transform.moveBy(0.0, -scrollDist, 0 /*no animation*/); - return true; - - default: - // We are not interested in this key - return false; + case KeyEvent.KEYCODE_DPAD_RIGHT: + if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { + return false; } - } - /** - * Called when the user long presses a key that is being tracked. - * - * @param keyCode the id of the long pressed key - * @param event the related key event - * @return true if event is handled - */ - boolean onKeyLongPress(int keyCode, KeyEvent event) { - // Check which key was pressed via hardware/real key code - switch (keyCode) { - // Tell the system to track these keys for long presses on - // onKeyLongPress is fired - case KeyEvent.KEYCODE_ENTER: - case KeyEvent.KEYCODE_DPAD_CENTER: - if (!uiSettings.isZoomGesturesEnabled()) { - return false; - } - - // Zoom out - transform.zoom(false, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - return true; - - default: - // We are not interested in this key - return false; + // Cancel any animation + transform.cancelTransitions(); + + // Move right + transform.moveBy(-scrollDist, 0.0, 0 /*no animation*/); + return true; + + case KeyEvent.KEYCODE_DPAD_UP: + if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { + return false; } - } - /** - * Called when the user releases a key. - * - * @param keyCode the id of the released key - * @param event the related key event - * @return true if the event is handled - */ - boolean onKeyUp(int keyCode, KeyEvent event) { - // Check if the key action was canceled (used for virtual keyboards) - if (event.isCanceled()) { - return false; + // Cancel any animation + transform.cancelTransitions(); + + // Move up + transform.moveBy(0.0, scrollDist, 0 /*no animation*/); + return true; + + case KeyEvent.KEYCODE_DPAD_DOWN: + if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { + return false; } - // Check which key was pressed via hardware/real key code - // Note if keyboard does not have physical key (ie primary non-shifted - // key) then it will not appear here - // Must use the key character map as physical to character is not - // fixed/guaranteed - switch (keyCode) { - case KeyEvent.KEYCODE_ENTER: - case KeyEvent.KEYCODE_DPAD_CENTER: - if (!uiSettings.isZoomGesturesEnabled()) { - return false; - } - - // Zoom in - transform.zoom(true, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - return true; + // Cancel any animation + transform.cancelTransitions(); + + // Move down + transform.moveBy(0.0, -scrollDist, 0 /*no animation*/); + return true; + + default: + // We are not interested in this key + return false; + } + } + + /** + * Called when the user long presses a key that is being tracked. + * + * @param keyCode the id of the long pressed key + * @param event the related key event + * @return true if event is handled + */ + boolean onKeyLongPress(int keyCode, KeyEvent event) { + // Check which key was pressed via hardware/real key code + switch (keyCode) { + // Tell the system to track these keys for long presses on + // onKeyLongPress is fired + case KeyEvent.KEYCODE_ENTER: + case KeyEvent.KEYCODE_DPAD_CENTER: + if (!uiSettings.isZoomGesturesEnabled()) { + return false; } + // Zoom out + transform.zoom(false, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); + return true; + + default: // We are not interested in this key return false; } + } + + /** + * Called when the user releases a key. + * + * @param keyCode the id of the released key + * @param event the related key event + * @return true if the event is handled + */ + boolean onKeyUp(int keyCode, KeyEvent event) { + // Check if the key action was canceled (used for virtual keyboards) + if (event.isCanceled()) { + return false; + } - /** - * Called for trackball events, all motions are relative in device specific units. - * - * @param event the related motion event - * @return true if the event is handled - */ - boolean onTrackballEvent(MotionEvent event) { - // Choose the action - switch (event.getActionMasked()) { - // The trackball was rotated - case MotionEvent.ACTION_MOVE: - if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { - return false; - } - - // Cancel any animation - transform.cancelTransitions(); - - // Scroll the map - transform.moveBy(-10.0 * event.getX(), -10.0 * event.getY(), 0 /*no animation*/); - return true; - - // Trackball was pushed in so start tracking and tell system we are - // interested - // We will then get the up action - case MotionEvent.ACTION_DOWN: - // Set up a delayed callback to check if trackball is still - // After waiting the system long press time out - if (currentTrackballLongPressTimeOut != null) { - currentTrackballLongPressTimeOut.cancel(); - currentTrackballLongPressTimeOut = null; - } - currentTrackballLongPressTimeOut = new TrackballLongPressTimeOut(); - new Handler().postDelayed(currentTrackballLongPressTimeOut, - ViewConfiguration.getLongPressTimeout()); - return true; - - // Trackball was released - case MotionEvent.ACTION_UP: - if (!uiSettings.isZoomGesturesEnabled()) { - return false; - } - - // Only handle if we have not already long pressed - if (currentTrackballLongPressTimeOut != null) { - // Zoom in - transform.zoom(true, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - } - return true; - - // Trackball was cancelled - case MotionEvent.ACTION_CANCEL: - if (currentTrackballLongPressTimeOut != null) { - currentTrackballLongPressTimeOut.cancel(); - currentTrackballLongPressTimeOut = null; - } - return true; - - default: - // We are not interested in this event - return false; + // Check which key was pressed via hardware/real key code + // Note if keyboard does not have physical key (ie primary non-shifted + // key) then it will not appear here + // Must use the key character map as physical to character is not + // fixed/guaranteed + switch (keyCode) { + case KeyEvent.KEYCODE_ENTER: + case KeyEvent.KEYCODE_DPAD_CENTER: + if (!uiSettings.isZoomGesturesEnabled()) { + return false; } - } - /** - * This class implements the trackball long press time out callback - */ - private class TrackballLongPressTimeOut implements Runnable { + // Zoom in + transform.zoom(true, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); + return true; + } - // Track if we have been cancelled - private boolean cancelled; + // We are not interested in this key + return false; + } + + /** + * Called for trackball events, all motions are relative in device specific units. + * + * @param event the related motion event + * @return true if the event is handled + */ + boolean onTrackballEvent(MotionEvent event) { + // Choose the action + switch (event.getActionMasked()) { + // The trackball was rotated + case MotionEvent.ACTION_MOVE: + if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { + return false; + } - TrackballLongPressTimeOut() { - cancelled = false; + // Cancel any animation + transform.cancelTransitions(); + + // Scroll the map + transform.moveBy(-10.0 * event.getX(), -10.0 * event.getY(), 0 /*no animation*/); + return true; + + // Trackball was pushed in so start tracking and tell system we are + // interested + // We will then get the up action + case MotionEvent.ACTION_DOWN: + // Set up a delayed callback to check if trackball is still + // After waiting the system long press time out + if (currentTrackballLongPressTimeOut != null) { + currentTrackballLongPressTimeOut.cancel(); + currentTrackballLongPressTimeOut = null; + } + currentTrackballLongPressTimeOut = new TrackballLongPressTimeOut(); + new Handler().postDelayed(currentTrackballLongPressTimeOut, + ViewConfiguration.getLongPressTimeout()); + return true; + + // Trackball was released + case MotionEvent.ACTION_UP: + if (!uiSettings.isZoomGesturesEnabled()) { + return false; } - // Cancel the timeout - public void cancel() { - cancelled = true; + // Only handle if we have not already long pressed + if (currentTrackballLongPressTimeOut != null) { + // Zoom in + transform.zoom(true, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); } + return true; - // Called when long press time out expires - @Override - public void run() { - // Check if the trackball is still pressed - if (!cancelled) { - // Zoom out - transform.zoom(false, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - - // Ensure the up action is not run - currentTrackballLongPressTimeOut = null; - } + // Trackball was cancelled + case MotionEvent.ACTION_CANCEL: + if (currentTrackballLongPressTimeOut != null) { + currentTrackballLongPressTimeOut.cancel(); + currentTrackballLongPressTimeOut = null; } + return true; + + default: + // We are not interested in this event + return false; + } + } + + /** + * This class implements the trackball long press time out callback + */ + private class TrackballLongPressTimeOut implements Runnable { + + // Track if we have been cancelled + private boolean cancelled; + + TrackballLongPressTimeOut() { + cancelled = false; + } + + // Cancel the timeout + public void cancel() { + cancelled = true; + } + + // Called when long press time out expires + @Override + public void run() { + // Check if the trackball is still pressed + if (!cancelled) { + // Zoom out + transform.zoom(false, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); + + // Ensure the up action is not run + currentTrackballLongPressTimeOut = null; + } } + } } 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 58144d7a2c..5bbba8682a 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 @@ -46,7 +46,6 @@ import com.mapbox.mapboxsdk.annotations.InfoWindow; import com.mapbox.mapboxsdk.annotations.MarkerViewManager; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; -import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.constants.MyBearingTracking; import com.mapbox.mapboxsdk.constants.MyLocationTracking; import com.mapbox.mapboxsdk.constants.Style; @@ -67,6 +66,45 @@ import java.util.List; import timber.log.Timber; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.ANIMATION_DURATION; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_ATTRIBUTION_ENABLED; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_ATTRIBUTION_GRAVITY; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_ATTRIBUTION_MARGIN_BOTTOM; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_ATTRIBUTION_MARGIN_LEFT; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_ATTRIBUTION_MARGIN_RIGHT; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_ATTRIBUTION_MARGIN_TOP; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_CAMERA_POSITION; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_COMPASS_ENABLED; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_COMPASS_FADE_WHEN_FACING_NORTH; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_COMPASS_GRAVITY; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_COMPASS_MARGIN_BOTTOM; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_COMPASS_MARGIN_LEFT; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_COMPASS_MARGIN_RIGHT; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_COMPASS_MARGIN_TOP; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_DEBUG_ACTIVE; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_HAS_SAVED_STATE; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_LOGO_ENABLED; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_LOGO_GRAVITY; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_LOGO_MARGIN_BOTTOM; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_LOGO_MARGIN_LEFT; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_LOGO_MARGIN_RIGHT; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_LOGO_MARGIN_TOP; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_MY_BEARING_TRACKING_DISMISS; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_MY_BEARING_TRACKING_MODE; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_MY_LOCATION_ENABLED; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_MY_LOCATION_TRACKING_DISMISS; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_MY_LOCATION_TRACKING_MODE; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_ROTATE_ENABLED; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_ROTATE_ENABLED_CHANGE; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_SCROLL_ENABLED; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_SCROLL_ENABLED_CHANGE; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_STYLE_URL; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_TILT_ENABLED; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_TILT_ENABLED_CHANGE; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_ZOOM_CONTROLS_ENABLED; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_ZOOM_ENABLED; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.STATE_ZOOM_ENABLED_CHANGE; + /** * <p> * A {@code MapView} provides an embeddable map interface. @@ -83,1272 +121,1277 @@ import timber.log.Timber; */ public class MapView extends FrameLayout { - private MapboxMap mapboxMap; + private MapboxMap mapboxMap; - private boolean initialLoad; - private boolean destroyed; + private boolean initialLoad; + private boolean destroyed; - private NativeMapView nativeMapView; - private boolean hasSurface = false; + private NativeMapView nativeMapView; + private boolean hasSurface = false; - private CompassView compassView; - private MyLocationView myLocationView; - private LocationListener myLocationListener; + private CompassView compassView; + private MyLocationView myLocationView; + private LocationListener myLocationListener; - private MapGestureDetector mapGestureDetector; - private MapKeyListener mapKeyListener; + private MapGestureDetector mapGestureDetector; + private MapKeyListener mapKeyListener; - private ConnectivityReceiver connectivityReceiver; + private ConnectivityReceiver connectivityReceiver; - private List<OnMapReadyCallback> onMapReadyCallbackList = new ArrayList<>(); - private SnapshotRequest snapshotRequest; - private ZoomButtonsController zoomButtonsController; + private List<OnMapReadyCallback> onMapReadyCallbackList = new ArrayList<>(); + private SnapshotRequest snapshotRequest; + private ZoomButtonsController zoomButtonsController; - private boolean onStartCalled; - private boolean onStopCalled; + private boolean onStartCalled; + private boolean onStopCalled; - @UiThread - public MapView(@NonNull Context context) { - super(context); - initialize(context, MapboxMapOptions.createFromAttributes(context, null)); - } + @UiThread + public MapView(@NonNull Context context) { + super(context); + initialize(context, MapboxMapOptions.createFromAttributes(context, null)); + } - @UiThread - public MapView(@NonNull Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - initialize(context, MapboxMapOptions.createFromAttributes(context, attrs)); - } + @UiThread + public MapView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + initialize(context, MapboxMapOptions.createFromAttributes(context, attrs)); + } - @UiThread - public MapView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - initialize(context, MapboxMapOptions.createFromAttributes(context, attrs)); - } + @UiThread + public MapView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initialize(context, MapboxMapOptions.createFromAttributes(context, attrs)); + } - @UiThread - public MapView(@NonNull Context context, @NonNull MapboxMapOptions options) { - super(context); - initialize(context, options); - } - - private void initialize(@NonNull final Context context, @NonNull final MapboxMapOptions options) { - if (isInEditMode()) { - // if we are in an editor mode we show an image of a map - LayoutInflater.from(context).inflate(R.layout.mapbox_mapview_preview, this); - return; - } - - // TODO distill into singular purpose methods/classes - initialLoad = true; - View view = LayoutInflater.from(context).inflate(R.layout.mapbox_mapview_internal, this); - setWillNotDraw(false); - - if (options.getTextureMode()) { - TextureView textureView = new TextureView(context); - textureView.setSurfaceTextureListener(new SurfaceTextureListener()); - addView(textureView, 0); - } else { - SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView); - surfaceView.getHolder().addCallback(new SurfaceCallback()); - surfaceView.setVisibility(View.VISIBLE); - } + @UiThread + public MapView(@NonNull Context context, @NonNull MapboxMapOptions options) { + super(context); + initialize(context, options); + } - nativeMapView = new NativeMapView(this); - - // inflate overlain Views - compassView = (CompassView) view.findViewById(R.id.compassView); - myLocationView = (MyLocationView) view.findViewById(R.id.userLocationView); - ImageView logoView = (ImageView) view.findViewById(R.id.logoView); - ImageView attributionsView = (ImageView) view.findViewById(R.id.attributionView); - attributionsView.setOnClickListener(new AttributionOnClickListener(this)); - ViewGroup markerViewContainer = (ViewGroup) findViewById(R.id.markerViewContainer); - - // interface for focal point invalidation - FocalPointInvalidator focalPointInvalidator = new FocalPointInvalidator(); - - // interface for registering touch listeners - RegisterTouchListener registerTouchListener = new RegisterTouchListener(); - - // setup components for MapboxMap creation - Projection projection = new Projection(nativeMapView); - UiSettings uiSettings = new UiSettings(projection, focalPointInvalidator, compassView, attributionsView, logoView); - TrackingSettings trackingSettings = new TrackingSettings(myLocationView, uiSettings, focalPointInvalidator); - MyLocationViewSettings myLocationViewSettings = new MyLocationViewSettings(myLocationView, projection, focalPointInvalidator); - MarkerViewManager markerViewManager = new MarkerViewManager(markerViewContainer); - AnnotationManager annotationManager = new AnnotationManager(nativeMapView, this, markerViewManager); - Transform transform = new Transform(nativeMapView, annotationManager.getMarkerViewManager(), trackingSettings); - mapboxMap = new MapboxMap(nativeMapView, transform, uiSettings, trackingSettings, myLocationViewSettings, projection, registerTouchListener, annotationManager); - - // active user input - mapGestureDetector = new MapGestureDetector(context, mapboxMap.getTransform(), projection, uiSettings, trackingSettings, annotationManager); - mapKeyListener = new MapKeyListener(mapboxMap.getTransform(), trackingSettings, uiSettings); - - // attach widgets to MapboxMap - compassView.setMapboxMap(mapboxMap); - myLocationView.setMapboxMap(mapboxMap); - - // Ensure this view is interactable - setClickable(true); - setLongClickable(true); - setFocusable(true); - setFocusableInTouchMode(true); - requestDisallowInterceptTouchEvent(true); - requestFocus(); - - // Connectivity - onConnectivityChanged(isConnected()); - - // configure the zoom button controller - zoomButtonsController = new ZoomButtonsController(MapView.this); - zoomButtonsController.setZoomSpeed(MapboxConstants.ANIMATION_DURATION); - zoomButtonsController.setOnZoomListener(new OnZoomListener(mapboxMap)); - - mapboxMap.initialise(context, options); - - // Shows the zoom controls - if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)) { - uiSettings.setZoomControlsEnabled(true); - } + private void initialize(@NonNull final Context context, @NonNull final MapboxMapOptions options) { + if (isInEditMode()) { + // if we are in an editor mode we show an image of a map + LayoutInflater.from(context).inflate(R.layout.mapbox_mapview_preview, this); + return; } - // - // Lifecycle events - // - - /** - * <p> - * You must call this method from the parent's {@link android.app.Activity#onCreate(Bundle)} or - * {@link android.app.Fragment#onCreate(Bundle)}. - * </p> - * You must set a valid access token with {@link MapView#setAccessToken(String)} before you this method - * or an exception will be thrown. - * - * @param savedInstanceState Pass in the parent's savedInstanceState. - * @see MapView#setAccessToken(String) - */ - @UiThread - public void onCreate(@Nullable Bundle savedInstanceState) { - // TODO distill into singular purpose methods/classes - String accessToken = mapboxMap.getAccessToken(); - if (TextUtils.isEmpty(accessToken)) { - accessToken = MapboxAccountManager.getInstance().getAccessToken(); - nativeMapView.setAccessToken(accessToken); - } else { - // user provided access token through xml attributes, need to start MapboxAccountManager - MapboxAccountManager.start(getContext(), accessToken); - nativeMapView.setAccessToken(accessToken); - } - - // Force a check for an access token - MapboxAccountManager.validateAccessToken(accessToken); - nativeMapView.setAccessToken(accessToken); - - if (savedInstanceState != null && savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) { + // TODO distill into singular purpose methods/classes + initialLoad = true; + View view = LayoutInflater.from(context).inflate(R.layout.mapbox_mapview_internal, this); + setWillNotDraw(false); + + if (options.getTextureMode()) { + TextureView textureView = new TextureView(context); + textureView.setSurfaceTextureListener(new SurfaceTextureListener()); + addView(textureView, 0); + } else { + SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView); + surfaceView.getHolder().addCallback(new SurfaceCallback()); + surfaceView.setVisibility(View.VISIBLE); + } - // Get previous camera position - CameraPosition cameraPosition = savedInstanceState.getParcelable(MapboxConstants.STATE_CAMERA_POSITION); - if (cameraPosition != null) { - mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder(cameraPosition).build())); - } + nativeMapView = new NativeMapView(this); + + // inflate overlain Views + compassView = (CompassView) view.findViewById(R.id.compassView); + myLocationView = (MyLocationView) view.findViewById(R.id.userLocationView); + ImageView logoView = (ImageView) view.findViewById(R.id.logoView); + ImageView attributionsView = (ImageView) view.findViewById(R.id.attributionView); + attributionsView.setOnClickListener(new AttributionOnClickListener(this)); + ViewGroup markerViewContainer = (ViewGroup) findViewById(R.id.markerViewContainer); + + // interface for focal point invalidation + FocalPointInvalidator focalPointInvalidator = new FocalPointInvalidator(); + + // interface for registering touch listeners + RegisterTouchListener registerTouchListener = new RegisterTouchListener(); + + // setup components for MapboxMap creation + Projection projection = new Projection(nativeMapView); + UiSettings uiSettings = new UiSettings(projection, focalPointInvalidator, compassView, attributionsView, logoView); + TrackingSettings trackingSettings = new TrackingSettings(myLocationView, uiSettings, focalPointInvalidator); + MyLocationViewSettings myLocationViewSettings = new MyLocationViewSettings(myLocationView, projection, + focalPointInvalidator); + MarkerViewManager markerViewManager = new MarkerViewManager(markerViewContainer); + AnnotationManager annotationManager = new AnnotationManager(nativeMapView, this, markerViewManager); + Transform transform = new Transform(nativeMapView, annotationManager.getMarkerViewManager(), trackingSettings); + mapboxMap = new MapboxMap(nativeMapView, transform, uiSettings, trackingSettings, myLocationViewSettings, + projection, registerTouchListener, annotationManager); + + // active user input + mapGestureDetector = new MapGestureDetector(context, mapboxMap.getTransform(), projection, uiSettings, + trackingSettings, annotationManager); + mapKeyListener = new MapKeyListener(mapboxMap.getTransform(), trackingSettings, uiSettings); + + // attach widgets to MapboxMap + compassView.setMapboxMap(mapboxMap); + myLocationView.setMapboxMap(mapboxMap); + + // Ensure this view is interactable + setClickable(true); + setLongClickable(true); + setFocusable(true); + setFocusableInTouchMode(true); + requestDisallowInterceptTouchEvent(true); + requestFocus(); + + // Connectivity + onConnectivityChanged(isConnected()); + + // configure the zoom button controller + zoomButtonsController = new ZoomButtonsController(MapView.this); + zoomButtonsController.setZoomSpeed(ANIMATION_DURATION); + zoomButtonsController.setOnZoomListener(new OnZoomListener(mapboxMap)); + + mapboxMap.initialise(context, options); + + // Shows the zoom controls + if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)) { + uiSettings.setZoomControlsEnabled(true); + } + } + + // + // Lifecycle events + // + + /** + * <p> + * You must call this method from the parent's {@link android.app.Activity#onCreate(Bundle)} or + * {@link android.app.Fragment#onCreate(Bundle)}. + * </p> + * You must set a valid access token with {@link MapView#setAccessToken(String)} before you this method + * or an exception will be thrown. + * + * @param savedInstanceState Pass in the parent's savedInstanceState. + * @see MapView#setAccessToken(String) + */ + @UiThread + public void onCreate(@Nullable Bundle savedInstanceState) { + // TODO distill into singular purpose methods/classes + String accessToken = mapboxMap.getAccessToken(); + if (TextUtils.isEmpty(accessToken)) { + accessToken = MapboxAccountManager.getInstance().getAccessToken(); + nativeMapView.setAccessToken(accessToken); + } else { + // user provided access token through xml attributes, need to start MapboxAccountManager + MapboxAccountManager.start(getContext(), accessToken); + nativeMapView.setAccessToken(accessToken); + } - UiSettings uiSettings = mapboxMap.getUiSettings(); - uiSettings.setZoomGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_ZOOM_ENABLED)); - uiSettings.setZoomGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_ZOOM_ENABLED_CHANGE)); - uiSettings.setScrollGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_SCROLL_ENABLED)); - uiSettings.setScrollGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_SCROLL_ENABLED_CHANGE)); - uiSettings.setRotateGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_ROTATE_ENABLED)); - uiSettings.setRotateGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_ROTATE_ENABLED_CHANGE)); - uiSettings.setTiltGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_TILT_ENABLED)); - uiSettings.setTiltGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_TILT_ENABLED_CHANGE)); - uiSettings.setZoomControlsEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_ZOOM_CONTROLS_ENABLED)); - - // Compass - uiSettings.setCompassEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_COMPASS_ENABLED)); - uiSettings.setCompassGravity(savedInstanceState.getInt(MapboxConstants.STATE_COMPASS_GRAVITY)); - uiSettings.setCompassMargins(savedInstanceState.getInt(MapboxConstants.STATE_COMPASS_MARGIN_LEFT), - savedInstanceState.getInt(MapboxConstants.STATE_COMPASS_MARGIN_TOP), - savedInstanceState.getInt(MapboxConstants.STATE_COMPASS_MARGIN_RIGHT), - savedInstanceState.getInt(MapboxConstants.STATE_COMPASS_MARGIN_BOTTOM)); - uiSettings.setCompassFadeFacingNorth(savedInstanceState.getBoolean(MapboxConstants.STATE_COMPASS_FADE_WHEN_FACING_NORTH)); - - // Logo - uiSettings.setLogoEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_LOGO_ENABLED)); - uiSettings.setLogoGravity(savedInstanceState.getInt(MapboxConstants.STATE_LOGO_GRAVITY)); - uiSettings.setLogoMargins(savedInstanceState.getInt(MapboxConstants.STATE_LOGO_MARGIN_LEFT) - , savedInstanceState.getInt(MapboxConstants.STATE_LOGO_MARGIN_TOP) - , savedInstanceState.getInt(MapboxConstants.STATE_LOGO_MARGIN_RIGHT) - , savedInstanceState.getInt(MapboxConstants.STATE_LOGO_MARGIN_BOTTOM)); - - // Attribution - uiSettings.setAttributionEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_ATTRIBUTION_ENABLED)); - uiSettings.setAttributionGravity(savedInstanceState.getInt(MapboxConstants.STATE_ATTRIBUTION_GRAVITY)); - uiSettings.setAttributionMargins(savedInstanceState.getInt(MapboxConstants.STATE_ATTRIBUTION_MARGIN_LEFT) - , savedInstanceState.getInt(MapboxConstants.STATE_ATTRIBUTION_MARGIN_TOP) - , savedInstanceState.getInt(MapboxConstants.STATE_ATTRIBUTION_MARGIN_RIGHT) - , savedInstanceState.getInt(MapboxConstants.STATE_ATTRIBUTION_MARGIN_BOTTOM)); - - mapboxMap.setDebugActive(savedInstanceState.getBoolean(MapboxConstants.STATE_DEBUG_ACTIVE)); - - String styleUrl = savedInstanceState.getString(MapboxConstants.STATE_STYLE_URL); - if (!TextUtils.isEmpty(styleUrl)) { - nativeMapView.setStyleUrl(savedInstanceState.getString(MapboxConstants.STATE_STYLE_URL)); - } + // Force a check for an access token + MapboxAccountManager.validateAccessToken(accessToken); + nativeMapView.setAccessToken(accessToken); + + if (savedInstanceState != null && savedInstanceState.getBoolean(STATE_HAS_SAVED_STATE)) { + + // Get previous camera position + CameraPosition cameraPosition = savedInstanceState.getParcelable(STATE_CAMERA_POSITION); + if (cameraPosition != null) { + mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder(cameraPosition).build())); + } + + UiSettings uiSettings = mapboxMap.getUiSettings(); + uiSettings.setZoomGesturesEnabled(savedInstanceState.getBoolean(STATE_ZOOM_ENABLED)); + uiSettings.setZoomGestureChangeAllowed(savedInstanceState.getBoolean(STATE_ZOOM_ENABLED_CHANGE)); + uiSettings.setScrollGesturesEnabled(savedInstanceState.getBoolean(STATE_SCROLL_ENABLED)); + uiSettings.setScrollGestureChangeAllowed(savedInstanceState.getBoolean(STATE_SCROLL_ENABLED_CHANGE)); + uiSettings.setRotateGesturesEnabled(savedInstanceState.getBoolean(STATE_ROTATE_ENABLED)); + uiSettings.setRotateGestureChangeAllowed(savedInstanceState.getBoolean(STATE_ROTATE_ENABLED_CHANGE)); + uiSettings.setTiltGesturesEnabled(savedInstanceState.getBoolean(STATE_TILT_ENABLED)); + uiSettings.setTiltGestureChangeAllowed(savedInstanceState.getBoolean(STATE_TILT_ENABLED_CHANGE)); + uiSettings.setZoomControlsEnabled(savedInstanceState.getBoolean(STATE_ZOOM_CONTROLS_ENABLED)); + + // Compass + uiSettings.setCompassEnabled(savedInstanceState.getBoolean(STATE_COMPASS_ENABLED)); + uiSettings.setCompassGravity(savedInstanceState.getInt(STATE_COMPASS_GRAVITY)); + uiSettings.setCompassMargins(savedInstanceState.getInt(STATE_COMPASS_MARGIN_LEFT), + savedInstanceState.getInt(STATE_COMPASS_MARGIN_TOP), + savedInstanceState.getInt(STATE_COMPASS_MARGIN_RIGHT), + savedInstanceState.getInt(STATE_COMPASS_MARGIN_BOTTOM)); + uiSettings.setCompassFadeFacingNorth(savedInstanceState.getBoolean(STATE_COMPASS_FADE_WHEN_FACING_NORTH)); + + // Logo + uiSettings.setLogoEnabled(savedInstanceState.getBoolean(STATE_LOGO_ENABLED)); + uiSettings.setLogoGravity(savedInstanceState.getInt(STATE_LOGO_GRAVITY)); + uiSettings.setLogoMargins(savedInstanceState.getInt(STATE_LOGO_MARGIN_LEFT), + savedInstanceState.getInt(STATE_LOGO_MARGIN_TOP), + savedInstanceState.getInt(STATE_LOGO_MARGIN_RIGHT), + savedInstanceState.getInt(STATE_LOGO_MARGIN_BOTTOM)); + + // Attribution + uiSettings.setAttributionEnabled(savedInstanceState.getBoolean(STATE_ATTRIBUTION_ENABLED)); + uiSettings.setAttributionGravity(savedInstanceState.getInt(STATE_ATTRIBUTION_GRAVITY)); + uiSettings.setAttributionMargins(savedInstanceState.getInt(STATE_ATTRIBUTION_MARGIN_LEFT), + savedInstanceState.getInt(STATE_ATTRIBUTION_MARGIN_TOP), + savedInstanceState.getInt(STATE_ATTRIBUTION_MARGIN_RIGHT), + savedInstanceState.getInt(STATE_ATTRIBUTION_MARGIN_BOTTOM)); + + mapboxMap.setDebugActive(savedInstanceState.getBoolean(STATE_DEBUG_ACTIVE)); + + String styleUrl = savedInstanceState.getString(STATE_STYLE_URL); + if (!TextUtils.isEmpty(styleUrl)) { + nativeMapView.setStyleUrl(savedInstanceState.getString(STATE_STYLE_URL)); + } + + // User location + try { + mapboxMap.setMyLocationEnabled(savedInstanceState.getBoolean(STATE_MY_LOCATION_ENABLED)); + } catch (SecurityException ignore) { + // User did not accept location permissions + } + + TrackingSettings trackingSettings = mapboxMap.getTrackingSettings(); + //noinspection ResourceType + trackingSettings.setMyLocationTrackingMode( + savedInstanceState.getInt(STATE_MY_LOCATION_TRACKING_MODE, MyLocationTracking.TRACKING_NONE)); + //noinspection ResourceType + trackingSettings.setMyBearingTrackingMode( + savedInstanceState.getInt(STATE_MY_BEARING_TRACKING_MODE, MyBearingTracking.NONE)); + trackingSettings.setDismissLocationTrackingOnGesture( + savedInstanceState.getBoolean(STATE_MY_LOCATION_TRACKING_DISMISS, true)); + trackingSettings.setDismissBearingTrackingOnGesture( + savedInstanceState.getBoolean(STATE_MY_BEARING_TRACKING_DISMISS, true)); + } else if (savedInstanceState == null) { + // Start Telemetry (authorization determined in initial MapboxEventManager constructor) + Timber.i("MapView start Telemetry..."); + MapboxEventManager eventManager = MapboxEventManager.getMapboxEventManager(); + eventManager.initialize(getContext(), getAccessToken()); + } - // User location - try { - mapboxMap.setMyLocationEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_MY_LOCATION_ENABLED)); - } catch (SecurityException ignore) { - // User did not accept location permissions + // Initialize EGL + nativeMapView.initializeDisplay(); + nativeMapView.initializeContext(); + + // Add annotation deselection listener + addOnMapChangedListener(new OnMapChangedListener() { + @Override + public void onMapChanged(@MapChange int change) { + + // TODO extract logic into separate OnMapReady and Update Component + if (change == DID_FINISH_LOADING_STYLE && initialLoad) { + initialLoad = false; + mapboxMap.getAnnotationManager().reloadMarkers(); + mapboxMap.getAnnotationManager().adjustTopOffsetPixels(mapboxMap); + + // Notify listeners the map is ready + if (onMapReadyCallbackList.size() > 0) { + Iterator<OnMapReadyCallback> iterator = onMapReadyCallbackList.iterator(); + while (iterator.hasNext()) { + OnMapReadyCallback callback = iterator.next(); + callback.onMapReady(mapboxMap); + iterator.remove(); } + } - TrackingSettings trackingSettings = mapboxMap.getTrackingSettings(); - //noinspection ResourceType - trackingSettings.setMyLocationTrackingMode( - savedInstanceState.getInt(MapboxConstants.STATE_MY_LOCATION_TRACKING_MODE, MyLocationTracking.TRACKING_NONE)); - //noinspection ResourceType - trackingSettings.setMyBearingTrackingMode( - savedInstanceState.getInt(MapboxConstants.STATE_MY_BEARING_TRACKING_MODE, MyBearingTracking.NONE)); - trackingSettings.setDismissLocationTrackingOnGesture( - savedInstanceState.getBoolean(MapboxConstants.STATE_MY_LOCATION_TRACKING_DISMISS, true)); - trackingSettings.setDismissBearingTrackingOnGesture( - savedInstanceState.getBoolean(MapboxConstants.STATE_MY_BEARING_TRACKING_DISMISS, true)); - } else if (savedInstanceState == null) { - // Start Telemetry (authorization determined in initial MapboxEventManager constructor) - Timber.i("MapView start Telemetry..."); - MapboxEventManager eventManager = MapboxEventManager.getMapboxEventManager(); - eventManager.initialize(getContext(), getAccessToken()); - } + // invalidate camera to update overlain views with correct tilt value + mapboxMap.invalidateCameraPosition(); - // Initialize EGL - nativeMapView.initializeDisplay(); - nativeMapView.initializeContext(); - - // Add annotation deselection listener - addOnMapChangedListener(new OnMapChangedListener() { - @Override - public void onMapChanged(@MapChange int change) { - - // TODO extract logic into separate OnMapReady and Update Component - if (change == DID_FINISH_LOADING_STYLE && initialLoad) { - initialLoad = false; - mapboxMap.getAnnotationManager().reloadMarkers(); - mapboxMap.getAnnotationManager().adjustTopOffsetPixels(mapboxMap); - - // Notify listeners the map is ready - if (onMapReadyCallbackList.size() > 0) { - Iterator<OnMapReadyCallback> iterator = onMapReadyCallbackList.iterator(); - while (iterator.hasNext()) { - OnMapReadyCallback callback = iterator.next(); - callback.onMapReady(mapboxMap); - iterator.remove(); - } - } - - // invalidate camera to update overlain views with correct tilt value - mapboxMap.invalidateCameraPosition(); - - } else if (change == REGION_IS_CHANGING || change == REGION_DID_CHANGE || change == DID_FINISH_LOADING_MAP) { - mapboxMap.getMarkerViewManager().scheduleViewMarkerInvalidation(); - - compassView.update(mapboxMap.getTransform().getBearing()); - myLocationView.update(); - mapboxMap.getMarkerViewManager().update(); - - for (InfoWindow infoWindow : mapboxMap.getInfoWindows()) { - infoWindow.update(); - } - } + } else if (change == REGION_IS_CHANGING || change == REGION_DID_CHANGE || change == DID_FINISH_LOADING_MAP) { + mapboxMap.getMarkerViewManager().scheduleViewMarkerInvalidation(); - } - }); + compassView.update(mapboxMap.getTransform().getBearing()); + myLocationView.update(); + mapboxMap.getMarkerViewManager().update(); - // Fire MapLoad - if (savedInstanceState == null) { - Hashtable<String, Object> evt = new Hashtable<>(); - evt.put(MapboxEvent.ATTRIBUTE_EVENT, MapboxEvent.TYPE_MAP_LOAD); - evt.put(MapboxEvent.ATTRIBUTE_CREATED, MapboxEventManager.generateCreateDate()); - MapboxEventManager.getMapboxEventManager().pushEvent(evt); + for (InfoWindow infoWindow : mapboxMap.getInfoWindows()) { + infoWindow.update(); + } } - } - /** - * You must call this method from the parent's {@link android.app.Activity#onSaveInstanceState(Bundle)} - * or {@link android.app.Fragment#onSaveInstanceState(Bundle)}. - * - * @param outState Pass in the parent's outState. - */ + } + }); - @UiThread - public void onSaveInstanceState(@NonNull Bundle outState) { - outState.putBoolean(MapboxConstants.STATE_HAS_SAVED_STATE, true); - outState.putParcelable(MapboxConstants.STATE_CAMERA_POSITION, mapboxMap.getCameraPosition()); - outState.putBoolean(MapboxConstants.STATE_DEBUG_ACTIVE, mapboxMap.isDebugActive()); - outState.putString(MapboxConstants.STATE_STYLE_URL, nativeMapView.getStyleUrl()); - outState.putBoolean(MapboxConstants.STATE_MY_LOCATION_ENABLED, mapboxMap.isMyLocationEnabled()); - - // TrackingSettings - TrackingSettings trackingSettings = mapboxMap.getTrackingSettings(); - outState.putInt(MapboxConstants.STATE_MY_LOCATION_TRACKING_MODE, trackingSettings.getMyLocationTrackingMode()); - outState.putInt(MapboxConstants.STATE_MY_BEARING_TRACKING_MODE, trackingSettings.getMyBearingTrackingMode()); - outState.putBoolean(MapboxConstants.STATE_MY_LOCATION_TRACKING_DISMISS, trackingSettings.isDismissLocationTrackingOnGesture()); - outState.putBoolean(MapboxConstants.STATE_MY_BEARING_TRACKING_DISMISS, trackingSettings.isDismissBearingTrackingOnGesture()); - - // UiSettings - UiSettings uiSettings = mapboxMap.getUiSettings(); - outState.putBoolean(MapboxConstants.STATE_ZOOM_ENABLED, uiSettings.isZoomGesturesEnabled()); - outState.putBoolean(MapboxConstants.STATE_ZOOM_ENABLED_CHANGE, uiSettings.isZoomGestureChangeAllowed()); - outState.putBoolean(MapboxConstants.STATE_SCROLL_ENABLED, uiSettings.isScrollGesturesEnabled()); - outState.putBoolean(MapboxConstants.STATE_SCROLL_ENABLED_CHANGE, uiSettings.isScrollGestureChangeAllowed()); - outState.putBoolean(MapboxConstants.STATE_ROTATE_ENABLED, uiSettings.isRotateGesturesEnabled()); - outState.putBoolean(MapboxConstants.STATE_ROTATE_ENABLED_CHANGE, uiSettings.isRotateGestureChangeAllowed()); - outState.putBoolean(MapboxConstants.STATE_TILT_ENABLED, uiSettings.isTiltGesturesEnabled()); - outState.putBoolean(MapboxConstants.STATE_TILT_ENABLED_CHANGE, uiSettings.isTiltGestureChangeAllowed()); - outState.putBoolean(MapboxConstants.STATE_ZOOM_CONTROLS_ENABLED, uiSettings.isZoomControlsEnabled()); - - // UiSettings - Compass - outState.putBoolean(MapboxConstants.STATE_COMPASS_ENABLED, uiSettings.isCompassEnabled()); - outState.putInt(MapboxConstants.STATE_COMPASS_GRAVITY, uiSettings.getCompassGravity()); - outState.putInt(MapboxConstants.STATE_COMPASS_MARGIN_LEFT, uiSettings.getCompassMarginLeft()); - outState.putInt(MapboxConstants.STATE_COMPASS_MARGIN_TOP, uiSettings.getCompassMarginTop()); - outState.putInt(MapboxConstants.STATE_COMPASS_MARGIN_BOTTOM, uiSettings.getCompassMarginBottom()); - outState.putInt(MapboxConstants.STATE_COMPASS_MARGIN_RIGHT, uiSettings.getCompassMarginRight()); - outState.putBoolean(MapboxConstants.STATE_COMPASS_FADE_WHEN_FACING_NORTH, uiSettings.isCompassFadeWhenFacingNorth()); - - // UiSettings - Logo - outState.putInt(MapboxConstants.STATE_LOGO_GRAVITY, uiSettings.getLogoGravity()); - outState.putInt(MapboxConstants.STATE_LOGO_MARGIN_LEFT, uiSettings.getLogoMarginLeft()); - outState.putInt(MapboxConstants.STATE_LOGO_MARGIN_TOP, uiSettings.getLogoMarginTop()); - outState.putInt(MapboxConstants.STATE_LOGO_MARGIN_RIGHT, uiSettings.getLogoMarginRight()); - outState.putInt(MapboxConstants.STATE_LOGO_MARGIN_BOTTOM, uiSettings.getLogoMarginBottom()); - outState.putBoolean(MapboxConstants.STATE_LOGO_ENABLED, uiSettings.isLogoEnabled()); - - // UiSettings - Attribution - outState.putInt(MapboxConstants.STATE_ATTRIBUTION_GRAVITY, uiSettings.getAttributionGravity()); - outState.putInt(MapboxConstants.STATE_ATTRIBUTION_MARGIN_LEFT, uiSettings.getAttributionMarginLeft()); - outState.putInt(MapboxConstants.STATE_ATTRIBUTION_MARGIN_TOP, uiSettings.getAttributionMarginTop()); - outState.putInt(MapboxConstants.STATE_ATTRIBUTION_MARGIN_RIGHT, uiSettings.getAttributionMarginRight()); - outState.putInt(MapboxConstants.STATE_ATTRIBUTION_MARGIN_BOTTOM, uiSettings.getAttributionMarginBottom()); - outState.putBoolean(MapboxConstants.STATE_ATTRIBUTION_ENABLED, uiSettings.isAttributionEnabled()); + // Fire MapLoad + if (savedInstanceState == null) { + Hashtable<String, Object> evt = new Hashtable<>(); + evt.put(MapboxEvent.ATTRIBUTE_EVENT, MapboxEvent.TYPE_MAP_LOAD); + evt.put(MapboxEvent.ATTRIBUTE_CREATED, MapboxEventManager.generateCreateDate()); + MapboxEventManager.getMapboxEventManager().pushEvent(evt); } - - /** - * You must call this method from the parent's {@link Activity#onStart()} or {@link Fragment#onStart()} - */ - @UiThread - public void onStart() { - onStartCalled = true; - - // Register for connectivity changes - connectivityReceiver = new ConnectivityReceiver(); - getContext().registerReceiver(connectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); - - nativeMapView.update(); - myLocationView.onStart(); - - if (TextUtils.isEmpty(nativeMapView.getStyleUrl())) { - // if user hasn't loaded a Style yet, load default for them instead - nativeMapView.setStyleUrl(Style.MAPBOX_STREETS); - } + } + + /** + * You must call this method from the parent's {@link android.app.Activity#onSaveInstanceState(Bundle)} + * or {@link android.app.Fragment#onSaveInstanceState(Bundle)}. + * + * @param outState Pass in the parent's outState. + */ + + @UiThread + public void onSaveInstanceState(@NonNull Bundle outState) { + outState.putBoolean(STATE_HAS_SAVED_STATE, true); + outState.putParcelable(STATE_CAMERA_POSITION, mapboxMap.getCameraPosition()); + outState.putBoolean(STATE_DEBUG_ACTIVE, mapboxMap.isDebugActive()); + outState.putString(STATE_STYLE_URL, nativeMapView.getStyleUrl()); + outState.putBoolean(STATE_MY_LOCATION_ENABLED, mapboxMap.isMyLocationEnabled()); + + // TrackingSettings + TrackingSettings trackingSettings = mapboxMap.getTrackingSettings(); + outState.putInt(STATE_MY_LOCATION_TRACKING_MODE, trackingSettings.getMyLocationTrackingMode()); + outState.putInt(STATE_MY_BEARING_TRACKING_MODE, trackingSettings.getMyBearingTrackingMode()); + outState.putBoolean(STATE_MY_LOCATION_TRACKING_DISMISS, trackingSettings.isDismissLocationTrackingOnGesture()); + outState.putBoolean(STATE_MY_BEARING_TRACKING_DISMISS, trackingSettings.isDismissBearingTrackingOnGesture()); + + // UiSettings + UiSettings uiSettings = mapboxMap.getUiSettings(); + outState.putBoolean(STATE_ZOOM_ENABLED, uiSettings.isZoomGesturesEnabled()); + outState.putBoolean(STATE_ZOOM_ENABLED_CHANGE, uiSettings.isZoomGestureChangeAllowed()); + outState.putBoolean(STATE_SCROLL_ENABLED, uiSettings.isScrollGesturesEnabled()); + outState.putBoolean(STATE_SCROLL_ENABLED_CHANGE, uiSettings.isScrollGestureChangeAllowed()); + outState.putBoolean(STATE_ROTATE_ENABLED, uiSettings.isRotateGesturesEnabled()); + outState.putBoolean(STATE_ROTATE_ENABLED_CHANGE, uiSettings.isRotateGestureChangeAllowed()); + outState.putBoolean(STATE_TILT_ENABLED, uiSettings.isTiltGesturesEnabled()); + outState.putBoolean(STATE_TILT_ENABLED_CHANGE, uiSettings.isTiltGestureChangeAllowed()); + outState.putBoolean(STATE_ZOOM_CONTROLS_ENABLED, uiSettings.isZoomControlsEnabled()); + + // UiSettings - Compass + outState.putBoolean(STATE_COMPASS_ENABLED, uiSettings.isCompassEnabled()); + outState.putInt(STATE_COMPASS_GRAVITY, uiSettings.getCompassGravity()); + outState.putInt(STATE_COMPASS_MARGIN_LEFT, uiSettings.getCompassMarginLeft()); + outState.putInt(STATE_COMPASS_MARGIN_TOP, uiSettings.getCompassMarginTop()); + outState.putInt(STATE_COMPASS_MARGIN_BOTTOM, uiSettings.getCompassMarginBottom()); + outState.putInt(STATE_COMPASS_MARGIN_RIGHT, uiSettings.getCompassMarginRight()); + outState.putBoolean(STATE_COMPASS_FADE_WHEN_FACING_NORTH, uiSettings.isCompassFadeWhenFacingNorth()); + + // UiSettings - Logo + outState.putInt(STATE_LOGO_GRAVITY, uiSettings.getLogoGravity()); + outState.putInt(STATE_LOGO_MARGIN_LEFT, uiSettings.getLogoMarginLeft()); + outState.putInt(STATE_LOGO_MARGIN_TOP, uiSettings.getLogoMarginTop()); + outState.putInt(STATE_LOGO_MARGIN_RIGHT, uiSettings.getLogoMarginRight()); + outState.putInt(STATE_LOGO_MARGIN_BOTTOM, uiSettings.getLogoMarginBottom()); + outState.putBoolean(STATE_LOGO_ENABLED, uiSettings.isLogoEnabled()); + + // UiSettings - Attribution + outState.putInt(STATE_ATTRIBUTION_GRAVITY, uiSettings.getAttributionGravity()); + outState.putInt(STATE_ATTRIBUTION_MARGIN_LEFT, uiSettings.getAttributionMarginLeft()); + outState.putInt(STATE_ATTRIBUTION_MARGIN_TOP, uiSettings.getAttributionMarginTop()); + outState.putInt(STATE_ATTRIBUTION_MARGIN_RIGHT, uiSettings.getAttributionMarginRight()); + outState.putInt(STATE_ATTRIBUTION_MARGIN_BOTTOM, uiSettings.getAttributionMarginBottom()); + outState.putBoolean(STATE_ATTRIBUTION_ENABLED, uiSettings.isAttributionEnabled()); + } + + /** + * You must call this method from the parent's {@link Activity#onStart()} or {@link Fragment#onStart()} + */ + @UiThread + public void onStart() { + onStartCalled = true; + + // Register for connectivity changes + connectivityReceiver = new ConnectivityReceiver(); + getContext().registerReceiver(connectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); + + nativeMapView.update(); + myLocationView.onStart(); + + if (TextUtils.isEmpty(nativeMapView.getStyleUrl())) { + // if user hasn't loaded a Style yet, load default for them instead + nativeMapView.setStyleUrl(Style.MAPBOX_STREETS); } - - /** - * You must call this method from the parent's {@link Activity#onResume()} or {@link Fragment#onResume()}. - */ - @UiThread - public void onResume() { - if (!onStartCalled) { - // TODO: 26/10/16, can be removed after 5.0.0 release - throw new IllegalStateException("MapView#onStart() was not called. " + - "You must call this method from the parent's {@link Activity#onStart()} or {@link Fragment#onStart()}."); - } + } + + /** + * You must call this method from the parent's {@link Activity#onResume()} or {@link Fragment#onResume()}. + */ + @UiThread + public void onResume() { + if (!onStartCalled) { + // TODO: 26/10/16, can be removed after 5.0.0 release + throw new IllegalStateException("MapView#onStart() was not called. " + + "You must call this method from the parent's {@link Activity#onStart()} or {@link Fragment#onStart()}."); } - - /** - * You must call this method from the parent's {@link Activity#onPause()} or {@link Fragment#onPause()}. - */ - @UiThread - public void onPause() { - // replaced by onStop in v5.0.0, keep around for future development + } + + /** + * You must call this method from the parent's {@link Activity#onPause()} or {@link Fragment#onPause()}. + */ + @UiThread + public void onPause() { + // replaced by onStop in v5.0.0, keep around for future development + } + + /** + * You must call this method from the parent's {@link Activity#onStop()} or {@link Fragment#onStop()}. + */ + @UiThread + public void onStop() { + onStopCalled = true; + + // Unregister for connectivity changes + if (connectivityReceiver != null) { + getContext().unregisterReceiver(connectivityReceiver); + connectivityReceiver = null; } - /** - * You must call this method from the parent's {@link Activity#onStop()} or {@link Fragment#onStop()}. - */ - @UiThread - public void onStop() { - onStopCalled = true; - - // Unregister for connectivity changes - if (connectivityReceiver != null) { - getContext().unregisterReceiver(connectivityReceiver); - connectivityReceiver = null; - } + myLocationView.onStop(); + } + + /** + * You must call this method from the parent's {@link Activity#onDestroy()} or {@link Fragment#onDestroy()}. + */ + @UiThread + public void onDestroy() { + if (!onStopCalled) { + // TODO: 26/10/16, can be removed after 5.0.0 release + throw new IllegalStateException("MapView#onStop() was not called. " + + "You must call this method from the parent's {@link Activity#onStop()} or {@link Fragment#onStop()}."); + } - myLocationView.onStop(); + destroyed = true; + nativeMapView.terminateContext(); + nativeMapView.terminateDisplay(); + nativeMapView.destroySurface(); + nativeMapView.destroy(); + nativeMapView = null; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (mapboxMap.getUiSettings().isZoomControlsEnabled()) { + zoomButtonsController.setVisible(true); + } } + return mapGestureDetector.onTouchEvent(event) || super.onTouchEvent(event); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return mapKeyListener.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event); + } + + @Override + public boolean onKeyLongPress(int keyCode, KeyEvent event) { + return mapKeyListener.onKeyLongPress(keyCode, event) || super.onKeyLongPress(keyCode, event); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + return mapKeyListener.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event); + } + + @Override + public boolean onTrackballEvent(MotionEvent event) { + return mapKeyListener.onTrackballEvent(event) || super.onTrackballEvent(event); + } + + @Override + public boolean onGenericMotionEvent(MotionEvent event) { + return mapGestureDetector.onGenericMotionEvent(event) || super.onGenericMotionEvent(event); + } + + @Override + public boolean onHoverEvent(MotionEvent event) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_HOVER_ENTER: + case MotionEvent.ACTION_HOVER_MOVE: + // Show the zoom controls + if (mapboxMap.getUiSettings().isZoomControlsEnabled()) { + zoomButtonsController.setVisible(true); + } + return true; - /** - * You must call this method from the parent's {@link Activity#onDestroy()} or {@link Fragment#onDestroy()}. - */ - @UiThread - public void onDestroy() { - if (!onStopCalled) { - // TODO: 26/10/16, can be removed after 5.0.0 release - throw new IllegalStateException("MapView#onStop() was not called. " + - "You must call this method from the parent's {@link Activity#onStop()} or {@link Fragment#onStop()}."); + case MotionEvent.ACTION_HOVER_EXIT: + // Hide the zoom controls + if (mapboxMap.getUiSettings().isZoomControlsEnabled()) { + zoomButtonsController.setVisible(false); } + return true; - destroyed = true; - nativeMapView.terminateContext(); - nativeMapView.terminateDisplay(); - nativeMapView.destroySurface(); - nativeMapView.destroy(); - nativeMapView = null; + default: + // We are not interested in this event + return false; } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - if (mapboxMap.getUiSettings().isZoomControlsEnabled()) { - zoomButtonsController.setVisible(true); - } + } + + /** + * You must call this method from the parent's {@link Activity#onLowMemory()} or {@link Fragment#onLowMemory()}. + */ + @UiThread + public void onLowMemory() { + 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) { + post(new Runnable() { + @Override + public void run() { + MapboxMap.OnFpsChangedListener listener = mapboxMap.getOnFpsChangedListener(); + if (listener != null) { + listener.onFpsChanged(fps); } - return mapGestureDetector.onTouchEvent(event) || super.onTouchEvent(event); + } + }); + } + + /** + * <p> + * Loads a new map style from the specified URL. + * </p> + * {@code url} can take the following forms: + * <ul> + * <li>{@code Style.*}: load one of the bundled styles in {@link Style}.</li> + * <li>{@code mapbox://styles/<user>/<style>}: + * retrieves the style from a <a href="https://www.mapbox.com/account/">Mapbox account.</a> + * {@code user} is your username. {@code style} is the ID of your custom + * style created in <a href="https://www.mapbox.com/studio">Mapbox Studio</a>.</li> + * <li>{@code http://...} or {@code https://...}: + * retrieves the style over the Internet from any web server.</li> + * <li>{@code asset://...}: + * reads the style from the APK {@code assets/} directory. + * This is used to load a style bundled with your app.</li> + * <li>{@code null}: loads the default {@link Style#MAPBOX_STREETS} style.</li> + * </ul> + * <p> + * This method is asynchronous and will return immediately before the style finishes loading. + * If you wish to wait for the map to finish loading listen for the {@link MapView#DID_FINISH_LOADING_MAP} event. + * </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. + * + * @param url The URL of the map style + * @see Style + */ + public void setStyleUrl(@NonNull String url) { + if (destroyed) { + return; } - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - return mapKeyListener.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event); + // stopgap for https://github.com/mapbox/mapbox-gl-native/issues/6242 + if (TextUtils.isEmpty(nativeMapView.getAccessToken())) { + setAccessToken(MapboxAccountManager.getInstance().getAccessToken()); } - @Override - public boolean onKeyLongPress(int keyCode, KeyEvent event) { - return mapKeyListener.onKeyLongPress(keyCode, event) || super.onKeyLongPress(keyCode, event); + nativeMapView.setStyleUrl(url); + } + + /** + * <p> + * DEPRECATED @see MapboxAccountManager#start(String) + * </p> + * <p> + * Sets the current Mapbox access token used to load map styles and tiles. + * </p> + * <p> + * You must set a valid access token before you call {@link MapView#onCreate(Bundle)} + * or an exception will be thrown. + * </p> + * + * @param accessToken Your public Mapbox access token. + * @see MapView#onCreate(Bundle) + * @deprecated As of release 4.1.0, replaced by + * {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)} + */ + @Deprecated + @UiThread + public void setAccessToken(@NonNull String accessToken) { + if (destroyed) { + return; } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - return mapKeyListener.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event); + // validateAccessToken does the null check + if (!TextUtils.isEmpty(accessToken)) { + accessToken = accessToken.trim(); } - - @Override - public boolean onTrackballEvent(MotionEvent event) { - return mapKeyListener.onTrackballEvent(event) || super.onTrackballEvent(event); + MapboxAccountManager.validateAccessToken(accessToken); + nativeMapView.setAccessToken(accessToken); + } + + /** + * <p> + * DEPRECATED @see MapboxAccountManager#getAccessToken() + * </p> + * <p> + * Returns the current Mapbox access token used to load map styles and tiles. + * </p> + * + * @return The current Mapbox access token. + * @deprecated As of release 4.1.0, replaced by {@link MapboxAccountManager#getAccessToken()} + */ + @Deprecated + @UiThread + @Nullable + public String getAccessToken() { + if (destroyed) { + return ""; } - - @Override - public boolean onGenericMotionEvent(MotionEvent event) { - return mapGestureDetector.onGenericMotionEvent(event) || super.onGenericMotionEvent(event); + return nativeMapView.getAccessToken(); + } + + // + // 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; } - @Override - public boolean onHoverEvent(MotionEvent event) { - switch (event.getActionMasked()) { - case MotionEvent.ACTION_HOVER_ENTER: - case MotionEvent.ACTION_HOVER_MOVE: - // Show the zoom controls - if (mapboxMap.getUiSettings().isZoomControlsEnabled()) { - zoomButtonsController.setVisible(true); - } - return true; - - case MotionEvent.ACTION_HOVER_EXIT: - // Hide the zoom controls - if (mapboxMap.getUiSettings().isZoomControlsEnabled()) { - zoomButtonsController.setVisible(false); - } - return true; - - default: - // We are not interested in this event - return false; - } + if (destroyed) { + return; } - /** - * You must call this method from the parent's {@link Activity#onLowMemory()} or {@link Fragment#onLowMemory()}. - */ - @UiThread - public void onLowMemory() { - 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) { - post(new Runnable() { - @Override - public void run() { - MapboxMap.OnFpsChangedListener listener = mapboxMap.getOnFpsChangedListener(); - if (listener != null) { - listener.onFpsChanged(fps); - } - } - }); + if (!hasSurface) { + return; } - /** - * <p> - * Loads a new map style from the specified URL. - * </p> - * {@code url} can take the following forms: - * <ul> - * <li>{@code Style.*}: load one of the bundled styles in {@link Style}.</li> - * <li>{@code mapbox://styles/<user>/<style>}: - * retrieves the style from a <a href="https://www.mapbox.com/account/">Mapbox account.</a> - * {@code user} is your username. {@code style} is the ID of your custom - * style created in <a href="https://www.mapbox.com/studio">Mapbox Studio</a>.</li> - * <li>{@code http://...} or {@code https://...}: - * retrieves the style over the Internet from any web server.</li> - * <li>{@code asset://...}: - * reads the style from the APK {@code assets/} directory. - * This is used to load a style bundled with your app.</li> - * <li>{@code null}: loads the default {@link Style#MAPBOX_STREETS} style.</li> - * </ul> - * <p> - * This method is asynchronous and will return immediately before the style finishes loading. - * If you wish to wait for the map to finish loading listen for the {@link MapView#DID_FINISH_LOADING_MAP} event. - * </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. - * - * @param url The URL of the map style - * @see Style - */ - public void setStyleUrl(@NonNull String url) { - if (destroyed) { - return; - } + nativeMapView.render(); + } - // stopgap for https://github.com/mapbox/mapbox-gl-native/issues/6242 - if (TextUtils.isEmpty(nativeMapView.getAccessToken())) { - setAccessToken(MapboxAccountManager.getInstance().getAccessToken()); - } - - nativeMapView.setStyleUrl(url); + @Override + protected void onSizeChanged(int width, int height, int oldw, int oldh) { + if (destroyed) { + return; } - /** - * <p> - * DEPRECATED @see MapboxAccountManager#start(String) - * </p> - * <p> - * Sets the current Mapbox access token used to load map styles and tiles. - * </p> - * <p> - * You must set a valid access token before you call {@link MapView#onCreate(Bundle)} - * or an exception will be thrown. - * </p> - * - * @param accessToken Your public Mapbox access token. - * @see MapView#onCreate(Bundle) - * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)} - */ - @Deprecated - @UiThread - public void setAccessToken(@NonNull String accessToken) { - if (destroyed) { - return; - } - // validateAccessToken does the null check - if (!TextUtils.isEmpty(accessToken)) { - accessToken = accessToken.trim(); - } - MapboxAccountManager.validateAccessToken(accessToken); - nativeMapView.setAccessToken(accessToken); + if (!isInEditMode()) { + nativeMapView.resizeView(width, height); } + } - /** - * <p> - * DEPRECATED @see MapboxAccountManager#getAccessToken() - * </p> - * <p> - * Returns the current Mapbox access token used to load map styles and tiles. - * </p> - * - * @return The current Mapbox access token. - * @deprecated As of release 4.1.0, replaced by {@link MapboxAccountManager#getAccessToken()} - */ - @Deprecated - @UiThread - @Nullable - public String getAccessToken() { - if (destroyed) { - return ""; - } - return nativeMapView.getAccessToken(); - } + private class SurfaceCallback implements SurfaceHolder.Callback { - // - // Rendering - // + private Surface surface; - // Called when the map needs to be rerendered - // Called via JNI from NativeMapView - protected void onInvalidate() { - postInvalidate(); + @Override + public void surfaceCreated(SurfaceHolder holder) { + nativeMapView.createSurface(surface = holder.getSurface()); + hasSurface = true; } @Override - public void onDraw(Canvas canvas) { - super.onDraw(canvas); - if (isInEditMode()) { - return; - } - - if (destroyed) { - return; - } - - if (!hasSurface) { - return; - } - - nativeMapView.render(); + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + if (destroyed) { + return; + } + nativeMapView.resizeFramebuffer(width, height); } @Override - protected void onSizeChanged(int width, int height, int oldw, int oldh) { - if (destroyed) { - return; - } + public void surfaceDestroyed(SurfaceHolder holder) { + hasSurface = false; - if (!isInEditMode()) { - nativeMapView.resizeView(width, height); - } + if (nativeMapView != null) { + nativeMapView.destroySurface(); + } + surface.release(); } + } - 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); - } + // This class handles TextureView callbacks + private class SurfaceTextureListener implements TextureView.SurfaceTextureListener { - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - hasSurface = false; + private Surface surface; - if (nativeMapView != null) { - nativeMapView.destroySurface(); - } - surface.release(); - } + // 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; } - // 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; - } - // TODO move to transform. java - compassView.update(mapboxMap.getTransform().getBearing()); - myLocationView.update(); - mapboxMap.getMarkerViewManager().update(); + // 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; - for (InfoWindow infoWindow : mapboxMap.getInfoWindows()) { - infoWindow.update(); - } - } + if (nativeMapView != null) { + nativeMapView.destroySurface(); + } + this.surface.release(); + return true; } - // - // View events - // - - // Called when view is no longer connected + // Called when the format or size of the native surface texture has been changed + // Must handle window resizing here. @Override - @CallSuper - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - // Required by ZoomButtonController (from Android SDK documentation) - if (mapboxMap.getUiSettings().isZoomControlsEnabled()) { - zoomButtonsController.setVisible(false); - } + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { + if (destroyed) { + return; + } - // make sure we don't leak location listener - if (myLocationListener != null) { - // cleanup to prevent memory leak - LocationServices services = LocationServices.getLocationServices(getContext()); - services.removeLocationListener(myLocationListener); - myLocationListener = null; - } + nativeMapView.resizeFramebuffer(width, height); } - // Called when view is hidden and shown + // Called when the SurfaceTexure frame is drawn to screen + // Must sync with UI here @Override - protected void onVisibilityChanged(@NonNull View changedView, int visibility) { - if (isInEditMode()) { - return; - } - - if (mapboxMap != null && mapboxMap.getUiSettings().isZoomControlsEnabled()) { - zoomButtonsController.setVisible(visibility == View.VISIBLE); - } + public void onSurfaceTextureUpdated(SurfaceTexture surface) { + if (destroyed) { + return; + } + // TODO move to transform. java + compassView.update(mapboxMap.getTransform().getBearing()); + myLocationView.update(); + mapboxMap.getMarkerViewManager().update(); + + for (InfoWindow infoWindow : mapboxMap.getInfoWindows()) { + infoWindow.update(); + } + } + } + + // + // View events + // + + // Called when view is no longer connected + @Override + @CallSuper + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + // Required by ZoomButtonController (from Android SDK documentation) + if (mapboxMap.getUiSettings().isZoomControlsEnabled()) { + zoomButtonsController.setVisible(false); } - // - // Connectivity events - // - - // This class handles connectivity changes - private class ConnectivityReceiver extends BroadcastReceiver { - - // Called when an action we are listening to in the manifest has been sent - @Override - public void onReceive(Context context, Intent intent) { - if (!destroyed && intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) { - boolean noConnectivity = intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); - onConnectivityChanged(!noConnectivity); - } - } + // make sure we don't leak location listener + if (myLocationListener != null) { + // cleanup to prevent memory leak + LocationServices services = LocationServices.getLocationServices(getContext()); + services.removeLocationListener(myLocationListener); + myLocationListener = null; } + } - // Called when MapView is being created - private boolean isConnected() { - Context appContext = getContext().getApplicationContext(); - ConnectivityManager connectivityManager = (ConnectivityManager) appContext.getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo(); - return (activeNetwork != null) && activeNetwork.isConnectedOrConnecting(); + // Called when view is hidden and shown + @Override + protected void onVisibilityChanged(@NonNull View changedView, int visibility) { + if (isInEditMode()) { + return; } - // Called when our Internet connectivity has changed - private void onConnectivityChanged(boolean isConnected) { - nativeMapView.setReachability(isConnected); + if (mapboxMap != null && mapboxMap.getUiSettings().isZoomControlsEnabled()) { + zoomButtonsController.setVisible(visibility == View.VISIBLE); } + } - // - // Map events - // + // + // Connectivity events + // - /** - * <p> - * Add a callback that's invoked when the displayed map view changes. - * </p> - * To remove the callback, use {@link MapView#removeOnMapChangedListener(OnMapChangedListener)}. - * - * @param listener The callback that's invoked on every frame rendered to the map view. - * @see MapView#removeOnMapChangedListener(OnMapChangedListener) - */ - public void addOnMapChangedListener(@Nullable OnMapChangedListener listener) { - if (listener != null) { - nativeMapView.addOnMapChangedListener(listener); - } - } + // This class handles connectivity changes + private class ConnectivityReceiver extends BroadcastReceiver { - /** - * Remove a callback added with {@link MapView#addOnMapChangedListener(OnMapChangedListener)} - * - * @param listener The previously added callback to remove. - * @see MapView#addOnMapChangedListener(OnMapChangedListener) - */ - public void removeOnMapChangedListener(@Nullable OnMapChangedListener listener) { - if (listener != null) { - nativeMapView.removeOnMapChangedListener(listener); - } + // Called when an action we are listening to in the manifest has been sent + @Override + public void onReceive(Context context, Intent intent) { + if (!destroyed && intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) { + boolean noConnectivity = intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); + onConnectivityChanged(!noConnectivity); + } } - - // Called when the map view transformation has changed - // Called via JNI from NativeMapView - // Forward to any listeners - protected void onMapChanged(int mapChange) { - nativeMapView.onMapChangedEventDispatch(mapChange); + } + + // Called when MapView is being created + private boolean isConnected() { + Context appContext = getContext().getApplicationContext(); + ConnectivityManager connectivityManager = (ConnectivityManager) + appContext.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo(); + return (activeNetwork != null) && activeNetwork.isConnectedOrConnecting(); + } + + // Called when our Internet connectivity has changed + private void onConnectivityChanged(boolean isConnected) { + nativeMapView.setReachability(isConnected); + } + + // + // Map events + // + + /** + * <p> + * Add a callback that's invoked when the displayed map view changes. + * </p> + * To remove the callback, use {@link MapView#removeOnMapChangedListener(OnMapChangedListener)}. + * + * @param listener The callback that's invoked on every frame rendered to the map view. + * @see MapView#removeOnMapChangedListener(OnMapChangedListener) + */ + public void addOnMapChangedListener(@Nullable OnMapChangedListener listener) { + if (listener != null) { + nativeMapView.addOnMapChangedListener(listener); } - - - /** - * Sets a callback object which will be triggered when the {@link MapboxMap} instance is ready to be used. - * - * @param callback The callback object that will be triggered when the map is ready to be used. - */ - @UiThread - public void getMapAsync(final OnMapReadyCallback callback) { - if (!initialLoad && callback != null) { - callback.onMapReady(mapboxMap); - } else { - if (callback != null) { - onMapReadyCallbackList.add(callback); - } - } + } + + /** + * Remove a callback added with {@link MapView#addOnMapChangedListener(OnMapChangedListener)} + * + * @param listener The previously added callback to remove. + * @see MapView#addOnMapChangedListener(OnMapChangedListener) + */ + public void removeOnMapChangedListener(@Nullable OnMapChangedListener listener) { + if (listener != null) { + nativeMapView.removeOnMapChangedListener(listener); } - - MapboxMap getMapboxMap() { - return mapboxMap; + } + + // Called when the map view transformation has changed + // Called via JNI from NativeMapView + // Forward to any listeners + protected void onMapChanged(int mapChange) { + nativeMapView.onMapChangedEventDispatch(mapChange); + } + + + /** + * Sets a callback object which will be triggered when the {@link MapboxMap} instance is ready to be used. + * + * @param callback The callback object that will be triggered when the map is ready to be used. + */ + @UiThread + public void getMapAsync(final OnMapReadyCallback callback) { + if (!initialLoad && callback != null) { + callback.onMapReady(mapboxMap); + } else { + if (callback != null) { + onMapReadyCallbackList.add(callback); + } } - - void setMapboxMap(MapboxMap mapboxMap) { - this.mapboxMap = mapboxMap; + } + + MapboxMap getMapboxMap() { + return mapboxMap; + } + + void setMapboxMap(MapboxMap mapboxMap) { + this.mapboxMap = mapboxMap; + } + + // + // Snapshot API + // + + @UiThread + void snapshot(@NonNull final MapboxMap.SnapshotReadyCallback callback, @Nullable final Bitmap bitmap) { + snapshotRequest = new SnapshotRequest(bitmap, callback); + nativeMapView.scheduleTakeSnapshot(); + nativeMapView.render(); + } + + // Called when the snapshot method was executed + // Called via JNI from NativeMapView + // Forward to any listeners + protected void onSnapshotReady(byte[] bytes) { + if (snapshotRequest != null && bytes != null) { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inBitmap = snapshotRequest.getBitmap(); // the old Bitmap to be reused + options.inMutable = true; + options.inSampleSize = 1; + Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); + + MapboxMap.SnapshotReadyCallback callback = snapshotRequest.getCallback(); + if (callback != null) { + callback.onSnapshotReady(bitmap); + } } + } - // - // Snapshot API - // + private class SnapshotRequest { + private Bitmap bitmap; + private MapboxMap.SnapshotReadyCallback callback; - @UiThread - void snapshot(@NonNull final MapboxMap.SnapshotReadyCallback callback, @Nullable final Bitmap bitmap) { - snapshotRequest = new SnapshotRequest(bitmap, callback); - nativeMapView.scheduleTakeSnapshot(); - nativeMapView.render(); + SnapshotRequest(Bitmap bitmap, MapboxMap.SnapshotReadyCallback callback) { + this.bitmap = bitmap; + this.callback = callback; } - // Called when the snapshot method was executed - // Called via JNI from NativeMapView - // Forward to any listeners - protected void onSnapshotReady(byte[] bytes) { - if (snapshotRequest != null && bytes != null) { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inBitmap = snapshotRequest.getBitmap(); // the old Bitmap to be reused - options.inMutable = true; - options.inSampleSize = 1; - Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); - - MapboxMap.SnapshotReadyCallback callback = snapshotRequest.getCallback(); - if (callback != null) { - callback.onSnapshotReady(bitmap); - } - } + public Bitmap getBitmap() { + return bitmap; } - private class SnapshotRequest { - private Bitmap bitmap; - private MapboxMap.SnapshotReadyCallback callback; - - SnapshotRequest(Bitmap bitmap, MapboxMap.SnapshotReadyCallback callback) { - this.bitmap = bitmap; - this.callback = callback; - } - - public Bitmap getBitmap() { - return bitmap; - } - - public MapboxMap.SnapshotReadyCallback getCallback() { - return callback; - } + public MapboxMap.SnapshotReadyCallback getCallback() { + return callback; } + } - private static class AttributionOnClickListener implements View.OnClickListener, DialogInterface.OnClickListener { + private static class AttributionOnClickListener implements View.OnClickListener, DialogInterface.OnClickListener { - private static final int ATTRIBUTION_INDEX_IMPROVE_THIS_MAP = 2; - private static final int ATTRIBUTION_INDEX_TELEMETRY_SETTINGS = 3; - private MapView mapView; + private static final int ATTRIBUTION_INDEX_IMPROVE_THIS_MAP = 2; + private static final int ATTRIBUTION_INDEX_TELEMETRY_SETTINGS = 3; + private MapView mapView; - AttributionOnClickListener(MapView mapView) { - super(); - this.mapView = mapView; - } + AttributionOnClickListener(MapView mapView) { + super(); + this.mapView = mapView; + } - // Called when someone presses the attribution icon - @Override - public void onClick(View view) { - AlertDialog.Builder builder = new AlertDialog.Builder(mapView.getContext(), R.style.mapbox_AlertDialogStyle); - builder.setTitle(R.string.mapbox_attributionsDialogTitle); - String[] items = mapView.getContext().getResources().getStringArray(R.array.mapbox_attribution_names); - builder.setAdapter(new ArrayAdapter<>(mapView.getContext(), R.layout.mapbox_attribution_list_item, items), this); - builder.show(); - } + // Called when someone presses the attribution icon + @Override + public void onClick(View view) { + AlertDialog.Builder builder = new AlertDialog.Builder(mapView.getContext(), R.style.mapbox_AlertDialogStyle); + builder.setTitle(R.string.mapbox_attributionsDialogTitle); + String[] items = mapView.getContext().getResources().getStringArray(R.array.mapbox_attribution_names); + builder.setAdapter(new ArrayAdapter<>(mapView.getContext(), R.layout.mapbox_attribution_list_item, items), this); + builder.show(); + } - // Called when someone selects an attribution, 'Improve this map' adds location data to the url - @Override - public void onClick(DialogInterface dialog, int which) { - final Context context = ((Dialog) dialog).getContext(); - if (which == ATTRIBUTION_INDEX_TELEMETRY_SETTINGS) { - AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.mapbox_AlertDialogStyle); - builder.setTitle(R.string.mapbox_attributionTelemetryTitle); - builder.setMessage(R.string.mapbox_attributionTelemetryMessage); - builder.setPositiveButton(R.string.mapbox_attributionTelemetryPositive, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - MapboxEventManager.getMapboxEventManager().setTelemetryEnabled(true); - dialog.cancel(); - } - }); - builder.setNeutralButton(R.string.mapbox_attributionTelemetryNeutral, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - String url = context.getResources().getStringArray(R.array.mapbox_attribution_links)[3]; - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(url)); - context.startActivity(intent); - dialog.cancel(); - } - }); - builder.setNegativeButton(R.string.mapbox_attributionTelemetryNegative, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - MapboxEventManager.getMapboxEventManager().setTelemetryEnabled(false); - dialog.cancel(); - } - }); - - builder.show(); - return; - } - String url = context.getResources().getStringArray(R.array.mapbox_attribution_links)[which]; - if (which == ATTRIBUTION_INDEX_IMPROVE_THIS_MAP) { - CameraPosition cameraPosition = mapView.getMapboxMap().getCameraPosition(); - if (cameraPosition != null) { - url = String.format(url, cameraPosition.target.getLongitude(), - cameraPosition.target.getLatitude(), (int) cameraPosition.zoom); - } - } + // Called when someone selects an attribution, 'Improve this map' adds location data to the url + @Override + public void onClick(DialogInterface dialog, int which) { + final Context context = ((Dialog) dialog).getContext(); + if (which == ATTRIBUTION_INDEX_TELEMETRY_SETTINGS) { + AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.mapbox_AlertDialogStyle); + builder.setTitle(R.string.mapbox_attributionTelemetryTitle); + builder.setMessage(R.string.mapbox_attributionTelemetryMessage); + builder.setPositiveButton(R.string.mapbox_attributionTelemetryPositive, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + MapboxEventManager.getMapboxEventManager().setTelemetryEnabled(true); + dialog.cancel(); + } + }); + builder.setNeutralButton(R.string.mapbox_attributionTelemetryNeutral, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String url = context.getResources().getStringArray(R.array.mapbox_attribution_links)[3]; Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); context.startActivity(intent); - } - } + dialog.cancel(); + } + }); + builder.setNegativeButton(R.string.mapbox_attributionTelemetryNegative, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + MapboxEventManager.getMapboxEventManager().setTelemetryEnabled(false); + dialog.cancel(); + } + }); - /** - * Definition of a map change event. - * - * @see MapView.OnMapChangedListener#onMapChanged(int) - */ - @IntDef({REGION_WILL_CHANGE, - REGION_WILL_CHANGE_ANIMATED, - REGION_IS_CHANGING, - REGION_DID_CHANGE, - REGION_DID_CHANGE_ANIMATED, - WILL_START_LOADING_MAP, - DID_FINISH_LOADING_MAP, - DID_FAIL_LOADING_MAP, - WILL_START_RENDERING_FRAME, - DID_FINISH_RENDERING_FRAME, - DID_FINISH_RENDERING_FRAME_FULLY_RENDERED, - WILL_START_RENDERING_MAP, - DID_FINISH_RENDERING_MAP, - DID_FINISH_RENDERING_MAP_FULLY_RENDERED, - DID_FINISH_LOADING_STYLE, - SOURCE_DID_CHANGE - }) - @Retention(RetentionPolicy.SOURCE) - public @interface MapChange { + builder.show(); + return; + } + String url = context.getResources().getStringArray(R.array.mapbox_attribution_links)[which]; + if (which == ATTRIBUTION_INDEX_IMPROVE_THIS_MAP) { + CameraPosition cameraPosition = mapView.getMapboxMap().getCameraPosition(); + if (cameraPosition != null) { + url = String.format(url, cameraPosition.target.getLongitude(), + cameraPosition.target.getLatitude(), (int) cameraPosition.zoom); + } + } + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(url)); + context.startActivity(intent); } - - /** - * This event is triggered whenever the currently displayed map region is about to changing - * without an animation. - * <p> - * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. - * </p> - * - * @see MapChange - * @see MapView.OnMapChangedListener - */ - public static final int REGION_WILL_CHANGE = 0; - - /** - * This event is triggered whenever the currently displayed map region is about to changing - * with an animation. - * <p - * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)} - * </p> - * - * @see MapChange - * @see MapView.OnMapChangedListener - */ - public static final int REGION_WILL_CHANGE_ANIMATED = 1; - - /** - * This event is triggered whenever the currently displayed map region is changing. - * <p> - * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. - * </p> - * - * @see MapChange - * @see MapView.OnMapChangedListener - */ - public static final int REGION_IS_CHANGING = 2; - - /** - * This event is triggered whenever the currently displayed map region finished changing - * without an animation. - * <p> - * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. - * </p> - * - * @see MapChange - * @see MapView.OnMapChangedListener - */ - public static final int REGION_DID_CHANGE = 3; - - /** - * This event is triggered whenever the currently displayed map region finished changing - * with an animation. - * <p> - * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. - * </p> - * - * @see MapChange - * @see MapView.OnMapChangedListener - */ - public static final int REGION_DID_CHANGE_ANIMATED = 4; - - /** - * This event is triggered when the map is about to start loading a new map style. - * <p> - * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. - * </p> - * - * @see MapChange - * @see MapView.OnMapChangedListener - */ - public static final int WILL_START_LOADING_MAP = 5; - - /** - * This is triggered when the map has successfully loaded a new map style. - * <p> - * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. - * </p> - * - * @see MapChange - * @see MapView.OnMapChangedListener - */ - public static final int DID_FINISH_LOADING_MAP = 6; - - /** - * This event is triggered when the map has failed to load a new map style. - * <p> - * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. - * </p> - * - * @see MapChange - * @see MapView.OnMapChangedListener - */ - public static final int DID_FAIL_LOADING_MAP = 7; - - /** - * This event is triggered when the map will start rendering a frame. - * <p> - * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. - * </p> - * - * @see MapChange - * @see MapView.OnMapChangedListener - */ - public static final int WILL_START_RENDERING_FRAME = 8; - - /** - * This event is triggered when the map finished rendering a frame. - * <p> - * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. - * </p> - * - * @see MapChange - * @see MapView.OnMapChangedListener - */ - public static final int DID_FINISH_RENDERING_FRAME = 9; - - /** - * This event is triggered when the map finished rendeirng the frame fully. - * <p> - * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. - * </p> - * - * @see MapChange - * @see MapView.OnMapChangedListener - */ - public static final int DID_FINISH_RENDERING_FRAME_FULLY_RENDERED = 10; - - /** - * This event is triggered when the map will start rendering the map. - * <p> - * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. - * </p> - * - * @see MapChange - * @see MapView.OnMapChangedListener - */ - public static final int WILL_START_RENDERING_MAP = 11; - - /** - * This event is triggered when the map finished rendering the map. - * <p> - * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. - * </p> - * - * @see MapChange - * @see MapView.OnMapChangedListener - */ - public static final int DID_FINISH_RENDERING_MAP = 12; - - /** - * This event is triggered when the map is fully rendered. - * <p> - * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. - * </p> - * - * @see MapChange - * @see MapView.OnMapChangedListener - */ - public static final int DID_FINISH_RENDERING_MAP_FULLY_RENDERED = 13; - - + } + + /** + * Definition of a map change event. + * + * @see MapView.OnMapChangedListener#onMapChanged(int) + */ + @IntDef( {REGION_WILL_CHANGE, + REGION_WILL_CHANGE_ANIMATED, + REGION_IS_CHANGING, + REGION_DID_CHANGE, + REGION_DID_CHANGE_ANIMATED, + WILL_START_LOADING_MAP, + DID_FINISH_LOADING_MAP, + DID_FAIL_LOADING_MAP, + WILL_START_RENDERING_FRAME, + DID_FINISH_RENDERING_FRAME, + DID_FINISH_RENDERING_FRAME_FULLY_RENDERED, + WILL_START_RENDERING_MAP, + DID_FINISH_RENDERING_MAP, + DID_FINISH_RENDERING_MAP_FULLY_RENDERED, + DID_FINISH_LOADING_STYLE, + SOURCE_DID_CHANGE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MapChange { + } + + /** + * This event is triggered whenever the currently displayed map region is about to changing + * without an animation. + * <p> + * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. + * </p> + * + * @see MapChange + * @see MapView.OnMapChangedListener + */ + public static final int REGION_WILL_CHANGE = 0; + + /** + * This event is triggered whenever the currently displayed map region is about to changing + * with an animation. + * <p> + * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)} + * </p> + * + * @see MapChange + * @see MapView.OnMapChangedListener + */ + public static final int REGION_WILL_CHANGE_ANIMATED = 1; + + /** + * This event is triggered whenever the currently displayed map region is changing. + * <p> + * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. + * </p> + * + * @see MapChange + * @see MapView.OnMapChangedListener + */ + public static final int REGION_IS_CHANGING = 2; + + /** + * This event is triggered whenever the currently displayed map region finished changing + * without an animation. + * <p> + * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. + * </p> + * + * @see MapChange + * @see MapView.OnMapChangedListener + */ + public static final int REGION_DID_CHANGE = 3; + + /** + * This event is triggered whenever the currently displayed map region finished changing + * with an animation. + * <p> + * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. + * </p> + * + * @see MapChange + * @see MapView.OnMapChangedListener + */ + public static final int REGION_DID_CHANGE_ANIMATED = 4; + + /** + * This event is triggered when the map is about to start loading a new map style. + * <p> + * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. + * </p> + * + * @see MapChange + * @see MapView.OnMapChangedListener + */ + public static final int WILL_START_LOADING_MAP = 5; + + /** + * This is triggered when the map has successfully loaded a new map style. + * <p> + * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. + * </p> + * + * @see MapChange + * @see MapView.OnMapChangedListener + */ + public static final int DID_FINISH_LOADING_MAP = 6; + + /** + * This event is triggered when the map has failed to load a new map style. + * <p> + * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. + * </p> + * + * @see MapChange + * @see MapView.OnMapChangedListener + */ + public static final int DID_FAIL_LOADING_MAP = 7; + + /** + * This event is triggered when the map will start rendering a frame. + * <p> + * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. + * </p> + * + * @see MapChange + * @see MapView.OnMapChangedListener + */ + public static final int WILL_START_RENDERING_FRAME = 8; + + /** + * This event is triggered when the map finished rendering a frame. + * <p> + * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. + * </p> + * + * @see MapChange + * @see MapView.OnMapChangedListener + */ + public static final int DID_FINISH_RENDERING_FRAME = 9; + + /** + * This event is triggered when the map finished rendeirng the frame fully. + * <p> + * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. + * </p> + * + * @see MapChange + * @see MapView.OnMapChangedListener + */ + public static final int DID_FINISH_RENDERING_FRAME_FULLY_RENDERED = 10; + + /** + * This event is triggered when the map will start rendering the map. + * <p> + * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. + * </p> + * + * @see MapChange + * @see MapView.OnMapChangedListener + */ + public static final int WILL_START_RENDERING_MAP = 11; + + /** + * This event is triggered when the map finished rendering the map. + * <p> + * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. + * </p> + * + * @see MapChange + * @see MapView.OnMapChangedListener + */ + public static final int DID_FINISH_RENDERING_MAP = 12; + + /** + * This event is triggered when the map is fully rendered. + * <p> + * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. + * </p> + * + * @see MapChange + * @see MapView.OnMapChangedListener + */ + public static final int DID_FINISH_RENDERING_MAP_FULLY_RENDERED = 13; + + + /** + * This {@link MapChange} is triggered when a style has finished loading. + * <p> + * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. + * </p> + * + * @see MapChange + * @see MapView.OnMapChangedListener + */ + public static final int DID_FINISH_LOADING_STYLE = 14; + + + /** + * This {@link MapChange} is triggered when a source attribution changes. + * <p> + * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. + * </p> + * + * @see MapChange + * @see MapView.OnMapChangedListener + */ + public static final int SOURCE_DID_CHANGE = 15; + + /** + * Interface definition for a callback to be invoked when the displayed map view changes. + * <p> + * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. + * </p> + * + * @see MapView#addOnMapChangedListener(OnMapChangedListener) + * @see MapView.MapChange + */ + public interface OnMapChangedListener { /** - * This {@link MapChange} is triggered when a style has finished loading. - * <p> - * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. - * </p> + * Called when the displayed map view changes. * - * @see MapChange - * @see MapView.OnMapChangedListener + * @param change Type of map change event, one of {@link #REGION_WILL_CHANGE}, + * {@link #REGION_WILL_CHANGE_ANIMATED}, + * {@link #REGION_IS_CHANGING}, + * {@link #REGION_DID_CHANGE}, + * {@link #REGION_DID_CHANGE_ANIMATED}, + * {@link #WILL_START_LOADING_MAP}, + * {@link #DID_FAIL_LOADING_MAP}, + * {@link #DID_FINISH_LOADING_MAP}, + * {@link #WILL_START_RENDERING_FRAME}, + * {@link #DID_FINISH_RENDERING_FRAME}, + * {@link #DID_FINISH_RENDERING_FRAME_FULLY_RENDERED}, + * {@link #WILL_START_RENDERING_MAP}, + * {@link #DID_FINISH_RENDERING_MAP}, + * {@link #DID_FINISH_RENDERING_MAP_FULLY_RENDERED}. */ - public static final int DID_FINISH_LOADING_STYLE = 14; + void onMapChanged(@MapChange int change); + } + // This class handles input events from the zoom control buttons + // Zoom controls allow single touch only devices to zoom in and out + private class OnZoomListener implements ZoomButtonsController.OnZoomListener { - /** - * This {@link MapChange} is triggered when a source attribution changes. - * <p> - * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. - * </p> - * - * @see MapChange - * @see MapView.OnMapChangedListener - */ - public static final int SOURCE_DID_CHANGE = 15; + private UiSettings uiSettings; + private Transform transform; - /** - * Interface definition for a callback to be invoked when the displayed map view changes. - * <p> - * Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}. - * </p> - * - * @see MapView#addOnMapChangedListener(OnMapChangedListener) - * @see MapView.MapChange - */ - public interface OnMapChangedListener { - /** - * Called when the displayed map view changes. - * - * @param change Type of map change event, one of {@link #REGION_WILL_CHANGE}, - * {@link #REGION_WILL_CHANGE_ANIMATED}, - * {@link #REGION_IS_CHANGING}, - * {@link #REGION_DID_CHANGE}, - * {@link #REGION_DID_CHANGE_ANIMATED}, - * {@link #WILL_START_LOADING_MAP}, - * {@link #DID_FAIL_LOADING_MAP}, - * {@link #DID_FINISH_LOADING_MAP}, - * {@link #WILL_START_RENDERING_FRAME}, - * {@link #DID_FINISH_RENDERING_FRAME}, - * {@link #DID_FINISH_RENDERING_FRAME_FULLY_RENDERED}, - * {@link #WILL_START_RENDERING_MAP}, - * {@link #DID_FINISH_RENDERING_MAP}, - * {@link #DID_FINISH_RENDERING_MAP_FULLY_RENDERED}. - */ - void onMapChanged(@MapChange int change); + OnZoomListener(MapboxMap mapboxMap) { + this.uiSettings = mapboxMap.getUiSettings(); + this.transform = mapboxMap.getTransform(); } - // This class handles input events from the zoom control buttons - // Zoom controls allow single touch only devices to zoom in and out - private class OnZoomListener implements ZoomButtonsController.OnZoomListener { - - private UiSettings uiSettings; - private Transform transform; - - OnZoomListener(MapboxMap mapboxMap) { - this.uiSettings = mapboxMap.getUiSettings(); - this.transform = mapboxMap.getTransform(); - } - - // Not used - @Override - public void onVisibilityChanged(boolean visible) { - // Ignore - } + // Not used + @Override + public void onVisibilityChanged(boolean visible) { + // Ignore + } - // Called when user pushes a zoom button - @Override - public void onZoom(boolean zoomIn) { - if (!uiSettings.isZoomGesturesEnabled()) { - return; - } - transform.zoom(zoomIn); - } + // Called when user pushes a zoom button + @Override + public void onZoom(boolean zoomIn) { + if (!uiSettings.isZoomGesturesEnabled()) { + return; + } + transform.zoom(zoomIn); } + } - private class FocalPointInvalidator implements FocalPointChangeListener { + private class FocalPointInvalidator implements FocalPointChangeListener { - @Override - public void onFocalPointChanged(PointF pointF) { - mapGestureDetector.setFocalPoint(pointF); - } + @Override + public void onFocalPointChanged(PointF pointF) { + mapGestureDetector.setFocalPoint(pointF); } + } - private class RegisterTouchListener implements MapboxMap.OnRegisterTouchListener { + private class RegisterTouchListener implements MapboxMap.OnRegisterTouchListener { - @Override - public void onRegisterMapClickListener(MapboxMap.OnMapClickListener listener) { - mapGestureDetector.setOnMapClickListener(listener); - } - - @Override - public void onRegisterMapLongClickListener(MapboxMap.OnMapLongClickListener listener) { - mapGestureDetector.setOnMapLongClickListener(listener); - } + @Override + public void onRegisterMapClickListener(MapboxMap.OnMapClickListener listener) { + mapGestureDetector.setOnMapClickListener(listener); + } - @Override - public void onRegisterScrollListener(MapboxMap.OnScrollListener listener) { - mapGestureDetector.setOnScrollListener(listener); - } + @Override + public void onRegisterMapLongClickListener(MapboxMap.OnMapLongClickListener listener) { + mapGestureDetector.setOnMapLongClickListener(listener); + } - @Override - public void onRegisterFlingListener(MapboxMap.OnFlingListener listener) { - mapGestureDetector.setOnFlingListener(listener); - } + @Override + public void onRegisterScrollListener(MapboxMap.OnScrollListener listener) { + mapGestureDetector.setOnScrollListener(listener); } - NativeMapView getNativeMapView() { - return nativeMapView; + @Override + public void onRegisterFlingListener(MapboxMap.OnFlingListener listener) { + mapGestureDetector.setOnFlingListener(listener); } + } + + NativeMapView getNativeMapView() { + return nativeMapView; + } } 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 7459256869..eabdfb34b8 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,1906 +59,1913 @@ import timber.log.Timber; */ public final class MapboxMap { - private final NativeMapView nativeMapView; - private final UiSettings uiSettings; - private final TrackingSettings trackingSettings; - private final Projection projection; - private final Transform transform; - private final AnnotationManager annotationManager; - private final MyLocationViewSettings myLocationViewSettings; - private final OnRegisterTouchListener onRegisterTouchListener; - - private MapboxMap.OnFpsChangedListener onFpsChangedListener; - - MapboxMap(NativeMapView map, Transform transform, UiSettings ui, TrackingSettings tracking, MyLocationViewSettings myLocationView, - Projection projection, OnRegisterTouchListener listener, AnnotationManager annotations) { - this.nativeMapView = map; - this.uiSettings = ui; - this.trackingSettings = tracking; - this.projection = projection; - this.myLocationViewSettings = myLocationView; - this.annotationManager = annotations.bind(this); - this.transform = transform; - this.onRegisterTouchListener = listener; - } - - void initialise(@NonNull Context context, @NonNull MapboxMapOptions options) { - transform.initialise(this, options); - uiSettings.initialise(context, options); - myLocationViewSettings.initialise(options); - setMyLocationEnabled(options.getLocationEnabled()); - - // api base url - setDebugActive(options.getDebugActive()); - setApiBaseUrl(options); - setAccessToken(options); - setStyleUrl(options); - } - - // Style - - @Nullable - @UiThread - public Layer getLayer(@NonNull String layerId) { - return nativeMapView.getLayer(layerId); - } - - /** - * Tries to cast the Layer to T, returns null if it's another type. - * - * @param layerId the layer id used to look up a layer - * @param <T> the generic attribute of a Layer - * @return the casted Layer, null if another type - */ - @Nullable - @UiThread - public <T extends Layer> T getLayerAs(@NonNull String layerId) { - try { - //noinspection unchecked - return (T) nativeMapView.getLayer(layerId); - } catch (ClassCastException e) { - Timber.e(String.format("Layer: %s is a different type: %s", layerId, e.getMessage())); - return null; - } - } - - /** - * Adds the layer to the map. The layer must be newly created and not added to the map before - * - * @param layer the layer to add - */ - @UiThread - public void addLayer(@NonNull Layer layer) { - addLayer(layer, null); - } - - /** - * Adds the layer to the map. The layer must be newly created and not added to the map before - * - * @param layer the layer to add - * @param before the layer id to add this layer before - */ - @UiThread - public void addLayer(@NonNull Layer layer, String before) { - nativeMapView.addLayer(layer, before); - } - - /** - * Removes the layer. Any references to the layer become invalid and should not be used anymore - * - * @param layerId the layer to remove - * @throws NoSuchLayerException - */ - @UiThread - public void removeLayer(@NonNull String layerId) throws NoSuchLayerException { - nativeMapView.removeLayer(layerId); - } - - /** - * Removes the layer. The reference is re-usable after this and can be re-added - * - * @param layer the layer to remove - * @throws NoSuchLayerException - */ - @UiThread - public void removeLayer(@NonNull Layer layer) throws NoSuchLayerException { - nativeMapView.removeLayer(layer); - } - - @Nullable - @UiThread - public Source getSource(@NonNull String sourceId) { - return nativeMapView.getSource(sourceId); - } - - /** - * Tries to cast the Source to T, returns null if it's another type. - * - * @param sourceId the id used to look up a layer - * @param <T> the generic type of a Source - * @return the casted Source, null if another type - */ - @Nullable - @UiThread - public <T extends Source> T getSourceAs(@NonNull String sourceId) { - try { - //noinspection unchecked - return (T) nativeMapView.getSource(sourceId); - } catch (ClassCastException e) { - Timber.e(String.format("Source: %s is a different type: %s", sourceId, e.getMessage())); - return null; - } - } - - /** - * Adds the source to the map. The source must be newly created and not added to the map before - * - * @param source the source to add - */ - @UiThread - public void addSource(@NonNull Source source) { - nativeMapView.addSource(source); - } - - /** - * Removes the source. Any references to the source become invalid and should not be used anymore - * - * @param sourceId the source to remove - * @throws NoSuchSourceException - */ - @UiThread - public void removeSource(@NonNull String sourceId) throws NoSuchSourceException { - nativeMapView.removeSource(sourceId); - } - - /** - * Removes the source, preserving the reverence for re-use - * - * @param source the source to remove - * @throws NoSuchSourceException - */ - @UiThread - public void removeSource(@NonNull Source source) throws NoSuchSourceException { - nativeMapView.removeSource(source); - } - - /** - * Adds an image to be used in the map's style - * - * @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); - } - - /** - * Removes an image from the map's style - * - * @param name the name of the image to remove - */ - @UiThread - public void removeImage(String name) { - nativeMapView.removeImage(name); - } - - // - // MinZoom - // - - /** - * <p> - * Sets the minimum zoom level the map can be displayed at. - * </p> - * - * @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); - } - - /** - * <p> - * Gets the maximum zoom level the map can be displayed at. - * </p> - * - * @return The minimum zoom level. - */ - @UiThread - public double getMinZoomLevel() { - return transform.getMinZoom(); - } - - // - // MaxZoom - // - - /** - * <p> - * Sets the maximum zoom level the map can be displayed at. - * </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); - } - - /** - * <p> - * Gets the maximum zoom level the map can be displayed at. - * </p> - * - * @return The maximum zoom level. - */ - @UiThread - public double getMaxZoomLevel() { - return transform.getMaxZoom(); - } - - // - // UiSettings - // - - /** - * Gets the user interface settings for the map. - * - * @return the UiSettings associated with this map - */ - public UiSettings getUiSettings() { - return uiSettings; - } - - // - // TrackingSettings - // - - /** - * Gets the tracking interface settings for the map. - * - * @return the TrackingSettings asssociated with this map - */ - public TrackingSettings getTrackingSettings() { - return trackingSettings; - } - - // - // MyLocationViewSettings - // - - /** - * Gets the settings of the user location for the map. - * - * @return the MyLocationViewSettings associated with this map - */ - public MyLocationViewSettings getMyLocationViewSettings() { - return myLocationViewSettings; - } - - // - // Projection - // - - /** - * Get the Projection object that you can use to convert between screen coordinates and latitude/longitude - * coordinates. - * - * @return the Projection associated with this map - */ - public Projection getProjection() { - return projection; - } - - // - // Camera API - // - - /** - * Cancels ongoing animations. - * <p> - * This invokes the {@link CancelableCallback} for ongoing camera updates. - * </p> - */ - public void cancelTransitions() { - transform.cancelTransitions(); - } - - /** - * Gets the current position of the camera. - * The CameraPosition returned is a snapshot of the current position, and will not automatically update when the - * camera moves. - * - * @return The current position of the Camera. - */ - public final CameraPosition getCameraPosition() { - return transform.getCameraPosition(); - } - - /** - * Repositions the camera according to the cameraPosition. - * The move is instantaneous, and a subsequent getCameraPosition() will reflect the new position. - * See CameraUpdateFactory for a set of updates. - * - * @param cameraPosition the camera position to set - */ - public void setCameraPosition(@NonNull CameraPosition cameraPosition) { - moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), null); - } - - /** - * Repositions the camera according to the instructions defined in the update. - * The move is instantaneous, and a subsequent getCameraPosition() will reflect the new position. - * See CameraUpdateFactory for a set of updates. - * - * @param update The change that should be applied to the camera. - */ - @UiThread - public final void moveCamera(CameraUpdate update) { - moveCamera(update, null); - // MapChange.REGION_DID_CHANGE_ANIMATED is not called for `jumpTo` - // invalidate camera position to provide OnCameraChange event. - invalidateCameraPosition(); - } - - /** - * Repositions the camera according to the instructions defined in the update. - * The move is instantaneous, and a subsequent getCameraPosition() will reflect the new position. - * See CameraUpdateFactory for a set of updates. - * - * @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 - public void run() { - transform.moveCamera(MapboxMap.this, update, callback); - } - }); - } - - /** - * Gradually move the camera by the default duration, zoom will not be affected unless specified - * within {@link CameraUpdate}. If {@link #getCameraPosition()} is called during the animation, - * it will return the current location of the camera in flight. - * - * @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); - } - - /** - * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected - * unless specified within {@link CameraUpdate}. If {@link #getCameraPosition()} is called - * during the animation, it will return the current location of the camera in flight. - * - * @param update The change that should be applied to the camera. - * @param durationMs The duration of the animation in milliseconds. This must be strictly - * 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); - } - - /** - * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected - * unless specified within {@link CameraUpdate}. A callback can be used to be notified when - * easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it - * will return the current location of the camera in flight. - * <p> - * Note that this will cancel location tracking mode if enabled. - * </p> - * - * @param update The change that should be applied to the camera. - * @param durationMs The duration of the animation in milliseconds. This must be strictly - * positive, otherwise an IllegalArgumentException will be thrown. - * @param callback An optional callback to be notified from the main thread when the animation - * stops. If the animation stops due to its natural completion, the callback - * will be notified with onFinish(). If the animation stops due to interruption - * by a later camera movement or a user gesture, onCancel() will be called. - * 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); - } - - /** - * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected - * unless specified within {@link CameraUpdate}. A callback can be used to be notified when - * easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it - * will return the current location of the camera in flight. - * <p> - * Note that this will cancel location tracking mode if enabled. - * </p> - * - * @param update The change that should be applied to the camera. - * @param durationMs The duration of the animation in milliseconds. This must be strictly - * 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); - } - - /** - * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected - * unless specified within {@link CameraUpdate}. A callback can be used to be notified when - * easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it - * will return the current location of the camera in flight. - * <p> - * Note that this will cancel location tracking mode if enabled. - * </p> - * - * @param update The change that should be applied to the camera. - * @param durationMs The duration of the animation in milliseconds. This must be strictly - * positive, otherwise an IllegalArgumentException will be thrown. - * @param easingInterpolator True for easing interpolator, false for linear. - * @param callback An optional callback to be notified from the main thread when the animation - * stops. If the animation stops due to its natural completion, the callback - * will be notified with onFinish(). If the animation stops due to interruption - * 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( - CameraUpdate update, int durationMs, boolean easingInterpolator, final MapboxMap.CancelableCallback callback) { - // dismiss tracking, moving camera is equal to a gesture - easeCamera(update, durationMs, easingInterpolator, true, callback); - } - - /** - * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected - * unless specified within {@link CameraUpdate}. A callback can be used to be notified when - * easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it - * will return the current location of the camera in flight. - * <p> - * Note that this will cancel location tracking mode if enabled. - * </p> - * - * @param update The change that should be applied to the camera. - * @param durationMs The duration of the animation in milliseconds. This must be strictly - * positive, otherwise an IllegalArgumentException will be thrown. - * @param resetTrackingMode True to reset tracking modes if required, false to ignore - * @param easingInterpolator True for easing interpolator, false for linear. - * @param callback An optional callback to be notified from the main thread when the animation - * stops. If the animation stops due to its natural completion, the callback - * will be notified with onFinish(). If the animation stops due to interruption - * 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 boolean resetTrackingMode, final MapboxMap.CancelableCallback callback) { - new Handler().post(new Runnable() { - @Override - public void run() { - transform.easeCamera(MapboxMap.this, update, durationMs, easingInterpolator, resetTrackingMode, callback); - } - }); - } - - /** - * Animate the camera to a new location defined within {@link CameraUpdate} using a transition - * animation that evokes powered flight. The animation will last the default amount of time. - * During the animation, a call to {@link #getCameraPosition()} returns an intermediate location - * of the camera in flight. - * - * @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); - } - - /** - * Animate the camera to a new location defined within {@link CameraUpdate} using a transition - * animation that evokes powered flight. The animation will last the default amount of time. A - * callback can be used to be notified when animating the camera stops. During the animation, a - * call to {@link #getCameraPosition()} returns an intermediate location of the camera in flight. - * - * @param update The change that should be applied to the camera. - * @param callback The callback to invoke from the main thread when the animation stops. If the - * animation completes normally, onFinish() is called; otherwise, onCancel() is - * 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); - } - - /** - * Animate the camera to a new location defined within {@link CameraUpdate} using a transition - * animation that evokes powered flight. The animation will last a specified amount of time - * given in milliseconds. During the animation, a call to {@link #getCameraPosition()} returns - * an intermediate location of the camera in flight. - * - * @param update The change that should be applied to the camera. - * @param durationMs The duration of the animation in milliseconds. This must be strictly - * 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); - } - - /** - * Animate the camera to a new location defined within {@link CameraUpdate} using a transition - * animation that evokes powered flight. The animation will last a specified amount of time - * given in milliseconds. A callback can be used to be notified when animating the camera stops. - * During the animation, a call to {@link #getCameraPosition()} returns an intermediate location - * of the camera in flight. - * - * @param update The change that should be applied to the camera. - * @param durationMs The duration of the animation in milliseconds. This must be strictly - * positive, otherwise an IllegalArgumentException will be thrown. - * @param callback An optional callback to be notified from the main thread when the animation - * stops. If the animation stops due to its natural completion, the callback - * will be notified with onFinish(). If the animation stops due to interruption - * by a later camera movement or a user gesture, onCancel() will be called. - * Do not update or animate the camera from within onCancel(). If a callback - * 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() { - @Override - public void run() { - transform.animateCamera(MapboxMap.this, update, durationMs, callback); - } - }); - } - - /** - * Invalidates the current camera position by reconstructing it from mbgl - */ - void invalidateCameraPosition() { - CameraPosition cameraPosition = transform.invalidateCameraPosition(); - if (cameraPosition != null) { - transform.updateCameraPosition(cameraPosition); - } - } - - // - // Reset North - // - - /** - * Resets the map view to face north. - */ - public void resetNorth() { - transform.resetNorth(); - } - - // - // Debug - // - - /** - * Returns whether the map debug information is currently shown. - * - * @return If true, map debug information is currently shown. - */ - @UiThread - public boolean isDebugActive() { - return nativeMapView.getDebug(); - } - - /** - * <p> - * Changes whether the map debug information is shown. - * </p> - * The default value is false. - * - * @param debugActive If true, map debug information is shown. - */ - @UiThread - public void setDebugActive(boolean debugActive) { - nativeMapView.setDebug(debugActive); - } - - /** - * <p> - * Cycles through the map debug options. - * </p> - * The value of isDebugActive reflects whether there are - * any map debug options enabled or disabled. - * - * @see #isDebugActive() - */ - @UiThread - public void cycleDebugOptions() { - nativeMapView.cycleDebugOptions(); - } - - // - // API endpoint config - // - - private void setApiBaseUrl(@NonNull MapboxMapOptions options) { - String apiBaseUrl = options.getApiBaseUrl(); - if (!TextUtils.isEmpty(apiBaseUrl)) { - nativeMapView.setApiBaseUrl(apiBaseUrl); - } - } - - // - // Styling - // - - /** - * <p> - * Loads a new map style from the specified URL. - * </p> - * {@code url} can take the following forms: - * <ul> - * <li>{@code Style.*}: load one of the bundled styles in {@link Style}.</li> - * <li>{@code mapbox://styles/<user>/<style>}: - * retrieves the style from a <a href="https://www.mapbox.com/account/">Mapbox account.</a> - * {@code user} is your username. {@code style} is the ID of your custom - * style created in <a href="https://www.mapbox.com/studio">Mapbox Studio</a>.</li> - * <li>{@code http://...} or {@code https://...}: - * retrieves the style over the Internet from any web server.</li> - * <li>{@code asset://...}: - * reads the style from the APK {@code assets/} directory. - * This is used to load a style bundled with your app.</li> - * <li>{@code null}: loads the default {@link Style#MAPBOX_STREETS} style.</li> - * </ul> - * <p> - * This method is asynchronous and will return immediately before the style finishes loading. - * If you wish to wait for the map to finish loading listen for the {@link MapView#DID_FINISH_LOADING_MAP} event. - * </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. - * - * @param url The URL of the map style - * @see Style - */ - @UiThread - public void setStyleUrl(@NonNull String url) { - nativeMapView.setStyleUrl(url); - } - - /** - * <p> - * Loads a new map style from the specified bundled style. - * </p> - * <p> - * This method is asynchronous and will return immediately before the style finishes loading. - * If you wish to wait for the map to finish loading listen for the {@link MapView#DID_FINISH_LOADING_MAP} event. - * </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. - * - * @param style The bundled style. Accepts one of the values from {@link Style}. - * @see Style - * @deprecated use {@link #setStyleUrl(String)} instead with versioned url methods from {@link Style} - */ - @UiThread - @Deprecated - public void setStyle(@Style.StyleUrl String style) { - setStyleUrl(style); - } - - /** - * Loads a new map style from MapboxMapOptions if available. - * - * @param options the object containing the style url - */ - private void setStyleUrl(@NonNull MapboxMapOptions options) { - String style = options.getStyle(); - if (!TextUtils.isEmpty(style)) { - setStyleUrl(style); - } - } - - /** - * <p> - * Returns the map style currently displayed in the map view. - * </p> - * If the default style is currently displayed, a URL will be returned instead of null. - * - * @return The URL of the map style. - */ - @UiThread - @NonNull - public String getStyleUrl() { - return nativeMapView.getStyleUrl(); - } - - // - // Access token - // - - /** - * <p> - * DEPRECATED @see MapboxAccountManager#start(String) - * </p> - * <p> - * Sets the current Mapbox access token used to load map styles and tiles. - * </p> - * - * @param accessToken Your public Mapbox access token. - * @see MapView#setAccessToken(String) - * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)} - */ - @Deprecated - @UiThread - public void setAccessToken(@NonNull String accessToken) { - nativeMapView.setAccessToken(accessToken); - } - - /** - * <p> - * DEPRECATED @see MapboxAccountManager#getAccessToken() - * </p> - * <p> - * Returns the current Mapbox access token used to load map styles and tiles. - * </p> - * - * @return The current Mapbox access token. - * @deprecated As of release 4.1.0, replaced by {@link MapboxAccountManager#getAccessToken()} - */ - @Deprecated - @UiThread - @Nullable - public String getAccessToken() { - return nativeMapView.getAccessToken(); - } - - private void setAccessToken(@NonNull MapboxMapOptions options) { - String accessToken = options.getAccessToken(); - if (!TextUtils.isEmpty(accessToken)) { - nativeMapView.setAccessToken(accessToken); - } else { - nativeMapView.setAccessToken(MapboxAccountManager.getInstance().getAccessToken()); - } - } - - // - // Annotations - // - - /** - * <p> - * Adds a marker to this map. - * </p> - * The marker's icon is rendered on the map at the location {@code Marker.position}. - * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet. - * - * @param markerOptions A marker options object that defines how to render the marker. - * @return The {@code Marker} that was added to the map. - */ - @UiThread - @NonNull - public Marker addMarker(@NonNull MarkerOptions markerOptions) { - return annotationManager.addMarker(markerOptions, this); - } - - /** - * <p> - * Adds a marker to this map. - * </p> - * The marker's icon is rendered on the map at the location {@code Marker.position}. - * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet. - * - * @param markerOptions A marker options object that defines how to render the marker. - * @return The {@code Marker} that was added to the map. - */ - @UiThread - @NonNull - public Marker addMarker(@NonNull BaseMarkerOptions markerOptions) { - return annotationManager.addMarker(markerOptions, this); - } - - /** - * <p> - * Adds a marker to this map. - * </p> - * The marker's icon is rendered on the map at the location {@code Marker.position}. - * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet. - * - * @param markerOptions A marker options object that defines how to render the marker. - * @return The {@code Marker} that was added to the map. - */ - @UiThread - @NonNull - public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions) { - return annotationManager.addMarker(markerOptions, this, null); - } + private final NativeMapView nativeMapView; + private final UiSettings uiSettings; + private final TrackingSettings trackingSettings; + private final Projection projection; + private final Transform transform; + private final AnnotationManager annotationManager; + private final MyLocationViewSettings myLocationViewSettings; + private final OnRegisterTouchListener onRegisterTouchListener; + + private MapboxMap.OnFpsChangedListener onFpsChangedListener; + + MapboxMap(NativeMapView map, Transform transform, UiSettings ui, TrackingSettings tracking, + MyLocationViewSettings myLocationView, Projection projection, OnRegisterTouchListener listener, + AnnotationManager annotations) { + this.nativeMapView = map; + this.uiSettings = ui; + this.trackingSettings = tracking; + this.projection = projection; + this.myLocationViewSettings = myLocationView; + this.annotationManager = annotations.bind(this); + this.transform = transform; + this.onRegisterTouchListener = listener; + } + + void initialise(@NonNull Context context, @NonNull MapboxMapOptions options) { + transform.initialise(this, options); + uiSettings.initialise(context, options); + myLocationViewSettings.initialise(options); + setMyLocationEnabled(options.getLocationEnabled()); + + // api base url + setDebugActive(options.getDebugActive()); + setApiBaseUrl(options); + setAccessToken(options); + setStyleUrl(options); + } + + // Style + + @Nullable + @UiThread + public Layer getLayer(@NonNull String layerId) { + return nativeMapView.getLayer(layerId); + } + + /** + * Tries to cast the Layer to T, returns null if it's another type. + * + * @param layerId the layer id used to look up a layer + * @param <T> the generic attribute of a Layer + * @return the casted Layer, null if another type + */ + @Nullable + @UiThread + public <T extends Layer> T getLayerAs(@NonNull String layerId) { + try { + //noinspection unchecked + return (T) nativeMapView.getLayer(layerId); + } catch (ClassCastException classCastException) { + Timber.e(String.format("Layer: %s is a different type: %s", layerId, classCastException.getMessage())); + return null; + } + } + + /** + * Adds the layer to the map. The layer must be newly created and not added to the map before + * + * @param layer the layer to add + */ + @UiThread + public void addLayer(@NonNull Layer layer) { + addLayer(layer, null); + } + + /** + * Adds the layer to the map. The layer must be newly created and not added to the map before + * + * @param layer the layer to add + * @param before the layer id to add this layer before + */ + @UiThread + public void addLayer(@NonNull Layer layer, String before) { + nativeMapView.addLayer(layer, before); + } + + /** + * Removes the layer. Any references to the layer become invalid and should not be used anymore + * + * @param layerId the layer to remove + * @throws NoSuchLayerException Thrown when the layer doesn't exist. + */ + @UiThread + public void removeLayer(@NonNull String layerId) throws NoSuchLayerException { + nativeMapView.removeLayer(layerId); + } + + /** + * Removes the layer. The reference is re-usable after this and can be re-added + * + * @param layer the layer to remove + * @throws NoSuchLayerException Thrown when the layer doesn't exist. + */ + @UiThread + public void removeLayer(@NonNull Layer layer) throws NoSuchLayerException { + nativeMapView.removeLayer(layer); + } + + @Nullable + @UiThread + public Source getSource(@NonNull String sourceId) { + return nativeMapView.getSource(sourceId); + } + + /** + * Tries to cast the Source to T, returns null if it's another type. + * + * @param sourceId the id used to look up a layer + * @param <T> the generic type of a Source + * @return the casted Source, null if another type + */ + @Nullable + @UiThread + public <T extends Source> T getSourceAs(@NonNull String sourceId) { + try { + //noinspection unchecked + return (T) nativeMapView.getSource(sourceId); + } catch (ClassCastException classCastException) { + Timber.e(String.format("Source: %s is a different type: %s", sourceId, classCastException.getMessage())); + return null; + } + } + + /** + * Adds the source to the map. The source must be newly created and not added to the map before + * + * @param source the source to add + */ + @UiThread + public void addSource(@NonNull Source source) { + nativeMapView.addSource(source); + } + + /** + * Removes the source. Any references to the source become invalid and should not be used anymore + * + * @param sourceId the source to remove + * @throws NoSuchSourceException Thrown when the source doesn't exist. + */ + @UiThread + public void removeSource(@NonNull String sourceId) throws NoSuchSourceException { + nativeMapView.removeSource(sourceId); + } + + /** + * Removes the source, preserving the reverence for re-use + * + * @param source the source to remove + * @throws NoSuchSourceException Thrown when the source doesn't exist. + */ + @UiThread + public void removeSource(@NonNull Source source) throws NoSuchSourceException { + nativeMapView.removeSource(source); + } + + /** + * Adds an image to be used in the map's style + * + * @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); + } + + /** + * Removes an image from the map's style + * + * @param name the name of the image to remove + */ + @UiThread + public void removeImage(String name) { + nativeMapView.removeImage(name); + } + + // + // MinZoom + // + + /** + * <p> + * Sets the minimum zoom level the map can be displayed at. + * </p> + * + * @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); + } + + /** + * <p> + * Gets the maximum zoom level the map can be displayed at. + * </p> + * + * @return The minimum zoom level. + */ + @UiThread + public double getMinZoomLevel() { + return transform.getMinZoom(); + } + + // + // MaxZoom + // + + /** + * <p> + * Sets the maximum zoom level the map can be displayed at. + * </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); + } + + /** + * <p> + * Gets the maximum zoom level the map can be displayed at. + * </p> + * + * @return The maximum zoom level. + */ + @UiThread + public double getMaxZoomLevel() { + return transform.getMaxZoom(); + } + + // + // UiSettings + // + + /** + * Gets the user interface settings for the map. + * + * @return the UiSettings associated with this map + */ + public UiSettings getUiSettings() { + return uiSettings; + } + + // + // TrackingSettings + // + + /** + * Gets the tracking interface settings for the map. + * + * @return the TrackingSettings asssociated with this map + */ + public TrackingSettings getTrackingSettings() { + return trackingSettings; + } + + // + // MyLocationViewSettings + // + + /** + * Gets the settings of the user location for the map. + * + * @return the MyLocationViewSettings associated with this map + */ + public MyLocationViewSettings getMyLocationViewSettings() { + return myLocationViewSettings; + } + + // + // Projection + // + + /** + * Get the Projection object that you can use to convert between screen coordinates and latitude/longitude + * coordinates. + * + * @return the Projection associated with this map + */ + public Projection getProjection() { + return projection; + } + + // + // Camera API + // + + /** + * Cancels ongoing animations. + * <p> + * This invokes the {@link CancelableCallback} for ongoing camera updates. + * </p> + */ + public void cancelTransitions() { + transform.cancelTransitions(); + } + + /** + * Gets the current position of the camera. + * The CameraPosition returned is a snapshot of the current position, and will not automatically update when the + * camera moves. + * + * @return The current position of the Camera. + */ + public final CameraPosition getCameraPosition() { + return transform.getCameraPosition(); + } + + /** + * Repositions the camera according to the cameraPosition. + * The move is instantaneous, and a subsequent getCameraPosition() will reflect the new position. + * See CameraUpdateFactory for a set of updates. + * + * @param cameraPosition the camera position to set + */ + public void setCameraPosition(@NonNull CameraPosition cameraPosition) { + moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), null); + } + + /** + * Repositions the camera according to the instructions defined in the update. + * The move is instantaneous, and a subsequent getCameraPosition() will reflect the new position. + * See CameraUpdateFactory for a set of updates. + * + * @param update The change that should be applied to the camera. + */ + @UiThread + public final void moveCamera(CameraUpdate update) { + moveCamera(update, null); + // MapChange.REGION_DID_CHANGE_ANIMATED is not called for `jumpTo` + // invalidate camera position to provide OnCameraChange event. + invalidateCameraPosition(); + } + + /** + * Repositions the camera according to the instructions defined in the update. + * The move is instantaneous, and a subsequent getCameraPosition() will reflect the new position. + * See CameraUpdateFactory for a set of updates. + * + * @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 + public void run() { + transform.moveCamera(MapboxMap.this, update, callback); + } + }); + } + + /** + * Gradually move the camera by the default duration, zoom will not be affected unless specified + * within {@link CameraUpdate}. If {@link #getCameraPosition()} is called during the animation, + * it will return the current location of the camera in flight. + * + * @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); + } + + /** + * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected + * unless specified within {@link CameraUpdate}. If {@link #getCameraPosition()} is called + * during the animation, it will return the current location of the camera in flight. + * + * @param update The change that should be applied to the camera. + * @param durationMs The duration of the animation in milliseconds. This must be strictly + * 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); + } + + /** + * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected + * unless specified within {@link CameraUpdate}. A callback can be used to be notified when + * easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it + * will return the current location of the camera in flight. + * <p> + * Note that this will cancel location tracking mode if enabled. + * </p> + * + * @param update The change that should be applied to the camera. + * @param durationMs The duration of the animation in milliseconds. This must be strictly + * positive, otherwise an IllegalArgumentException will be thrown. + * @param callback An optional callback to be notified from the main thread when the animation + * stops. If the animation stops due to its natural completion, the callback + * will be notified with onFinish(). If the animation stops due to interruption + * by a later camera movement or a user gesture, onCancel() will be called. + * 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); + } + + /** + * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected + * unless specified within {@link CameraUpdate}. A callback can be used to be notified when + * easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it + * will return the current location of the camera in flight. + * <p> + * Note that this will cancel location tracking mode if enabled. + * </p> + * + * @param update The change that should be applied to the camera. + * @param durationMs The duration of the animation in milliseconds. This must be strictly + * 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); + } + + /** + * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected + * unless specified within {@link CameraUpdate}. A callback can be used to be notified when + * easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it + * will return the current location of the camera in flight. + * <p> + * Note that this will cancel location tracking mode if enabled. + * </p> + * + * @param update The change that should be applied to the camera. + * @param durationMs The duration of the animation in milliseconds. This must be strictly + * positive, otherwise an IllegalArgumentException will be thrown. + * @param easingInterpolator True for easing interpolator, false for linear. + * @param callback An optional callback to be notified from the main thread when the animation + * stops. If the animation stops due to its natural completion, the callback + * will be notified with onFinish(). If the animation stops due to interruption + * 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( + CameraUpdate update, int durationMs, boolean easingInterpolator, final MapboxMap.CancelableCallback callback) { + // dismiss tracking, moving camera is equal to a gesture + easeCamera(update, durationMs, easingInterpolator, true, callback); + } + + /** + * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected + * unless specified within {@link CameraUpdate}. A callback can be used to be notified when + * easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it + * will return the current location of the camera in flight. + * <p> + * Note that this will cancel location tracking mode if enabled. + * </p> + * + * @param update The change that should be applied to the camera. + * @param durationMs The duration of the animation in milliseconds. This must be strictly + * positive, otherwise an IllegalArgumentException will be thrown. + * @param resetTrackingMode True to reset tracking modes if required, false to ignore + * @param easingInterpolator True for easing interpolator, false for linear. + * @param callback An optional callback to be notified from the main thread when the animation + * stops. If the animation stops due to its natural completion, the callback + * will be notified with onFinish(). If the animation stops due to interruption + * 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 boolean resetTrackingMode, final MapboxMap.CancelableCallback callback) { + new Handler().post(new Runnable() { + @Override + public void run() { + transform.easeCamera(MapboxMap.this, update, durationMs, easingInterpolator, resetTrackingMode, callback); + } + }); + } + + /** + * Animate the camera to a new location defined within {@link CameraUpdate} using a transition + * animation that evokes powered flight. The animation will last the default amount of time. + * During the animation, a call to {@link #getCameraPosition()} returns an intermediate location + * of the camera in flight. + * + * @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); + } + + /** + * Animate the camera to a new location defined within {@link CameraUpdate} using a transition + * animation that evokes powered flight. The animation will last the default amount of time. A + * callback can be used to be notified when animating the camera stops. During the animation, a + * call to {@link #getCameraPosition()} returns an intermediate location of the camera in flight. + * + * @param update The change that should be applied to the camera. + * @param callback The callback to invoke from the main thread when the animation stops. If the + * animation completes normally, onFinish() is called; otherwise, onCancel() is + * 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); + } + + /** + * Animate the camera to a new location defined within {@link CameraUpdate} using a transition + * animation that evokes powered flight. The animation will last a specified amount of time + * given in milliseconds. During the animation, a call to {@link #getCameraPosition()} returns + * an intermediate location of the camera in flight. + * + * @param update The change that should be applied to the camera. + * @param durationMs The duration of the animation in milliseconds. This must be strictly + * 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); + } + + /** + * Animate the camera to a new location defined within {@link CameraUpdate} using a transition + * animation that evokes powered flight. The animation will last a specified amount of time + * given in milliseconds. A callback can be used to be notified when animating the camera stops. + * During the animation, a call to {@link #getCameraPosition()} returns an intermediate location + * of the camera in flight. + * + * @param update The change that should be applied to the camera. + * @param durationMs The duration of the animation in milliseconds. This must be strictly + * positive, otherwise an IllegalArgumentException will be thrown. + * @param callback An optional callback to be notified from the main thread when the animation + * stops. If the animation stops due to its natural completion, the callback + * will be notified with onFinish(). If the animation stops due to interruption + * by a later camera movement or a user gesture, onCancel() will be called. + * Do not update or animate the camera from within onCancel(). If a callback + * 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() { + @Override + public void run() { + transform.animateCamera(MapboxMap.this, update, durationMs, callback); + } + }); + } + + /** + * Invalidates the current camera position by reconstructing it from mbgl + */ + void invalidateCameraPosition() { + CameraPosition cameraPosition = transform.invalidateCameraPosition(); + if (cameraPosition != null) { + transform.updateCameraPosition(cameraPosition); + } + } + + // + // Reset North + // + + /** + * Resets the map view to face north. + */ + public void resetNorth() { + transform.resetNorth(); + } + + // + // Debug + // + + /** + * Returns whether the map debug information is currently shown. + * + * @return If true, map debug information is currently shown. + */ + @UiThread + public boolean isDebugActive() { + return nativeMapView.getDebug(); + } + + /** + * <p> + * Changes whether the map debug information is shown. + * </p> + * The default value is false. + * + * @param debugActive If true, map debug information is shown. + */ + @UiThread + public void setDebugActive(boolean debugActive) { + nativeMapView.setDebug(debugActive); + } + + /** + * <p> + * Cycles through the map debug options. + * </p> + * The value of isDebugActive reflects whether there are + * any map debug options enabled or disabled. + * + * @see #isDebugActive() + */ + @UiThread + public void cycleDebugOptions() { + nativeMapView.cycleDebugOptions(); + } + + // + // API endpoint config + // + + private void setApiBaseUrl(@NonNull MapboxMapOptions options) { + String apiBaseUrl = options.getApiBaseUrl(); + if (!TextUtils.isEmpty(apiBaseUrl)) { + nativeMapView.setApiBaseUrl(apiBaseUrl); + } + } + + // + // Styling + // + + /** + * <p> + * Loads a new map style from the specified bundled style. + * </p> + * <p> + * This method is asynchronous and will return immediately before the style finishes loading. + * If you wish to wait for the map to finish loading listen for the {@link MapView#DID_FINISH_LOADING_MAP} event. + * </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. + * + * @param style The bundled style. Accepts one of the values from {@link Style}. + * @see Style + * @deprecated use {@link #setStyleUrl(String)} instead with versioned url methods from {@link Style} + */ + @UiThread + @Deprecated + public void setStyle(@Style.StyleUrl String style) { + setStyleUrl(style); + } + + /** + * <p> + * Loads a new map style from the specified URL. + * </p> + * {@code url} can take the following forms: + * <ul> + * <li>{@code Style.*}: load one of the bundled styles in {@link Style}.</li> + * <li>{@code mapbox://styles/<user>/<style>}: + * retrieves the style from a <a href="https://www.mapbox.com/account/">Mapbox account.</a> + * {@code user} is your username. {@code style} is the ID of your custom + * style created in <a href="https://www.mapbox.com/studio">Mapbox Studio</a>.</li> + * <li>{@code http://...} or {@code https://...}: + * retrieves the style over the Internet from any web server.</li> + * <li>{@code asset://...}: + * reads the style from the APK {@code assets/} directory. + * This is used to load a style bundled with your app.</li> + * <li>{@code null}: loads the default {@link Style#MAPBOX_STREETS} style.</li> + * </ul> + * <p> + * This method is asynchronous and will return immediately before the style finishes loading. + * If you wish to wait for the map to finish loading listen for the {@link MapView#DID_FINISH_LOADING_MAP} event. + * </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. + * + * @param url The URL of the map style + * @see Style + */ + @UiThread + public void setStyleUrl(@NonNull String url) { + nativeMapView.setStyleUrl(url); + } + + /** + * Loads a new map style from MapboxMapOptions if available. + * + * @param options the object containing the style url + */ + private void setStyleUrl(@NonNull MapboxMapOptions options) { + String style = options.getStyle(); + if (!TextUtils.isEmpty(style)) { + setStyleUrl(style); + } + } + + /** + * <p> + * Returns the map style currently displayed in the map view. + * </p> + * If the default style is currently displayed, a URL will be returned instead of null. + * + * @return The URL of the map style. + */ + @UiThread + @NonNull + public String getStyleUrl() { + return nativeMapView.getStyleUrl(); + } + + // + // Access token + // + + /** + * <p> + * DEPRECATED @see MapboxAccountManager#getAccessToken() + * </p> + * <p> + * Returns the current Mapbox access token used to load map styles and tiles. + * </p> + * + * @return The current Mapbox access token. + * @deprecated As of release 4.1.0, replaced by {@link MapboxAccountManager#getAccessToken()} + */ + @Deprecated + @UiThread + @Nullable + public String getAccessToken() { + return nativeMapView.getAccessToken(); + } + + /** + * <p> + * DEPRECATED @see MapboxAccountManager#start(String) + * </p> + * <p> + * Sets the current Mapbox access token used to load map styles and tiles. + * </p> + * + * @param accessToken Your public Mapbox access token. + * @see MapView#setAccessToken(String) + * @deprecated As of release 4.1.0, replaced by + * {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)} + */ + @Deprecated + @UiThread + public void setAccessToken(@NonNull String accessToken) { + nativeMapView.setAccessToken(accessToken); + } + + private void setAccessToken(@NonNull MapboxMapOptions options) { + String accessToken = options.getAccessToken(); + if (!TextUtils.isEmpty(accessToken)) { + nativeMapView.setAccessToken(accessToken); + } else { + nativeMapView.setAccessToken(MapboxAccountManager.getInstance().getAccessToken()); + } + } + + // + // Annotations + // + + /** + * <p> + * Adds a marker to this map. + * </p> + * The marker's icon is rendered on the map at the location {@code Marker.position}. + * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet. + * + * @param markerOptions A marker options object that defines how to render the marker. + * @return The {@code Marker} that was added to the map. + */ + @UiThread + @NonNull + public Marker addMarker(@NonNull MarkerOptions markerOptions) { + return annotationManager.addMarker(markerOptions, this); + } + + /** + * <p> + * Adds a marker to this map. + * </p> + * The marker's icon is rendered on the map at the location {@code Marker.position}. + * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet. + * + * @param markerOptions A marker options object that defines how to render the marker. + * @return The {@code Marker} that was added to the map. + */ + @UiThread + @NonNull + public Marker addMarker(@NonNull BaseMarkerOptions markerOptions) { + return annotationManager.addMarker(markerOptions, this); + } + + /** + * <p> + * Adds a marker to this map. + * </p> + * The marker's icon is rendered on the map at the location {@code Marker.position}. + * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet. + * + * @param markerOptions A marker options object that defines how to render the marker. + * @return The {@code Marker} that was added to the map. + */ + @UiThread + @NonNull + public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions) { + return annotationManager.addMarker(markerOptions, this, null); + } + + + /** + * <p> + * Adds a marker to this map. + * </p> + * The marker's icon is rendered on the map at the location {@code Marker.position}. + * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet. + * + * @param markerOptions A marker options object that defines how to render the marker. + * @param onMarkerViewAddedListener Callback invoked when the View has been added to the map. + * @return The {@code Marker} that was added to the map. + */ + @UiThread + @NonNull + public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions, + final MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener) { + return annotationManager.addMarker(markerOptions, this, onMarkerViewAddedListener); + } + + @UiThread + @NonNull + public List<MarkerView> addMarkerViews(@NonNull List<? extends + BaseMarkerViewOptions> markerViewOptions) { + return annotationManager.addMarkerViews(markerViewOptions, this); + } + + @UiThread + @NonNull + public List<MarkerView> getMarkerViewsInRect(@NonNull RectF rect) { + return annotationManager.getMarkerViewsInRect(rect); + } + + /** + * <p> + * Adds multiple markers to this map. + * </p> + * The marker's icon is rendered on the map at the location {@code Marker.position}. + * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet. + * + * @param 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) { + return annotationManager.addMarkers(markerOptionsList, this); + } + + /** + * <p> + * Updates a marker on this map. Does nothing if the marker isn't already added. + * </p> + * + * @param updatedMarker An updated marker object. + */ + @UiThread + public void updateMarker(@NonNull Marker updatedMarker) { + annotationManager.updateMarker(updatedMarker, this); + } + + /** + * Adds a polyline to this map. + * + * @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); + } + + /** + * Adds multiple polylines to this map. + * + * @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); + } + + /** + * Update a polyline on this map. + * + * @param polyline An updated polyline object. + */ + @UiThread + public void updatePolyline(Polyline polyline) { + annotationManager.updatePolyline(polyline); + } + + /** + * Adds a polygon to this map. + * + * @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); + } + + /** + * Adds multiple polygons to this map. + * + * @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); + } + + + /** + * Update a polygon on this map. + * + * @param polygon An updated polygon object. + */ + @UiThread + public void updatePolygon(Polygon polygon) { + annotationManager.updatePolygon(polygon); + } + + /** + * <p> + * Convenience method for removing a Marker from the map. + * </p> + * Calls removeAnnotation() internally + * + * @param marker Marker to remove + */ + @UiThread + public void removeMarker(@NonNull Marker marker) { + annotationManager.removeAnnotation(marker); + } + + /** + * <p> + * Convenience method for removing a Polyline from the map. + * </p> + * Calls removeAnnotation() internally + * + * @param polyline Polyline to remove + */ + @UiThread + public void removePolyline(@NonNull Polyline polyline) { + annotationManager.removeAnnotation(polyline); + } + + /** + * <p> + * Convenience method for removing a Polygon from the map. + * </p> + * Calls removeAnnotation() internally + * + * @param polygon Polygon to remove + */ + @UiThread + public void removePolygon(@NonNull Polygon polygon) { + annotationManager.removeAnnotation(polygon); + } + + /** + * Removes an annotation from the map. + * + * @param annotation The annotation object to remove. + */ + @UiThread + public void removeAnnotation(@NonNull Annotation annotation) { + annotationManager.removeAnnotation(annotation); + } + + /** + * Removes an annotation from the map + * + * @param id The identifier associated to the annotation to be removed + */ + @UiThread + public void removeAnnotation(long id) { + annotationManager.removeAnnotation(id); + } + + /** + * Removes multiple annotations from the map. + * + * @param annotationList A list of annotation objects to remove. + */ + @UiThread + public void removeAnnotations(@NonNull List<? extends Annotation> annotationList) { + annotationManager.removeAnnotations(annotationList); + } + + /** + * Removes all annotations from the map. + */ + @UiThread + public void removeAnnotations() { + annotationManager.removeAnnotations(); + } + + /** + * Removes all markers, polylines, polygons, overlays, etc from the map. + */ + @UiThread + public void clear() { + annotationManager.removeAnnotations(); + } + + /** + * Return a annotation based on its id. + * + * @param id the id used to look up an annotation + * @return An annotation with a matched id, null is returned if no match was found + */ + @Nullable + public Annotation getAnnotation(long id) { + return annotationManager.getAnnotation(id); + } + + /** + * Returns a list of all the annotations on the map. + * + * @return A list of all the annotation objects. The returned object is a copy so modifying this + * list will not update the map + */ + @NonNull + public List<Annotation> getAnnotations() { + return annotationManager.getAnnotations(); + } + + /** + * Returns a list of all the markers on the map. + * + * @return A list of all the markers objects. The returned object is a copy so modifying this + * list will not update the map. + */ + @NonNull + public List<Marker> getMarkers() { + return annotationManager.getMarkers(); + } + + /** + * Returns a list of all the polygons on the map. + * + * @return A list of all the polygon objects. The returned object is a copy so modifying this + * list will not update the map. + */ + @NonNull + public List<Polygon> getPolygons() { + return annotationManager.getPolygons(); + } + + /** + * Returns a list of all the polylines on the map. + * + * @return A list of all the polylines objects. The returned object is a copy so modifying this + * list will not update the map. + */ + @NonNull + public List<Polyline> getPolylines() { + return annotationManager.getPolylines(); + } + + /** + * Sets a callback that's invoked when the user clicks on a marker. + * + * @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); + } + + /** + * <p> + * Selects a marker. The selected marker will have it's info window opened. + * Any other open info windows will be closed unless isAllowConcurrentMultipleOpenInfoWindows() + * is true. + * </p> + * Selecting an already selected marker will have no effect. + * + * @param marker The marker to select. + */ + @UiThread + public void selectMarker(@NonNull Marker marker) { + if (marker == null) { + Timber.w("marker was null, so just returning"); + return; + } + annotationManager.selectMarker(marker); + } + + /** + * Deselects any currently selected marker. All markers will have it's info window closed. + */ + @UiThread + public void deselectMarkers() { + annotationManager.deselectMarkers(); + } + + /** + * Deselects a currently selected marker. The selected marker will have it's info window closed. + * + * @param marker the marker to deselect + */ + @UiThread + public void deselectMarker(@NonNull Marker marker) { + annotationManager.deselectMarker(marker); + } + + /** + * Gets the currently selected marker. + * + * @return The currently selected marker. + */ + @UiThread + public List<Marker> getSelectedMarkers() { + return annotationManager.getSelectedMarkers(); + } + + /** + * Get the MarkerViewManager associated to the MapView. + * + * @return the associated MarkerViewManager + */ + public MarkerViewManager getMarkerViewManager() { + return annotationManager.getMarkerViewManager(); + } + + // + // InfoWindow + // + + /** + * <p> + * Sets a custom renderer for the contents of info window. + * </p> + * When set your callback is invoked when an info window is about to be shown. By returning + * a custom {@link View}, the default info window will be replaced. + * + * @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); + } + + /** + * Gets the callback to be invoked when an info window will be shown. + * + * @return The callback to be invoked when an info window will be shown. + */ + @UiThread + @Nullable + public InfoWindowAdapter getInfoWindowAdapter() { + return annotationManager.getInfoWindowManager().getInfoWindowAdapter(); + } + + /** + * Changes whether the map allows concurrent multiple infowindows to be shown. + * + * @param allow If true, map allows concurrent multiple infowindows to be shown. + */ + @UiThread + public void setAllowConcurrentMultipleOpenInfoWindows(boolean allow) { + annotationManager.getInfoWindowManager().setAllowConcurrentMultipleOpenInfoWindows(allow); + } + + /** + * Returns whether the map allows concurrent multiple infowindows to be shown. + * + * @return If true, map allows concurrent multiple infowindows to be shown. + */ + @UiThread + public boolean isAllowConcurrentMultipleOpenInfoWindows() { + return annotationManager.getInfoWindowManager().isAllowConcurrentMultipleOpenInfoWindows(); + } + + // Internal API + List<InfoWindow> getInfoWindows() { + return annotationManager.getInfoWindowManager().getInfoWindows(); + } + + AnnotationManager getAnnotationManager() { + return annotationManager; + } + + Transform getTransform() { + return transform; + } + + + // + // Padding + // + + /** + * <p> + * Sets the distance from the edges of the map view’s frame to the edges of the map + * view’s logical viewport. + * </p> + * <p> + * When the value of this property is equal to {0,0,0,0}, viewport + * properties such as `centerCoordinate` assume a viewport that matches the map + * view’s frame. Otherwise, those properties are inset, excluding part of the + * frame from the viewport. For instance, if the only the top edge is inset, the + * map center is effectively shifted downward. + * </p> + * + * @param left The left margin in pixels. + * @param top The top margin in pixels. + * @param right The right margin in pixels. + * @param bottom The bottom margin in pixels. + */ + public void setPadding(int left, int top, int right, int bottom) { + projection.setContentPadding(new int[] {left, top, right, bottom}, myLocationViewSettings.getPadding()); + uiSettings.invalidate(); + } + + /** + * Returns the current configured content padding on map view. + * + * @return An array with length 4 in the LTRB order. + */ + public int[] getPadding() { + return projection.getContentPadding(); + } + + // + // Map events + // + + /** + * Sets a callback that's invoked on every change in camera position. + * + * @param listener The callback that's invoked on every camera change position. + * To unset the callback, use null. + */ + @UiThread + public void setOnCameraChangeListener(@Nullable OnCameraChangeListener listener) { + transform.setOnCameraChangeListener(listener); + } + + /** + * Sets a callback that's invoked on every frame rendered to the map view. + * + * @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; + } + + // used by MapView + OnFpsChangedListener getOnFpsChangedListener() { + return onFpsChangedListener; + } + + /** + * Sets a callback that's invoked when the map is scrolled. + * + * @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); + } + + /** + * Sets a callback that's invoked when the map is flinged. + * + * @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); + } + + /** + * Sets a callback that's invoked when the user clicks on the map view. + * + * @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); + } + + /** + * Sets a callback that's invoked when the user long clicks on the map view. + * + * @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); + } + + /** + * Sets a callback that's invoked when the user clicks on an info window. + * + * @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) { + getAnnotationManager().getInfoWindowManager().setOnInfoWindowClickListener(listener); + } + + /** + * Return the InfoWindow click listener + * + * @return Current active InfoWindow Click Listener + */ + @UiThread + public OnInfoWindowClickListener getOnInfoWindowClickListener() { + return getAnnotationManager().getInfoWindowManager().getOnInfoWindowClickListener(); + } + + /** + * Sets a callback that's invoked when a marker's info window is long pressed. + * + * @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) { + getAnnotationManager().getInfoWindowManager().setOnInfoWindowLongClickListener(listener); + } + + /** + * Return the InfoWindow long click listener + * + * @return Current active InfoWindow long Click Listener + */ + public OnInfoWindowLongClickListener getOnInfoWindowLongClickListener() { + return getAnnotationManager().getInfoWindowManager().getOnInfoWindowLongClickListener(); + } + + public void setOnInfoWindowCloseListener(@Nullable OnInfoWindowCloseListener listener) { + getAnnotationManager().getInfoWindowManager().setOnInfoWindowCloseListener(listener); + } + + /** + * Return the InfoWindow close listener + * + * @return Current active InfoWindow Close Listener + */ + @UiThread + public OnInfoWindowCloseListener getOnInfoWindowCloseListener() { + return getAnnotationManager().getInfoWindowManager().getOnInfoWindowCloseListener(); + } + + // + // User location + // + + /** + * Returns the status of the my-location layer. + * + * @return True if the my-location layer is enabled, false otherwise. + */ + @UiThread + public boolean isMyLocationEnabled() { + return trackingSettings.isMyLocationEnabled(); + } + + /** + * Enables or disables the my-location layer. + * While enabled, the my-location layer continuously draws an indication of a user's current + * location and bearing. + * <p> + * In order to use the my-location layer feature you need to request permission for either + * {@code ACCESS_COARSE_LOCATION} or {@code ACCESS_FINE_LOCATION}. + * </p> + * + * @param enabled True to enable; false to disable. + */ + @UiThread + public void setMyLocationEnabled(boolean enabled) { + trackingSettings.setMyLocationEnabled(enabled); + } + + /** + * Returns the currently displayed user location, or null if there is no location data available. + * + * @return The currently displayed user location. + */ + @UiThread + @Nullable + public Location getMyLocation() { + return trackingSettings.getMyLocation(); + } + + /** + * Sets a callback that's invoked when the the My Location view + * (which signifies the user's location) changes location. + * + * @param listener The callback that's invoked when the user clicks on a marker. + * To unset the callback, use null. + */ + @UiThread + public void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener + listener) { + trackingSettings.setOnMyLocationChangeListener(listener); + } + + /** + * Sets a callback that's invoked when the location tracking mode changes. + * + * @param listener The callback that's invoked when the location tracking mode changes. + * To unset the callback, use null. + */ + @UiThread + public void setOnMyLocationTrackingModeChangeListener( + @Nullable MapboxMap.OnMyLocationTrackingModeChangeListener listener) { + trackingSettings.setOnMyLocationTrackingModeChangeListener(listener); + } + + /** + * Sets a callback that's invoked when the bearing tracking mode changes. + * + * @param listener The callback that's invoked when the bearing tracking mode changes. + * To unset the callback, use null. + */ + @UiThread + public void setOnMyBearingTrackingModeChangeListener(@Nullable OnMyBearingTrackingModeChangeListener listener) { + trackingSettings.setOnMyBearingTrackingModeChangeListener(listener); + } + + // + // Invalidate + // + + /** + * Takes a snapshot of the map. + * + * @param callback Callback method invoked when the snapshot is taken. + * @param bitmap A pre-allocated bitmap. + */ + @UiThread + public void snapshot(@NonNull SnapshotReadyCallback callback, @Nullable final Bitmap bitmap) { + // FIXME 12/02/2016 + //mapView.snapshot(callback, bitmap); + } + + /** + * Takes a snapshot of the map. + * + * @param callback Callback method invoked when the snapshot is taken. + */ + @UiThread + public void snapshot(@NonNull SnapshotReadyCallback callback) { + // FIXME 12/02/2016 + //mapView.snapshot(callback, null); + } + + /** + * Queries the map for rendered features + * + * @param coordinates the point to query + * @param layerIds optionally - only query these layers + * @return the list of feature + */ + @UiThread + @NonNull + public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates, @Nullable String... + layerIds) { + return nativeMapView.queryRenderedFeatures(coordinates, layerIds); + } + + /** + * Queries the map for rendered features + * + * @param coordinates the box to query + * @param layerIds optionally - only query these layers + * @return the list of feature + */ + @UiThread + @NonNull + public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates, @Nullable String... + layerIds) { + return nativeMapView.queryRenderedFeatures(coordinates, layerIds); + } + + // + // Interfaces + // + + /** + * Interface definition for a callback to be invoked when the map is flinged. + * + * @see MapboxMap#setOnFlingListener(OnFlingListener) + */ + public interface OnFlingListener { + /** + * Called when the map is flinged. + */ + void onFling(); + } + + /** + * Interface definition for a callback to be invoked when the map is scrolled. + * + * @see MapboxMap#setOnScrollListener(OnScrollListener) + */ + public interface OnScrollListener { + /** + * Called when the map is scrolled. + */ + void onScroll(); + } + + /** + * Interface definition for a callback to be invoked when the camera changes position. + */ + public interface OnCameraChangeListener { + /** + * Called after the camera position has changed. During an animation, + * this listener may not be notified of intermediate camera positions. + * It is always called for the final position in the animation. + * + * @param position The CameraPosition at the end of the last camera change. + */ + void onCameraChange(CameraPosition position); + } + + /** + * Interface definition for a callback to be invoked when a frame is rendered to the map view. + * + * @see MapboxMap#setOnFpsChangedListener(OnFpsChangedListener) + */ + public interface OnFpsChangedListener { + /** + * Called for every frame rendered to the map view. + * + * @param fps The average number of frames rendered over the last second. + */ + void onFpsChanged(double fps); + } + + /** + * Interface definition for a callback to be invoked when a user registers an listener that is + * related to touch and click events. + */ + interface OnRegisterTouchListener { + void onRegisterMapClickListener(OnMapClickListener listener); + + void onRegisterMapLongClickListener(OnMapLongClickListener listener); + + void onRegisterScrollListener(OnScrollListener listener); + void onRegisterFlingListener(OnFlingListener listener); + } + /** + * Interface definition for a callback to be invoked when the user clicks on the map view. + * + * @see MapboxMap#setOnMapClickListener(OnMapClickListener) + */ + public interface OnMapClickListener { /** - * <p> - * Adds a marker to this map. - * </p> - * The marker's icon is rendered on the map at the location {@code Marker.position}. - * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet. + * Called when the user clicks on the map view. * - * @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. + * @param point The projected map coordinate the user clicked on. */ - @UiThread - @NonNull - public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions, final MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener) { - return annotationManager.addMarker(markerOptions, this, onMarkerViewAddedListener); - } - - @UiThread - @NonNull - public List<MarkerView> addMarkerViews(@NonNull List<? extends - BaseMarkerViewOptions> markerViewOptions) { - return annotationManager.addMarkerViews(markerViewOptions, this); - } - - @UiThread - @NonNull - public List<MarkerView> getMarkerViewsInRect(@NonNull RectF rect) { - return annotationManager.getMarkerViewsInRect(rect); - } + void onMapClick(@NonNull LatLng point); + } + /** + * Interface definition for a callback to be invoked when the user long clicks on the map view. + * + * @see MapboxMap#setOnMapLongClickListener(OnMapLongClickListener) + */ + public interface OnMapLongClickListener { /** - * <p> - * Adds multiple markers to this map. - * </p> - * The marker's icon is rendered on the map at the location {@code Marker.position}. - * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet. + * Called when the user long clicks on the map view. * - * @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. + * @param point The projected map coordinate the user long clicked on. */ - @UiThread - @NonNull - public List<Marker> addMarkers(@NonNull List<? extends - BaseMarkerOptions> markerOptionsList) { - return annotationManager.addMarkers(markerOptionsList, this); - } + void onMapLongClick(@NonNull LatLng point); + } + /** + * Interface definition for a callback to be invoked when the user clicks on a marker. + * + * @see MapboxMap#setOnMarkerClickListener(OnMarkerClickListener) + */ + public interface OnMarkerClickListener { /** - * <p> - * Updates a marker on this map. Does nothing if the marker isn't already added. - * </p> + * Called when the user clicks on a marker. * - * @param updatedMarker An updated marker object. + * @param marker The marker the user clicked on. + * @return If true the listener has consumed the event and the info window will not be shown. */ - @UiThread - public void updateMarker(@NonNull Marker updatedMarker) { - annotationManager.updateMarker(updatedMarker, this); - } + boolean onMarkerClick(@NonNull Marker marker); + } + /** + * Interface definition for a callback to be invoked when the user clicks on an info window. + * + * @see MapboxMap#setOnInfoWindowClickListener(OnInfoWindowClickListener) + */ + public interface OnInfoWindowClickListener { /** - * Adds a polyline to this map. + * Called when the user clicks on an info window. * - * @param polylineOptions A polyline options object that defines how to render the polyline. - * @return The {@code Polyine} that was added to the map. + * @param marker The marker of the info window the user clicked on. + * @return If true the listener has consumed the event and the info window will not be closed. */ - @UiThread - @NonNull - public Polyline addPolyline(@NonNull PolylineOptions polylineOptions) { - return annotationManager.addPolyline(polylineOptions, this); - } + boolean onInfoWindowClick(@NonNull Marker marker); + } - /** - * Adds multiple polylines to this map. - * - * @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); - } + /** + * Interface definition for a callback to be invoked when the user long presses on a marker's info window. + * + * @see MapboxMap#setOnInfoWindowClickListener(OnInfoWindowClickListener) + */ + public interface OnInfoWindowLongClickListener { /** - * Update a polyline on this map. + * Called when the user makes a long-press gesture on the marker's info window. * - * @param polyline An updated polyline object. + * @param marker The marker were the info window is attached to */ - @UiThread - public void updatePolyline(Polyline polyline) { - annotationManager.updatePolyline(polyline); - } + void onInfoWindowLongClick(Marker marker); + } - /** - * Adds a polygon to this map. - * - * @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); - } + /** + * Interface definition for a callback to be invoked when a marker's info window is closed. + * + * @see MapboxMap#setOnInfoWindowCloseListener(OnInfoWindowCloseListener) + */ + public interface OnInfoWindowCloseListener { /** - * Adds multiple polygons to this map. + * Called when the marker's info window is closed. * - * @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. + * @param marker The marker of the info window that was closed. */ - @UiThread - @NonNull - public List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList) { - return annotationManager.addPolygons(polygonOptionsList, this); - } - + void onInfoWindowClose(Marker marker); + } + /** + * Interface definition for a callback to be invoked when an info window will be shown. + * + * @see MapboxMap#setInfoWindowAdapter(InfoWindowAdapter) + */ + public interface InfoWindowAdapter { /** - * Update a polygon on this map. - * - * @param polygon An updated polygon object. - */ - @UiThread - public void updatePolygon(Polygon polygon) { - annotationManager.updatePolygon(polygon); - } - - /** - * <p> - * Convenience method for removing a Marker from the map. - * </p> - * Calls removeAnnotation() internally + * Called when an info window will be shown as a result of a marker click. * - * @param marker Marker to remove - */ - @UiThread - public void removeMarker(@NonNull Marker marker) { - annotationManager.removeAnnotation(marker); - } - - /** - * <p> - * Convenience method for removing a Polyline from the map. - * </p> - * Calls removeAnnotation() internally - * - * @param polyline Polyline to remove - */ - @UiThread - public void removePolyline(@NonNull Polyline polyline) { - annotationManager.removeAnnotation(polyline); - } - - /** - * <p> - * Convenience method for removing a Polygon from the map. - * </p> - * Calls removeAnnotation() internally - * - * @param polygon Polygon to remove - */ - @UiThread - public void removePolygon(@NonNull Polygon polygon) { - annotationManager.removeAnnotation(polygon); - } - - /** - * Removes an annotation from the map. - * - * @param annotation The annotation object to remove. - */ - @UiThread - public void removeAnnotation(@NonNull Annotation annotation) { - annotationManager.removeAnnotation(annotation); - } - - /** - * Removes an annotation from the map - * - * @param id The identifier associated to the annotation to be removed - */ - @UiThread - public void removeAnnotation(long id) { - annotationManager.removeAnnotation(id); - } - - /** - * Removes multiple annotations from the map. - * - * @param annotationList A list of annotation objects to remove. - */ - @UiThread - public void removeAnnotations(@NonNull List<? extends Annotation> annotationList) { - annotationManager.removeAnnotations(annotationList); - } - - /** - * Removes all annotations from the map. - */ - @UiThread - public void removeAnnotations() { - annotationManager.removeAnnotations(); - } - - /** - * Removes all markers, polylines, polygons, overlays, etc from the map. - */ - @UiThread - public void clear() { - annotationManager.removeAnnotations(); - } - - /** - * Return a annotation based on its id. - * - * @param id the id used to look up an annotation - * @return An annotation with a matched id, null is returned if no match was found + * @param marker The marker the user clicked on. + * @return View to be shown as a info window. If null is returned the default + * info window will be shown. */ @Nullable - public Annotation getAnnotation(long id) { - return annotationManager.getAnnotation(id); - } - - /** - * Returns a list of all the annotations on the map. - * - * @return A list of all the annotation objects. The returned object is a copy so modifying this - * list will not update the map - */ - @NonNull - public List<Annotation> getAnnotations() { - return annotationManager.getAnnotations(); - } - - /** - * Returns a list of all the markers on the map. - * - * @return A list of all the markers objects. The returned object is a copy so modifying this - * list will not update the map. - */ - @NonNull - public List<Marker> getMarkers() { - return annotationManager.getMarkers(); - } - - /** - * Returns a list of all the polygons on the map. - * - * @return A list of all the polygon objects. The returned object is a copy so modifying this - * list will not update the map. - */ - @NonNull - public List<Polygon> getPolygons() { - return annotationManager.getPolygons(); - } - - /** - * Returns a list of all the polylines on the map. - * - * @return A list of all the polylines objects. The returned object is a copy so modifying this - * list will not update the map. - */ - @NonNull - public List<Polyline> getPolylines() { - return annotationManager.getPolylines(); - } - - /** - * Sets a callback that's invoked when the user clicks on a marker. - * - * @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); - } - - /** - * <p> - * Selects a marker. The selected marker will have it's info window opened. - * Any other open info windows will be closed unless isAllowConcurrentMultipleOpenInfoWindows() - * is true. - * </p> - * Selecting an already selected marker will have no effect. - * - * @param marker The marker to select. - */ - @UiThread - public void selectMarker(@NonNull Marker marker) { - if (marker == null) { - Timber.w("marker was null, so just returning"); - return; - } - annotationManager.selectMarker(marker); - } - - /** - * Deselects any currently selected marker. All markers will have it's info window closed. - */ - @UiThread - public void deselectMarkers() { - annotationManager.deselectMarkers(); - } + View getInfoWindow(@NonNull Marker marker); + } - /** - * Deselects a currently selected marker. The selected marker will have it's info window closed. - * - * @param marker the marker to deselect - */ - @UiThread - public void deselectMarker(@NonNull Marker marker) { - annotationManager.deselectMarker(marker); - } + /** + * Interface definition for a callback to be invoked when an MarkerView will be shown. + * + * @param <U> the instance type of MarkerView + */ + public abstract static class MarkerViewAdapter<U extends MarkerView> { - /** - * Gets the currently selected marker. - * - * @return The currently selected marker. - */ - @UiThread - public List<Marker> getSelectedMarkers() { - return annotationManager.getSelectedMarkers(); - } + private Context context; + private final Class<U> persistentClass; + private final Pools.SimplePool<View> viewReusePool; /** - * Get the MarkerViewManager associated to the MapView. - * - * @return the associated MarkerViewManager - */ - public MarkerViewManager getMarkerViewManager() { - return annotationManager.getMarkerViewManager(); - } - - // - // InfoWindow - // - - /** - * <p> - * Sets a custom renderer for the contents of info window. - * </p> - * When set your callback is invoked when an info window is about to be shown. By returning - * a custom {@link View}, the default info window will be replaced. + * Create an instance of MarkerViewAdapter. * - * @param infoWindowAdapter The callback to be invoked when an info window will be shown. - * To unset the callback, use null. + * @param context the context associated to a MapView */ - @UiThread - public void setInfoWindowAdapter(@Nullable InfoWindowAdapter infoWindowAdapter) { - annotationManager.getInfoWindowManager().setInfoWindowAdapter(infoWindowAdapter); + @SuppressWarnings("unchecked") + public MarkerViewAdapter(Context context) { + this.context = context; + persistentClass = (Class<U>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; + viewReusePool = new Pools.SimplePool<>(10000); } /** - * Gets the callback to be invoked when an info window will be shown. + * Called when an MarkerView will be added to the MapView. * - * @return The callback to be invoked when an info window will be shown. + * @param marker the model representing the MarkerView + * @param convertView the reusable view + * @param parent the parent ViewGroup of the convertview + * @return the View that is adapted to the contents of MarkerView */ - @UiThread @Nullable - public InfoWindowAdapter getInfoWindowAdapter() { - return annotationManager.getInfoWindowManager().getInfoWindowAdapter(); - } - - /** - * Changes whether the map allows concurrent multiple infowindows to be shown. - * - * @param allow If true, map allows concurrent multiple infowindows to be shown. - */ - @UiThread - public void setAllowConcurrentMultipleOpenInfoWindows(boolean allow) { - annotationManager.getInfoWindowManager().setAllowConcurrentMultipleOpenInfoWindows(allow); - } - - /** - * Returns whether the map allows concurrent multiple infowindows to be shown. - * - * @return If true, map allows concurrent multiple infowindows to be shown. - */ - @UiThread - public boolean isAllowConcurrentMultipleOpenInfoWindows() { - return annotationManager.getInfoWindowManager().isAllowConcurrentMultipleOpenInfoWindows(); - } - - // Internal API - List<InfoWindow> getInfoWindows() { - return annotationManager.getInfoWindowManager().getInfoWindows(); - } - - AnnotationManager getAnnotationManager() { - return annotationManager; - } - - Transform getTransform() { - return transform; - } - - - // - // Padding - // + public abstract View getView(@NonNull U marker, @Nullable View convertView, @NonNull ViewGroup parent); /** + * Called when an MarkerView is removed from the MapView or the View object is going to be reused. * <p> - * Sets the distance from the edges of the map view’s frame to the edges of the map - * view’s logical viewport. + * This method should be used to reset an animated view back to it's original state for view reuse. * </p> * <p> - * When the value of this property is equal to {0,0,0,0}, viewport - * properties such as `centerCoordinate` assume a viewport that matches the map - * view’s frame. Otherwise, those properties are inset, excluding part of the - * frame from the viewport. For instance, if the only the top edge is inset, the - * map center is effectively shifted downward. + * Returning true indicates you want to the view reuse to be handled automatically. + * Returning false indicates you want to perform an animation and you are required calling + * {@link #releaseView(View)} yourself. * </p> * - * @param left The left margin in pixels. - * @param top The top margin in pixels. - * @param right The right margin in pixels. - * @param bottom The bottom margin in pixels. + * @param marker the model representing the MarkerView + * @param convertView the reusable view + * @return true if you want reuse to occur automatically, false if you want to manage this yourself. */ - public void setPadding(int left, int top, int right, int bottom) { - projection.setContentPadding(new int[]{left, top, right, bottom}, myLocationViewSettings.getPadding()); - uiSettings.invalidate(); - } - - /** - * Returns the current configured content padding on map view. - * - * @return An array with length 4 in the LTRB order. - */ - public int[] getPadding() { - return projection.getContentPadding(); - } - - // - // Map events - // - - /** - * Sets a callback that's invoked on every change in camera position. - * - * @param listener The callback that's invoked on every camera change position. - * To unset the callback, use null. - */ - @UiThread - public void setOnCameraChangeListener(@Nullable OnCameraChangeListener listener) { - transform.setOnCameraChangeListener(listener); - } - - /** - * Sets a callback that's invoked on every frame rendered to the map view. - * - * @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; - } - - // used by MapView - OnFpsChangedListener getOnFpsChangedListener() { - return onFpsChangedListener; - } - - /** - * Sets a callback that's invoked when the map is scrolled. - * - * @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); - } - - /** - * Sets a callback that's invoked when the map is flinged. - * - * @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); - } - - /** - * Sets a callback that's invoked when the user clicks on the map view. - * - * @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); - } - - /** - * Sets a callback that's invoked when the user long clicks on the map view. - * - * @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); - } - - /** - * Sets a callback that's invoked when the user clicks on an info window. - * - * @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) { - getAnnotationManager().getInfoWindowManager().setOnInfoWindowClickListener(listener); - } - - /** - * Return the InfoWindow click listener - * - * @return Current active InfoWindow Click Listener - */ - @UiThread - public OnInfoWindowClickListener getOnInfoWindowClickListener() { - return getAnnotationManager().getInfoWindowManager().getOnInfoWindowClickListener(); - } - - /** - * Sets a callback that's invoked when a marker's info window is long pressed. - * - * @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) { - getAnnotationManager().getInfoWindowManager().setOnInfoWindowLongClickListener(listener); - } - - /** - * Return the InfoWindow long click listener - * - * @return Current active InfoWindow long Click Listener - */ - public OnInfoWindowLongClickListener getOnInfoWindowLongClickListener() { - return getAnnotationManager().getInfoWindowManager().getOnInfoWindowLongClickListener(); - } - - public void setOnInfoWindowCloseListener(@Nullable OnInfoWindowCloseListener listener) { - getAnnotationManager().getInfoWindowManager().setOnInfoWindowCloseListener(listener); - } - - /** - * Return the InfoWindow close listener - * - * @return Current active InfoWindow Close Listener - */ - @UiThread - public OnInfoWindowCloseListener getOnInfoWindowCloseListener() { - return getAnnotationManager().getInfoWindowManager().getOnInfoWindowCloseListener(); - } - - // - // User location - // - - /** - * Returns the status of the my-location layer. - * - * @return True if the my-location layer is enabled, false otherwise. - */ - @UiThread - public boolean isMyLocationEnabled() { - return trackingSettings.isMyLocationEnabled(); + public boolean prepareViewForReuse(@NonNull MarkerView marker, @NonNull View convertView) { + return true; } /** + * Called when a MarkerView is selected from the MapView. * <p> - * Enables or disables the my-location layer. - * While enabled, the my-location layer continuously draws an indication of a user's current - * location and bearing. + * Returning true from this method indicates you want to move the MarkerView to the selected state. + * Returning false indicates you want to animate the View first an manually select the MarkerView when appropriate. * </p> - * In order to use the my-location layer feature you need to request permission for either - * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} - * or @link android.Manifest.permission#ACCESS_FINE_LOCATION. * - * @param enabled True to enable; false to disable. + * @param marker the model representing the MarkerView + * @param convertView the reusable view + * @param reselectionFromRecycling indicates if the onSelect callback is the initial selection + * callback or that selection occurs due to recreation of selected marker + * @return true if you want to select the Marker immediately, false if you want to manage this yourself. */ - @UiThread - public void setMyLocationEnabled(boolean enabled) { - trackingSettings.setMyLocationEnabled(enabled); + public boolean onSelect(@NonNull U marker, @NonNull View convertView, boolean reselectionFromRecycling) { + return true; } /** - * Returns the currently displayed user location, or null if there is no location data available. + * Called when a MarkerView is deselected from the MapView. * - * @return The currently displayed user location. + * @param marker the model representing the MarkerView + * @param convertView the reusable view */ - @UiThread - @Nullable - public Location getMyLocation() { - return trackingSettings.getMyLocation(); + public void onDeselect(@NonNull U marker, @NonNull View convertView) { } /** - * Sets a callback that's invoked when the the My Location view - * (which signifies the user's location) changes location. + * Returns the generic type of the used MarkerView. * - * @param listener The callback that's invoked when the user clicks on a marker. - * To unset the callback, use null. + * @return the generic type */ - @UiThread - public void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener - listener) { - trackingSettings.setOnMyLocationChangeListener(listener); + public final Class<U> getMarkerClass() { + return persistentClass; } /** - * Sets a callback that's invoked when the location tracking mode changes. + * Returns the pool used to store reusable Views. * - * @param listener The callback that's invoked when the location tracking mode changes. - * To unset the callback, use null. + * @return the pool associated to this adapter */ - @UiThread - public void setOnMyLocationTrackingModeChangeListener(@Nullable MapboxMap.OnMyLocationTrackingModeChangeListener listener) { - trackingSettings.setOnMyLocationTrackingModeChangeListener(listener); + public final Pools.SimplePool<View> getViewReusePool() { + return viewReusePool; } /** - * Sets a callback that's invoked when the bearing tracking mode changes. + * Returns the context associated to the hosting MapView. * - * @param listener The callback that's invoked when the bearing tracking mode changes. - * To unset the callback, use null. + * @return the context used */ - @UiThread - public void setOnMyBearingTrackingModeChangeListener(@Nullable OnMyBearingTrackingModeChangeListener listener) { - trackingSettings.setOnMyBearingTrackingModeChangeListener(listener); + public final Context getContext() { + return context; } - // - // Invalidate - // - /** - * Takes a snapshot of the map. + * Release a View to the ViewPool. * - * @param callback Callback method invoked when the snapshot is taken. - * @param bitmap A pre-allocated bitmap. + * @param view the view to be released */ - @UiThread - public void snapshot(@NonNull SnapshotReadyCallback callback, @Nullable final Bitmap bitmap) { - // FIXME 12/02/2016 - //mapView.snapshot(callback, bitmap); + public final void releaseView(View view) { + view.setVisibility(View.GONE); + viewReusePool.release(view); } + } - /** - * Takes a snapshot of the map. - * - * @param callback Callback method invoked when the snapshot is taken. - */ - @UiThread - public void snapshot(@NonNull SnapshotReadyCallback callback) { - // FIXME 12/02/2016 - //mapView.snapshot(callback, null); - } + /** + * Interface definition for a callback to be invoked when the user clicks on a MarkerView. + */ + public interface OnMarkerViewClickListener { /** - * Queries the map for rendered features + * Called when the user clicks on a MarkerView. * - * @param coordinates the point to query - * @param layerIds optionally - only query these layers - * @return the list of feature + * @param marker the MarkerView associated to the clicked View + * @param view the clicked View + * @param adapter the adapter used to adapt the MarkerView to the View + * @return If true the listener has consumed the event and the info window will not be shown */ - @UiThread - @NonNull - public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates, @Nullable String... - layerIds) { - return nativeMapView.queryRenderedFeatures(coordinates, layerIds); - } - - /** - * Queries the map for rendered features - * - * @param coordinates the box to query - * @param layerIds optionally - only query these layers - * @return the list of feature - */ - @UiThread - @NonNull - public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates, @Nullable String... - layerIds) { - return nativeMapView.queryRenderedFeatures(coordinates, layerIds); - } - - // - // Interfaces - // - - /** - * Interface definition for a callback to be invoked when the map is flinged. - * - * @see MapboxMap#setOnFlingListener(OnFlingListener) - */ - public interface OnFlingListener { - /** - * Called when the map is flinged. - */ - void onFling(); - } - - /** - * Interface definition for a callback to be invoked when the map is scrolled. - * - * @see MapboxMap#setOnScrollListener(OnScrollListener) - */ - public interface OnScrollListener { - /** - * Called when the map is scrolled. - */ - void onScroll(); - } - - /** - * Interface definition for a callback to be invoked when the camera changes position. - */ - public interface OnCameraChangeListener { - /** - * Called after the camera position has changed. During an animation, - * this listener may not be notified of intermediate camera positions. - * It is always called for the final position in the animation. - * - * @param position The CameraPosition at the end of the last camera change. - */ - void onCameraChange(CameraPosition position); - } - - /** - * Interface definition for a callback to be invoked when a frame is rendered to the map view. - * - * @see MapboxMap#setOnFpsChangedListener(OnFpsChangedListener) - */ - public interface OnFpsChangedListener { - /** - * Called for every frame rendered to the map view. - * - * @param fps The average number of frames rendered over the last second. - */ - void onFpsChanged(double fps); - } - - /** - * Interface definition for a callback to be invoked when a user registers an listener that is - * related to touch and click events. - */ - interface OnRegisterTouchListener { - void onRegisterMapClickListener(OnMapClickListener listener); - - void onRegisterMapLongClickListener(OnMapLongClickListener listener); - - void onRegisterScrollListener(OnScrollListener listener); - - void onRegisterFlingListener(OnFlingListener listener); - } - - /** - * Interface definition for a callback to be invoked when the user clicks on the map view. - * - * @see MapboxMap#setOnMapClickListener(OnMapClickListener) - */ - public interface OnMapClickListener { - /** - * Called when the user clicks on the map view. - * - * @param point The projected map coordinate the user clicked on. - */ - void onMapClick(@NonNull LatLng point); - } + boolean onMarkerClick(@NonNull Marker marker, @NonNull View view, @NonNull MarkerViewAdapter adapter); + } + /** + * Interface definition for a callback to be invoked when the the My Location view changes location. + * + * @see MapboxMap#setOnMyLocationChangeListener(OnMyLocationChangeListener) + */ + public interface OnMyLocationChangeListener { /** - * Interface definition for a callback to be invoked when the user long clicks on the map view. + * Called when the location of the My Location view has changed + * (be it latitude/longitude, bearing or accuracy). * - * @see MapboxMap#setOnMapLongClickListener(OnMapLongClickListener) + * @param location The current location of the My Location view The type of map change event. */ - public interface OnMapLongClickListener { - /** - * Called when the user long clicks on the map view. - * - * @param point The projected map coordinate the user long clicked on. - */ - void onMapLongClick(@NonNull LatLng point); - } + void onMyLocationChange(@Nullable Location location); + } - /** - * Interface definition for a callback to be invoked when the user clicks on a marker. - * - * @see MapboxMap#setOnMarkerClickListener(OnMarkerClickListener) - */ - public interface OnMarkerClickListener { - /** - * Called when the user clicks on a marker. - * - * @param marker The marker the user clicked on. - * @return If true the listener has consumed the event and the info window will not be shown. - */ - boolean onMarkerClick(@NonNull Marker marker); - } + /** + * Interface definition for a callback to be invoked when the the My Location tracking mode changes. + * + * @see TrackingSettings#setMyLocationTrackingMode(int) + */ + public interface OnMyLocationTrackingModeChangeListener { /** - * Interface definition for a callback to be invoked when the user clicks on an info window. + * Called when the tracking mode of My Location tracking has changed * - * @see MapboxMap#setOnInfoWindowClickListener(OnInfoWindowClickListener) + * @param myLocationTrackingMode the current active location tracking mode */ - public interface OnInfoWindowClickListener { - /** - * Called when the user clicks on an info window. - * - * @param marker The marker of the info window the user clicked on. - * @return If true the listener has consumed the event and the info window will not be closed. - */ - boolean onInfoWindowClick(@NonNull Marker marker); - } + void onMyLocationTrackingModeChange(@MyLocationTracking.Mode int myLocationTrackingMode); + } - /** - * Interface definition for a callback to be invoked when the user long presses on a marker's info window. - * - * @see MapboxMap#setOnInfoWindowClickListener(OnInfoWindowClickListener) - */ - public interface OnInfoWindowLongClickListener { - - /** - * Called when the user makes a long-press gesture on the marker's info window. - * - * @param marker The marker were the info window is attached to - */ - void onInfoWindowLongClick(Marker marker); - } - - /** - * Interface definition for a callback to be invoked when a marker's info window is closed. - * - * @see MapboxMap#setOnInfoWindowCloseListener(OnInfoWindowCloseListener) - */ - public interface OnInfoWindowCloseListener { - - /** - * Called when the marker's info window is closed. - * - * @param marker The marker of the info window that was closed. - */ - void onInfoWindowClose(Marker marker); - } - - /** - * Interface definition for a callback to be invoked when an info window will be shown. - * - * @see MapboxMap#setInfoWindowAdapter(InfoWindowAdapter) - */ - public interface InfoWindowAdapter { - /** - * Called when an info window will be shown as a result of a marker click. - * - * @param marker The marker the user clicked on. - * @return View to be shown as a info window. If null is returned the default - * info window will be shown. - */ - @Nullable - View getInfoWindow(@NonNull Marker marker); - } + /** + * Interface definition for a callback to be invoked when the the My Location tracking mode changes. + * + * @see TrackingSettings#setMyLocationTrackingMode(int) + */ + public interface OnMyBearingTrackingModeChangeListener { /** - * Interface definition for a callback to be invoked when an MarkerView will be shown. + * Called when the tracking mode of My Bearing tracking has changed * - * @param <U> the instance type of MarkerView - */ - public abstract static class MarkerViewAdapter<U extends MarkerView> { - - private Context context; - private final Class<U> persistentClass; - private final Pools.SimplePool<View> viewReusePool; - - /** - * Create an instance of MarkerViewAdapter. - * - * @param context the context associated to a MapView - */ - @SuppressWarnings("unchecked") - public MarkerViewAdapter(Context context) { - this.context = context; - persistentClass = (Class<U>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; - viewReusePool = new Pools.SimplePool<>(10000); - } - - /** - * Called when an MarkerView will be added to the MapView. - * - * @param marker the model representing the MarkerView - * @param convertView the reusable view - * @param parent the parent ViewGroup of the convertview - * @return the View that is adapted to the contents of MarkerView - */ - @Nullable - public abstract View getView(@NonNull U marker, @Nullable View convertView, @NonNull ViewGroup parent); - - /** - * Called when an MarkerView is removed from the MapView or the View object is going to be reused. - * <p> - * This method should be used to reset an animated view back to it's original state for view reuse. - * </p> - * <p> - * Returning true indicates you want to the view reuse to be handled automatically. - * Returning false indicates you want to perform an animation and you are required calling {@link #releaseView(View)} yourself. - * </p> - * - * @param marker the model representing the MarkerView - * @param convertView the reusable view - * @return true if you want reuse to occur automatically, false if you want to manage this yourself. - */ - public boolean prepareViewForReuse(@NonNull MarkerView marker, @NonNull View convertView) { - return true; - } - - /** - * Called when a MarkerView is selected from the MapView. - * <p> - * Returning true from this method indicates you want to move the MarkerView to the selected state. - * Returning false indicates you want to animate the View first an manually select the MarkerView when appropriate. - * </p> - * - * @param marker the model representing the MarkerView - * @param convertView the reusable view - * @param reselectionFromRecycling indicates if the onSelect callback is the initial selection - * callback or that selection occurs due to recreation of selected marker - * @return true if you want to select the Marker immediately, false if you want to manage this yourself. - */ - public boolean onSelect(@NonNull U marker, @NonNull View convertView, boolean reselectionFromRecycling) { - return true; - } - - /** - * Called when a MarkerView is deselected from the MapView. - * - * @param marker the model representing the MarkerView - * @param convertView the reusable view - */ - public void onDeselect(@NonNull U marker, @NonNull View convertView) { - } - - /** - * Returns the generic type of the used MarkerView. - * - * @return the generic type - */ - public final Class<U> getMarkerClass() { - return persistentClass; - } - - /** - * Returns the pool used to store reusable Views. - * - * @return the pool associated to this adapter - */ - public final Pools.SimplePool<View> getViewReusePool() { - return viewReusePool; - } - - /** - * Returns the context associated to the hosting MapView. - * - * @return the context used - */ - public final Context getContext() { - return context; - } - - /** - * Release a View to the ViewPool. - * - * @param view the view to be released - */ - public final void releaseView(View view) { - view.setVisibility(View.GONE); - viewReusePool.release(view); - } - } - - /** - * Interface definition for a callback to be invoked when the user clicks on a MarkerView. + * @param myBearingTrackingMode the current active bearing tracking mode */ - public interface OnMarkerViewClickListener { - - /** - * Called when the user clicks on a MarkerView. - * - * @param marker the MarkerView associated to the clicked View - * @param view the clicked View - * @param adapter the adapter used to adapt the MarkerView to the View - * @return If true the listener has consumed the event and the info window will not be shown - */ - boolean onMarkerClick(@NonNull Marker marker, @NonNull View view, @NonNull MarkerViewAdapter adapter); - } + void onMyBearingTrackingModeChange(@MyBearingTracking.Mode int myBearingTrackingMode); + } + /** + * Interface definition for a callback to be invoked when a task is complete or cancelled. + */ + public interface CancelableCallback { /** - * Interface definition for a callback to be invoked when the the My Location view changes location. - * - * @see MapboxMap#setOnMyLocationChangeListener(OnMyLocationChangeListener) + * Invoked when a task is cancelled. */ - public interface OnMyLocationChangeListener { - /** - * Called when the location of the My Location view has changed - * (be it latitude/longitude, bearing or accuracy). - * - * @param location The current location of the My Location view The type of map change event. - */ - void onMyLocationChange(@Nullable Location location); - } + void onCancel(); /** - * Interface definition for a callback to be invoked when the the My Location tracking mode changes. - * - * @see TrackingSettings#setMyLocationTrackingMode(int) + * Invoked when a task is complete. */ - public interface OnMyLocationTrackingModeChangeListener { - - /** - * Called when the tracking mode of My Location tracking has changed - * - * @param myLocationTrackingMode the current active location tracking mode - */ - void onMyLocationTrackingModeChange(@MyLocationTracking.Mode int myLocationTrackingMode); - } + void onFinish(); + } + /** + * Interface definition for a callback to be invoked when the snapshot has been taken. + */ + public interface SnapshotReadyCallback { /** - * Interface definition for a callback to be invoked when the the My Location tracking mode changes. + * Invoked when the snapshot has been taken. * - * @see TrackingSettings#setMyLocationTrackingMode(int) + * @param snapshot the snapshot bitmap */ - public interface OnMyBearingTrackingModeChangeListener { - - /** - * Called when the tracking mode of My Bearing tracking has changed - * - * @param myBearingTrackingMode the current active bearing tracking mode - */ - void onMyBearingTrackingModeChange(@MyBearingTracking.Mode int myBearingTrackingMode); - } - - /** - * Interface definition for a callback to be invoked when a task is complete or cancelled. - */ - public interface CancelableCallback { - /** - * Invoked when a task is cancelled. - */ - void onCancel(); - - /** - * Invoked when a task is complete. - */ - void onFinish(); - } - - /** - * Interface definition for a callback to be invoked when the snapshot has been taken. - */ - public interface SnapshotReadyCallback { - /** - * Invoked when the snapshot has been taken. - * - * @param snapshot the snapshot bitmap - */ - void onSnapshotReady(Bitmap snapshot); - } + void onSnapshotReady(Bitmap snapshot); + } } 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 2b0659322f..95bd943eca 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 @@ -34,1054 +34,1172 @@ import java.util.Arrays; */ public class MapboxMapOptions implements Parcelable { - private static final float DIMENSION_SEVEN_DP = 7f; - private static final float DIMENSION_TEN_DP = 10f; - private static final float DIMENSION_SIXTEEN_DP = 16f; - private static final float DIMENSION_SEVENTY_SIX_DP = 76f; - - private CameraPosition cameraPosition; - - private boolean debugActive; - - private boolean compassEnabled = true; - private boolean fadeCompassFacingNorth = true; - private int compassGravity = Gravity.TOP | Gravity.END; - private int[] compassMargins; - - private boolean logoEnabled = true; - private int logoGravity = Gravity.BOTTOM | Gravity.START; - private int[] logoMargins; - - @ColorInt - private int attributionTintColor = -1; - private boolean attributionEnabled = true; - private int attributionGravity = Gravity.BOTTOM; - private int[] attributionMargins; - - private double minZoom = MapboxConstants.MINIMUM_ZOOM; - private double maxZoom = MapboxConstants.MAXIMUM_ZOOM; - - private boolean rotateGesturesEnabled = true; - private boolean scrollGesturesEnabled = true; - private boolean tiltGesturesEnabled = true; - private boolean zoomGesturesEnabled = true; - private boolean zoomControlsEnabled = false; - - private boolean myLocationEnabled; - private Drawable myLocationForegroundDrawable; - private Drawable myLocationForegroundBearingDrawable; - private Drawable myLocationBackgroundDrawable; - private int myLocationForegroundTintColor; - private int myLocationBackgroundTintColor; - private int[] myLocationBackgroundPadding; - private int myLocationAccuracyTintColor; - private int myLocationAccuracyAlpha; - - private String apiBaseUrl; - - @Deprecated - private boolean textureMode; - - private String style; - @Deprecated - private String accessToken; - - /** - * Creates a new MapboxMapOptions object. - */ - public MapboxMapOptions() { - } - - private MapboxMapOptions(Parcel in) { - cameraPosition = in.readParcelable(CameraPosition.class.getClassLoader()); - debugActive = in.readByte() != 0; - - compassEnabled = in.readByte() != 0; - compassGravity = in.readInt(); - compassMargins = in.createIntArray(); - fadeCompassFacingNorth = in.readByte() != 0; - - logoEnabled = in.readByte() != 0; - logoGravity = in.readInt(); - logoMargins = in.createIntArray(); - - attributionEnabled = in.readByte() != 0; - attributionGravity = in.readInt(); - attributionMargins = in.createIntArray(); - attributionTintColor = in.readInt(); - - minZoom = in.readDouble(); - maxZoom = in.readDouble(); - - rotateGesturesEnabled = in.readByte() != 0; - scrollGesturesEnabled = in.readByte() != 0; - tiltGesturesEnabled = in.readByte() != 0; - zoomControlsEnabled = in.readByte() != 0; - zoomGesturesEnabled = in.readByte() != 0; - - myLocationEnabled = in.readByte() != 0; - - Bitmap foregroundBitmap = in.readParcelable(getClass().getClassLoader()); - if (foregroundBitmap != null) { - myLocationForegroundDrawable = new BitmapDrawable(foregroundBitmap); - } - - Bitmap foregroundBearingBitmap = in.readParcelable(getClass().getClassLoader()); - if (foregroundBearingBitmap != null) { - myLocationForegroundBearingDrawable = new BitmapDrawable(foregroundBearingBitmap); - } - - Bitmap backgroundBitmap = in.readParcelable(getClass().getClassLoader()); - if (backgroundBitmap != null) { - myLocationBackgroundDrawable = new BitmapDrawable(backgroundBitmap); - } - - myLocationForegroundTintColor = in.readInt(); - myLocationBackgroundTintColor = in.readInt(); - myLocationBackgroundPadding = in.createIntArray(); - myLocationAccuracyAlpha = in.readInt(); - myLocationAccuracyTintColor = in.readInt(); - - style = in.readString(); - accessToken = in.readString(); - apiBaseUrl = in.readString(); - textureMode = in.readByte() != 0; - } - - public static Bitmap getBitmapFromDrawable(Drawable drawable) { - if (drawable instanceof BitmapDrawable) { - return ((BitmapDrawable) drawable).getBitmap(); - } else { - Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawable.draw(canvas); - return bitmap; - } - } - - /** - * Creates a MapboxMapsOptions from the attribute set.s - * - * @param context Context related to a map view. - * @param attrs Attributeset containing configuration - * @return the MapboxMapOptions created from attributes - */ - public static MapboxMapOptions createFromAttributes(@NonNull Context context, @Nullable AttributeSet attrs) { - MapboxMapOptions mapboxMapOptions = new MapboxMapOptions(); - float screenDensity = context.getResources().getDisplayMetrics().density; - TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.mapbox_MapView, 0, 0); - try { - mapboxMapOptions.camera(new CameraPosition.Builder(typedArray).build()); - - mapboxMapOptions.accessToken(typedArray.getString(R.styleable.mapbox_MapView_mapbox_accessToken)); - mapboxMapOptions.styleUrl(typedArray.getString(R.styleable.mapbox_MapView_mapbox_styleUrl)); - mapboxMapOptions.apiBaseUrl(typedArray.getString(R.styleable.mapbox_MapView_mapbox_apiBaseUrl)); - - mapboxMapOptions.zoomGesturesEnabled(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiZoomGestures, true)); - mapboxMapOptions.scrollGesturesEnabled(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiScrollGestures, true)); - mapboxMapOptions.rotateGesturesEnabled(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiRotateGestures, true)); - mapboxMapOptions.tiltGesturesEnabled(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiTiltGestures, true)); - mapboxMapOptions.zoomControlsEnabled(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiZoomControls, false)); - - mapboxMapOptions.maxZoomPreference(typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraZoomMax, MapboxConstants.MAXIMUM_ZOOM)); - mapboxMapOptions.minZoomPreference(typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraZoomMin, MapboxConstants.MINIMUM_ZOOM)); - - mapboxMapOptions.compassEnabled(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiCompass, true)); - mapboxMapOptions.compassGravity(typedArray.getInt(R.styleable.mapbox_MapView_mapbox_uiCompassGravity, Gravity.TOP | Gravity.END)); - mapboxMapOptions.compassMargins(new int[]{(int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiCompassMarginLeft, DIMENSION_TEN_DP * screenDensity)) - , ((int) typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiCompassMarginTop, DIMENSION_TEN_DP * screenDensity)) - , ((int) typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiCompassMarginRight, DIMENSION_TEN_DP * screenDensity)) - , ((int) typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiCompassMarginBottom, DIMENSION_TEN_DP * screenDensity))}); - mapboxMapOptions.compassFadesWhenFacingNorth(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiCompassFadeFacingNorth, true)); - - mapboxMapOptions.logoEnabled(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiLogo, true)); - mapboxMapOptions.logoGravity(typedArray.getInt(R.styleable.mapbox_MapView_mapbox_uiLogoGravity, Gravity.BOTTOM | Gravity.START)); - mapboxMapOptions.logoMargins(new int[]{(int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiLogoMarginLeft, DIMENSION_SIXTEEN_DP * screenDensity)) - , (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiLogoMarginTop, DIMENSION_SIXTEEN_DP * screenDensity)) - , (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiLogoMarginRight, DIMENSION_SIXTEEN_DP * screenDensity)) - , (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiLogoMarginBottom, DIMENSION_SIXTEEN_DP * screenDensity))}); - - mapboxMapOptions.attributionTintColor(typedArray.getColor(R.styleable.mapbox_MapView_mapbox_uiAttributionTintColor, -1)); - mapboxMapOptions.attributionEnabled(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiAttribution, true)); - mapboxMapOptions.attributionGravity(typedArray.getInt(R.styleable.mapbox_MapView_mapbox_uiAttributionGravity, Gravity.BOTTOM)); - mapboxMapOptions.attributionMargins(new int[]{(int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginLeft, DIMENSION_SEVENTY_SIX_DP) * screenDensity) - , (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginTop, DIMENSION_SEVEN_DP * screenDensity)) - , (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginRight, DIMENSION_SEVEN_DP * screenDensity)) - , (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginBottom, DIMENSION_SEVEN_DP * screenDensity))}); - - mapboxMapOptions.locationEnabled(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_myLocation, false)); - mapboxMapOptions.myLocationForegroundTintColor(typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationTintColor, ColorUtils.getPrimaryColor(context))); - mapboxMapOptions.myLocationBackgroundTintColor(typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundTintColor, Color.WHITE)); - - Drawable foregroundDrawable = typedArray.getDrawable(R.styleable.mapbox_MapView_mapbox_myLocationDrawable); - if (foregroundDrawable == null) { - foregroundDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_default); - } - - Drawable foregroundBearingDrawable = typedArray.getDrawable(R.styleable.mapbox_MapView_mapbox_myLocationBearingDrawable); - if (foregroundBearingDrawable == null) { - foregroundBearingDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_bearing); - } - - Drawable backgroundDrawable = typedArray.getDrawable(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundDrawable); - if (backgroundDrawable == null) { - backgroundDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_bg_shape); - } - - mapboxMapOptions.myLocationForegroundDrawables(foregroundDrawable, foregroundBearingDrawable); - mapboxMapOptions.myLocationBackgroundDrawable(backgroundDrawable); - mapboxMapOptions.myLocationBackgroundPadding(new int[]{(int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundMarginLeft, 0) * screenDensity) - , (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundMarginTop, 0) * screenDensity) - , (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundMarginRight, 0) * screenDensity) - , (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundMarginBottom, 0) * screenDensity)}); - mapboxMapOptions.myLocationAccuracyAlpha(typedArray.getInt(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyAlpha, 100)); - mapboxMapOptions.myLocationAccuracyTint(typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyTintColor, ColorUtils.getPrimaryColor(context))); - mapboxMapOptions.textureMode(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_renderTextureMode, false)); - } finally { - typedArray.recycle(); - } - return mapboxMapOptions; - } - - /** - * Specifies the URL used for API endpoint. - * - * @param apiBaseUrl The base of our API endpoint - * @return This - */ - public MapboxMapOptions apiBaseUrl(String apiBaseUrl) { - this.apiBaseUrl = apiBaseUrl; - return this; - } - - /** - * Specifies a the initial camera position for the map view. - * - * @param cameraPosition Inital camera position - * @return This - */ - public MapboxMapOptions camera(CameraPosition cameraPosition) { - this.cameraPosition = cameraPosition; - return this; - } - - /** - * <p> - * DEPRECATED @see MapboxAccountManager#start(String) - * </p> - * Specifies the accesstoken associated with a map view. - * - * @param accessToken Token to be used to access the service - * @return This - * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)} - */ - @Deprecated - public MapboxMapOptions accessToken(String accessToken) { - this.accessToken = accessToken; - return this; - } - - /** - * Specifies the style url associated with a map view. - * - * @param styleUrl Url to be used to load a style - * @return This - */ - public MapboxMapOptions styleUrl(String styleUrl) { - style = styleUrl; - return this; - } - - /** - * Specifies the used debug type for a map view. - * - * @param enabled True is debug is enabled - * @return This - */ - public MapboxMapOptions debugActive(boolean enabled) { - debugActive = enabled; - return this; - } - - /** - * Specifies the used minimum zoom level for a map view. - * - * @param minZoom Zoom level to be used - * @return This - */ - public MapboxMapOptions minZoomPreference(double minZoom) { - this.minZoom = minZoom; - return this; - } - - /** - * Specifies the used maximum zoom level for a map view. - * - * @param maxZoom Zoom level to be used - * @return This - */ - public MapboxMapOptions maxZoomPreference(double maxZoom) { - this.maxZoom = maxZoom; - return this; - } - - /** - * Specifies the visibility state of a mapbox_compass_icon for a map view. - * - * @param enabled True and mapbox_compass_icon is shown - * @return This - */ - public MapboxMapOptions compassEnabled(boolean enabled) { - compassEnabled = enabled; - return this; - } - - /** - * Specifies the gravity state of mapbox_compass_icon for a map view. - * - * @param gravity see {@link android.view.Gravity} - * @return This - */ - public MapboxMapOptions compassGravity(int gravity) { - compassGravity = gravity; - return this; - } - - /** - * Specifies the margin state of mapbox_compass_icon for a map view - * - * @param margins 4 long array for LTRB margins - * @return This - */ - public MapboxMapOptions compassMargins(int[] margins) { - compassMargins = margins; - return this; - } - - /** - * Specifies if the mapbox_compass_icon fades to invisible when facing north. - * <p> - * By default this value is true. - * </p> - * - * @param compassFadeWhenFacingNorth true is mapbox_compass_icon fades to invisble - * @return This - */ - public MapboxMapOptions compassFadesWhenFacingNorth(boolean compassFadeWhenFacingNorth) { - this.fadeCompassFacingNorth = compassFadeWhenFacingNorth; - return this; - } - - /** - * Specifies the visibility state of a logo for a map view. - * - * @param enabled True and logo is shown - * @return This - */ - public MapboxMapOptions logoEnabled(boolean enabled) { - logoEnabled = enabled; - return this; - } + private static final float DIMENSION_SEVEN_DP = 7f; + private static final float DIMENSION_TEN_DP = 10f; + private static final float DIMENSION_SIXTEEN_DP = 16f; + private static final float DIMENSION_SEVENTY_SIX_DP = 76f; - /** - * Specifies the gravity state of logo for a map view. - * - * @param gravity see {@link android.view.Gravity} - * @return This - */ - public MapboxMapOptions logoGravity(int gravity) { - logoGravity = gravity; - return this; - } - - /** - * Specifies the margin state of logo for a map view - * - * @param margins 4 long array for LTRB margins - * @return This - */ - public MapboxMapOptions logoMargins(int[] margins) { - logoMargins = margins; - return this; - } - - /** - * Specifies the visibility state of a attribution for a map view. - * - * @param enabled True and attribution is shown - * @return This - */ - public MapboxMapOptions attributionEnabled(boolean enabled) { - attributionEnabled = enabled; - return this; - } - - /** - * Specifies the gravity state of attribution for a map view. - * - * @param gravity see {@link android.view.Gravity} - * @return This - */ - public MapboxMapOptions attributionGravity(int gravity) { - attributionGravity = gravity; - return this; - } - - /** - * Specifies the margin state of attribution for a map view - * - * @param margins 4 long array for LTRB margins - * @return This - */ - public MapboxMapOptions attributionMargins(int[] margins) { - attributionMargins = margins; - return this; - } - - /** - * Specifies the tint color of the attribution for a map view - * - * @param color integer resembling a color - * @return This - */ - public MapboxMapOptions attributionTintColor(@ColorInt int color) { - attributionTintColor = color; - return this; - } - - /** - * Specifies if the rotate gesture is enabled for a map view. - * - * @param enabled True and gesture will be enabled - * @return This - */ - public MapboxMapOptions rotateGesturesEnabled(boolean enabled) { - rotateGesturesEnabled = enabled; - return this; - } - - /** - * Specifies if the scroll gesture is enabled for a map view. - * - * @param enabled True and gesture will be enabled - * @return This - */ - public MapboxMapOptions scrollGesturesEnabled(boolean enabled) { - scrollGesturesEnabled = enabled; - return this; - } + private CameraPosition cameraPosition; - /** - * Specifies if the tilt gesture is enabled for a map view. - * - * @param enabled True and gesture will be enabled - * @return This - */ - public MapboxMapOptions tiltGesturesEnabled(boolean enabled) { - tiltGesturesEnabled = enabled; - return this; - } + private boolean debugActive; - /** - * Specifies if the zoom controls are enabled for a map view. - * - * @param enabled True and gesture will be enabled - * @return This - */ - public MapboxMapOptions zoomControlsEnabled(boolean enabled) { - zoomControlsEnabled = enabled; - return this; - } - - /** - * Specifies if the zoom gesture is enabled for a map view. - * - * @param enabled True and gesture will be enabled - * @return This - */ - public MapboxMapOptions zoomGesturesEnabled(boolean enabled) { - zoomGesturesEnabled = enabled; - return this; - } - - /** - * Specifies if the user location view is enabled for a map view. - * - * @param locationEnabled True and gesture will be enabled - * @return This - */ - public MapboxMapOptions locationEnabled(boolean locationEnabled) { - this.myLocationEnabled = locationEnabled; - return this; - } - - /** - * Set the foreground drawables of the MyLocationView. - * - * @param myLocationForegroundDrawable the drawable to show as foreground without bearing - * @param myLocationBearingDrawable the drawable to show as foreground when bearing is disabled - * @return This - */ - public MapboxMapOptions myLocationForegroundDrawables(Drawable myLocationForegroundDrawable, Drawable myLocationBearingDrawable) { - this.myLocationForegroundDrawable = myLocationForegroundDrawable; - this.myLocationForegroundBearingDrawable = myLocationBearingDrawable; - return this; - } - - /** - * Set the foreground drawable of the MyLocationView. - * <p> - * The same drawable will be used for both bearing as non bearing modes. - * </p> - * - * @param myLocationForegroundDrawable the drawable to show as foreground - * @return This - */ - public MapboxMapOptions myLocationForegroundDrawable(Drawable myLocationForegroundDrawable) { - this.myLocationForegroundDrawable = myLocationForegroundDrawable; - return this; - } - - /** - * Set the background drawable of MyLocationView. - * <p> - * Padding can be added to provide an offset to the background. - * </p> - * - * @param myLocationBackgroundDrawable the drawable to show as background - * @return This - */ - public MapboxMapOptions myLocationBackgroundDrawable(Drawable myLocationBackgroundDrawable) { - this.myLocationBackgroundDrawable = myLocationBackgroundDrawable; - return this; - } - - /** - * Set the foreground tint color of MyLocationView. - * <p> - * The color will tint both the foreground and the bearing foreground drawable. - * </p> - * - * @param myLocationForegroundTintColor the color to tint the foreground drawable - * @return This - */ - public MapboxMapOptions myLocationForegroundTintColor(@ColorInt int myLocationForegroundTintColor) { - this.myLocationForegroundTintColor = myLocationForegroundTintColor; - return this; - } - - /** - * Set the background tint color of MyLocationView. - * - * @param myLocationBackgroundTintColor the color to tint the background - * @return This - */ - public MapboxMapOptions myLocationBackgroundTintColor(@ColorInt int myLocationBackgroundTintColor) { - this.myLocationBackgroundTintColor = myLocationBackgroundTintColor; - return this; - } + private boolean compassEnabled = true; + private boolean fadeCompassFacingNorth = true; + private int compassGravity = Gravity.TOP | Gravity.END; + private int[] compassMargins; - /** - * Set the MyLocationView padding. - * - * @param myLocationBackgroundPadding the color to tint the background - * @return This - */ - public MapboxMapOptions myLocationBackgroundPadding(int[] myLocationBackgroundPadding) { - this.myLocationBackgroundPadding = myLocationBackgroundPadding; - return this; - } - - /** - * Set the MyLocationView accuracy circle tint color. - * - * @param myLocationAccuracyTintColor the color to tint the accuracy circle - * @return This - */ - public MapboxMapOptions myLocationAccuracyTint(@ColorInt int myLocationAccuracyTintColor) { - this.myLocationAccuracyTintColor = myLocationAccuracyTintColor; - return this; - } - - /** - * Set the MyLocationView accuracy alpha value. - * - * @param alpha the alpha value - * @return This - */ - public MapboxMapOptions myLocationAccuracyAlpha(@IntRange(from = 0, to = 255) int alpha) { - this.myLocationAccuracyAlpha = alpha; - return this; - } - - /** - * 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> - * - * @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; - return this; - } - - /** - * Get the current configured API endpoint base URL. - * - * @return Base URL to be used API endpoint. - */ - public String getApiBaseUrl() { - return apiBaseUrl; - } - - /** - * Get the current configured initial camera position for a map view. - * - * @return CameraPosition to be initially used. - */ - public CameraPosition getCamera() { - return cameraPosition; - } - - /** - * Get the current configured min zoom for a map view. - * - * @return Mininum zoom level to be used. - */ - public double getMinZoomPreference() { - return minZoom; - } - - /** - * Get the current configured maximum zoom for a map view. - * - * @return Maximum zoom to be used. - */ - public double getMaxZoomPreference() { - return maxZoom; - } - - /** - * Get the current configured visibility state for mapbox_compass_icon for a map view. - * - * @return Visibility state of the mapbox_compass_icon - */ - public boolean getCompassEnabled() { - return compassEnabled; - } - - /** - * Get the current configured gravity state for mapbox_compass_icon for a map view. - * - * @return Gravity state of the mapbox_compass_icon - */ - public int getCompassGravity() { - return compassGravity; - } - - /** - * Get the current configured margins for mapbox_compass_icon for a map view. - * - * @return Margins state of the mapbox_compass_icon - */ - public int[] getCompassMargins() { - return compassMargins; - } - - /** - * Get the current configured state for fading the mapbox_compass_icon when facing north. - * - * @return True if mapbox_compass_icon fades to invisible when facing north - */ - public boolean getCompassFadeFacingNorth() { - return fadeCompassFacingNorth; - } - - /** - * Get the current configured visibility state for mapbox_compass_icon for a map view. - * - * @return Visibility state of the mapbox_compass_icon - */ - public boolean getLogoEnabled() { - return logoEnabled; - } - - /** - * Get the current configured gravity state for logo for a map view. - * - * @return Gravity state of the logo - */ - public int getLogoGravity() { - return logoGravity; - } - - /** - * Get the current configured margins for logo for a map view. - * - * @return Margins state of the logo - */ - public int[] getLogoMargins() { - return logoMargins; - } - - /** - * <p> - * DEPRECATED @see MapboxAccountManager#start(String) - * </p> - * Get the current configured access token for a map view. - * - * @return Access token to be used. - */ - @Deprecated - public String getAccessToken() { - return accessToken; - } + private boolean logoEnabled = true; + private int logoGravity = Gravity.BOTTOM | Gravity.START; + private int[] logoMargins; + + @ColorInt + private int attributionTintColor = -1; + private boolean attributionEnabled = true; + private int attributionGravity = Gravity.BOTTOM; + private int[] attributionMargins; + + private double minZoom = MapboxConstants.MINIMUM_ZOOM; + private double maxZoom = MapboxConstants.MAXIMUM_ZOOM; + + private boolean rotateGesturesEnabled = true; + private boolean scrollGesturesEnabled = true; + private boolean tiltGesturesEnabled = true; + private boolean zoomGesturesEnabled = true; + private boolean zoomControlsEnabled = false; + + private boolean myLocationEnabled; + private Drawable myLocationForegroundDrawable; + private Drawable myLocationForegroundBearingDrawable; + private Drawable myLocationBackgroundDrawable; + private int myLocationForegroundTintColor; + private int myLocationBackgroundTintColor; + private int[] myLocationBackgroundPadding; + private int myLocationAccuracyTintColor; + private int myLocationAccuracyAlpha; + + private String apiBaseUrl; + + @Deprecated + private boolean textureMode; + + private String style; + @Deprecated + private String accessToken; + + /** + * Creates a new MapboxMapOptions object. + */ + public MapboxMapOptions() { + } + + private MapboxMapOptions(Parcel in) { + cameraPosition = in.readParcelable(CameraPosition.class.getClassLoader()); + debugActive = in.readByte() != 0; + + compassEnabled = in.readByte() != 0; + compassGravity = in.readInt(); + compassMargins = in.createIntArray(); + fadeCompassFacingNorth = in.readByte() != 0; + + logoEnabled = in.readByte() != 0; + logoGravity = in.readInt(); + logoMargins = in.createIntArray(); + + attributionEnabled = in.readByte() != 0; + attributionGravity = in.readInt(); + attributionMargins = in.createIntArray(); + attributionTintColor = in.readInt(); + + minZoom = in.readDouble(); + maxZoom = in.readDouble(); + + rotateGesturesEnabled = in.readByte() != 0; + scrollGesturesEnabled = in.readByte() != 0; + tiltGesturesEnabled = in.readByte() != 0; + zoomControlsEnabled = in.readByte() != 0; + zoomGesturesEnabled = in.readByte() != 0; + + myLocationEnabled = in.readByte() != 0; + + Bitmap foregroundBitmap = in.readParcelable(getClass().getClassLoader()); + if (foregroundBitmap != null) { + myLocationForegroundDrawable = new BitmapDrawable(foregroundBitmap); + } + + Bitmap foregroundBearingBitmap = in.readParcelable(getClass().getClassLoader()); + if (foregroundBearingBitmap != null) { + myLocationForegroundBearingDrawable = new BitmapDrawable(foregroundBearingBitmap); + } + + Bitmap backgroundBitmap = in.readParcelable(getClass().getClassLoader()); + if (backgroundBitmap != null) { + myLocationBackgroundDrawable = new BitmapDrawable(backgroundBitmap); + } + + myLocationForegroundTintColor = in.readInt(); + myLocationBackgroundTintColor = in.readInt(); + myLocationBackgroundPadding = in.createIntArray(); + myLocationAccuracyAlpha = in.readInt(); + myLocationAccuracyTintColor = in.readInt(); + + style = in.readString(); + accessToken = in.readString(); + apiBaseUrl = in.readString(); + textureMode = in.readByte() != 0; + } + + public static Bitmap getBitmapFromDrawable(Drawable drawable) { + if (drawable instanceof BitmapDrawable) { + return ((BitmapDrawable) drawable).getBitmap(); + } else { + Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), + Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + return bitmap; + } + } + + /** + * Creates a MapboxMapsOptions from the attribute set.s + * + * @param context Context related to a map view. + * @param attrs Attributeset containing configuration + * @return the MapboxMapOptions created from attributes + */ + public static MapboxMapOptions createFromAttributes(@NonNull Context context, @Nullable AttributeSet attrs) { + MapboxMapOptions mapboxMapOptions = new MapboxMapOptions(); + float screenDensity = context.getResources().getDisplayMetrics().density; + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.mapbox_MapView, 0, 0); + try { + mapboxMapOptions.camera(new CameraPosition.Builder(typedArray).build()); + + mapboxMapOptions.accessToken(typedArray.getString(R.styleable.mapbox_MapView_mapbox_accessToken)); + mapboxMapOptions.styleUrl(typedArray.getString(R.styleable.mapbox_MapView_mapbox_styleUrl)); + mapboxMapOptions.apiBaseUrl(typedArray.getString(R.styleable.mapbox_MapView_mapbox_apiBaseUrl)); + + mapboxMapOptions.zoomGesturesEnabled(typedArray.getBoolean( + R.styleable.mapbox_MapView_mapbox_uiZoomGestures, true)); + mapboxMapOptions.scrollGesturesEnabled(typedArray.getBoolean( + R.styleable.mapbox_MapView_mapbox_uiScrollGestures, true)); + mapboxMapOptions.rotateGesturesEnabled(typedArray.getBoolean( + R.styleable.mapbox_MapView_mapbox_uiRotateGestures, true)); + mapboxMapOptions.tiltGesturesEnabled(typedArray.getBoolean( + R.styleable.mapbox_MapView_mapbox_uiTiltGestures, true)); + mapboxMapOptions.zoomControlsEnabled(typedArray.getBoolean( + R.styleable.mapbox_MapView_mapbox_uiZoomControls, false)); + + mapboxMapOptions.maxZoomPreference(typedArray.getFloat( + R.styleable.mapbox_MapView_mapbox_cameraZoomMax, MapboxConstants.MAXIMUM_ZOOM)); + mapboxMapOptions.minZoomPreference(typedArray.getFloat( + R.styleable.mapbox_MapView_mapbox_cameraZoomMin, MapboxConstants.MINIMUM_ZOOM)); + + mapboxMapOptions.compassEnabled(typedArray.getBoolean( + R.styleable.mapbox_MapView_mapbox_uiCompass, true)); + mapboxMapOptions.compassGravity(typedArray.getInt( + R.styleable.mapbox_MapView_mapbox_uiCompassGravity, Gravity.TOP | Gravity.END)); + mapboxMapOptions.compassMargins( + new int[] {(int) (typedArray.getDimension( + R.styleable.mapbox_MapView_mapbox_uiCompassMarginLeft, DIMENSION_TEN_DP * screenDensity)), + ((int) typedArray.getDimension( + R.styleable.mapbox_MapView_mapbox_uiCompassMarginTop, DIMENSION_TEN_DP * screenDensity)), + ((int) typedArray.getDimension( + R.styleable.mapbox_MapView_mapbox_uiCompassMarginRight, DIMENSION_TEN_DP * screenDensity)), + ((int) typedArray.getDimension( + R.styleable.mapbox_MapView_mapbox_uiCompassMarginBottom, DIMENSION_TEN_DP * screenDensity))}); + mapboxMapOptions.compassFadesWhenFacingNorth( + typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiCompassFadeFacingNorth, true)); + + mapboxMapOptions.logoEnabled(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiLogo, true)); + mapboxMapOptions.logoGravity(typedArray.getInt( + R.styleable.mapbox_MapView_mapbox_uiLogoGravity, Gravity.BOTTOM | Gravity.START)); + mapboxMapOptions.logoMargins(new int[] {(int) ( + typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiLogoMarginLeft, DIMENSION_SIXTEEN_DP + * screenDensity)), + (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiLogoMarginTop, DIMENSION_SIXTEEN_DP + * screenDensity)), + (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiLogoMarginRight, DIMENSION_SIXTEEN_DP + * screenDensity)), + (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiLogoMarginBottom, DIMENSION_SIXTEEN_DP + * screenDensity))}); + + mapboxMapOptions.attributionTintColor( + typedArray.getColor(R.styleable.mapbox_MapView_mapbox_uiAttributionTintColor, -1)); + mapboxMapOptions.attributionEnabled( + typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiAttribution, true)); + mapboxMapOptions.attributionGravity( + typedArray.getInt(R.styleable.mapbox_MapView_mapbox_uiAttributionGravity, Gravity.BOTTOM)); + mapboxMapOptions.attributionMargins( + new int[] {(int) ( + typedArray.getDimension( + R.styleable.mapbox_MapView_mapbox_uiAttributionMarginLeft, DIMENSION_SEVENTY_SIX_DP) + * screenDensity), + (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginTop, DIMENSION_SEVEN_DP + * screenDensity)), + (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginRight, DIMENSION_SEVEN_DP + * screenDensity)), + (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginBottom, DIMENSION_SEVEN_DP + * screenDensity))}); + + mapboxMapOptions.locationEnabled( + typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_myLocation, false)); + mapboxMapOptions.myLocationForegroundTintColor( + typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationTintColor, + ColorUtils.getPrimaryColor(context))); + mapboxMapOptions.myLocationBackgroundTintColor( + typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundTintColor, Color.WHITE)); + + Drawable foregroundDrawable = typedArray.getDrawable(R.styleable.mapbox_MapView_mapbox_myLocationDrawable); + if (foregroundDrawable == null) { + foregroundDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_default); + } + + Drawable foregroundBearingDrawable = typedArray.getDrawable( + R.styleable.mapbox_MapView_mapbox_myLocationBearingDrawable); + if (foregroundBearingDrawable == null) { + foregroundBearingDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_bearing); + } + + Drawable backgroundDrawable = typedArray.getDrawable( + R.styleable.mapbox_MapView_mapbox_myLocationBackgroundDrawable); + if (backgroundDrawable == null) { + backgroundDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_bg_shape); + } + + mapboxMapOptions.myLocationForegroundDrawables(foregroundDrawable, foregroundBearingDrawable); + mapboxMapOptions.myLocationBackgroundDrawable(backgroundDrawable); + mapboxMapOptions.myLocationBackgroundPadding(new int[] { + (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundMarginLeft, 0) + * screenDensity), + (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundMarginTop, 0) + * screenDensity), + (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundMarginRight, 0) + * screenDensity), + (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundMarginBottom, 0) + * screenDensity)}); + mapboxMapOptions.myLocationAccuracyAlpha(typedArray.getInt( + R.styleable.mapbox_MapView_mapbox_myLocationAccuracyAlpha, 100)); + mapboxMapOptions.myLocationAccuracyTint(typedArray.getColor( + R.styleable.mapbox_MapView_mapbox_myLocationAccuracyTintColor, ColorUtils.getPrimaryColor(context))); + mapboxMapOptions.textureMode(typedArray.getBoolean( + R.styleable.mapbox_MapView_mapbox_renderTextureMode, false)); + } finally { + typedArray.recycle(); + } + return mapboxMapOptions; + } + + /** + * Specifies the URL used for API endpoint. + * + * @param apiBaseUrl The base of our API endpoint + * @return This + */ + public MapboxMapOptions apiBaseUrl(String apiBaseUrl) { + this.apiBaseUrl = apiBaseUrl; + return this; + } + + /** + * Specifies a the initial camera position for the map view. + * + * @param cameraPosition Inital camera position + * @return This + */ + public MapboxMapOptions camera(CameraPosition cameraPosition) { + this.cameraPosition = cameraPosition; + return this; + } + + /** + * <p> + * DEPRECATED @see MapboxAccountManager#start(String) + * </p> + * Specifies the accesstoken associated with a map view. + * + * @param accessToken Token to be used to access the service + * @return This + * @deprecated As of release 4.1.0, replaced by + * {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)} + */ + @Deprecated + public MapboxMapOptions accessToken(String accessToken) { + this.accessToken = accessToken; + return this; + } + + /** + * Specifies the style url associated with a map view. + * + * @param styleUrl Url to be used to load a style + * @return This + */ + public MapboxMapOptions styleUrl(String styleUrl) { + style = styleUrl; + return this; + } + + /** + * Specifies the used debug type for a map view. + * + * @param enabled True is debug is enabled + * @return This + */ + public MapboxMapOptions debugActive(boolean enabled) { + debugActive = enabled; + return this; + } + + /** + * Specifies the used minimum zoom level for a map view. + * + * @param minZoom Zoom level to be used + * @return This + */ + public MapboxMapOptions minZoomPreference(double minZoom) { + this.minZoom = minZoom; + return this; + } + + /** + * Specifies the used maximum zoom level for a map view. + * + * @param maxZoom Zoom level to be used + * @return This + */ + public MapboxMapOptions maxZoomPreference(double maxZoom) { + this.maxZoom = maxZoom; + return this; + } + + /** + * Specifies the visibility state of a mapbox_compass_icon for a map view. + * + * @param enabled True and mapbox_compass_icon is shown + * @return This + */ + public MapboxMapOptions compassEnabled(boolean enabled) { + compassEnabled = enabled; + return this; + } + + /** + * Specifies the gravity state of mapbox_compass_icon for a map view. + * + * @param gravity see {@link android.view.Gravity} + * @return This + */ + public MapboxMapOptions compassGravity(int gravity) { + compassGravity = gravity; + return this; + } + + /** + * Specifies the margin state of mapbox_compass_icon for a map view + * + * @param margins 4 long array for LTRB margins + * @return This + */ + public MapboxMapOptions compassMargins(int[] margins) { + compassMargins = margins; + return this; + } + + /** + * Specifies if the mapbox_compass_icon fades to invisible when facing north. + * <p> + * By default this value is true. + * </p> + * + * @param compassFadeWhenFacingNorth true is mapbox_compass_icon fades to invisble + * @return This + */ + public MapboxMapOptions compassFadesWhenFacingNorth(boolean compassFadeWhenFacingNorth) { + this.fadeCompassFacingNorth = compassFadeWhenFacingNorth; + return this; + } + + /** + * Specifies the visibility state of a logo for a map view. + * + * @param enabled True and logo is shown + * @return This + */ + public MapboxMapOptions logoEnabled(boolean enabled) { + logoEnabled = enabled; + return this; + } + + /** + * Specifies the gravity state of logo for a map view. + * + * @param gravity see {@link android.view.Gravity} + * @return This + */ + public MapboxMapOptions logoGravity(int gravity) { + logoGravity = gravity; + return this; + } + + /** + * Specifies the margin state of logo for a map view + * + * @param margins 4 long array for LTRB margins + * @return This + */ + public MapboxMapOptions logoMargins(int[] margins) { + logoMargins = margins; + return this; + } + + /** + * Specifies the visibility state of a attribution for a map view. + * + * @param enabled True and attribution is shown + * @return This + */ + public MapboxMapOptions attributionEnabled(boolean enabled) { + attributionEnabled = enabled; + return this; + } + + /** + * Specifies the gravity state of attribution for a map view. + * + * @param gravity see {@link android.view.Gravity} + * @return This + */ + public MapboxMapOptions attributionGravity(int gravity) { + attributionGravity = gravity; + return this; + } + + /** + * Specifies the margin state of attribution for a map view + * + * @param margins 4 long array for LTRB margins + * @return This + */ + public MapboxMapOptions attributionMargins(int[] margins) { + attributionMargins = margins; + return this; + } + + /** + * Specifies the tint color of the attribution for a map view + * + * @param color integer resembling a color + * @return This + */ + public MapboxMapOptions attributionTintColor(@ColorInt int color) { + attributionTintColor = color; + return this; + } + + /** + * Specifies if the rotate gesture is enabled for a map view. + * + * @param enabled True and gesture will be enabled + * @return This + */ + public MapboxMapOptions rotateGesturesEnabled(boolean enabled) { + rotateGesturesEnabled = enabled; + return this; + } + + /** + * Specifies if the scroll gesture is enabled for a map view. + * + * @param enabled True and gesture will be enabled + * @return This + */ + public MapboxMapOptions scrollGesturesEnabled(boolean enabled) { + scrollGesturesEnabled = enabled; + return this; + } + + /** + * Specifies if the tilt gesture is enabled for a map view. + * + * @param enabled True and gesture will be enabled + * @return This + */ + public MapboxMapOptions tiltGesturesEnabled(boolean enabled) { + tiltGesturesEnabled = enabled; + return this; + } + + /** + * Specifies if the zoom controls are enabled for a map view. + * + * @param enabled True and gesture will be enabled + * @return This + */ + public MapboxMapOptions zoomControlsEnabled(boolean enabled) { + zoomControlsEnabled = enabled; + return this; + } + + /** + * Specifies if the zoom gesture is enabled for a map view. + * + * @param enabled True and gesture will be enabled + * @return This + */ + public MapboxMapOptions zoomGesturesEnabled(boolean enabled) { + zoomGesturesEnabled = enabled; + return this; + } + + /** + * Specifies if the user location view is enabled for a map view. + * + * @param locationEnabled True and gesture will be enabled + * @return This + */ + public MapboxMapOptions locationEnabled(boolean locationEnabled) { + this.myLocationEnabled = locationEnabled; + return this; + } + + /** + * Set the foreground drawables of the MyLocationView. + * + * @param myLocationForegroundDrawable the drawable to show as foreground without bearing + * @param myLocationBearingDrawable the drawable to show as foreground when bearing is disabled + * @return This + */ + public MapboxMapOptions myLocationForegroundDrawables(Drawable myLocationForegroundDrawable, + Drawable myLocationBearingDrawable) { + this.myLocationForegroundDrawable = myLocationForegroundDrawable; + this.myLocationForegroundBearingDrawable = myLocationBearingDrawable; + return this; + } + + /** + * Set the foreground drawable of the MyLocationView. + * <p> + * The same drawable will be used for both bearing as non bearing modes. + * </p> + * + * @param myLocationForegroundDrawable the drawable to show as foreground + * @return This + */ + public MapboxMapOptions myLocationForegroundDrawable(Drawable myLocationForegroundDrawable) { + this.myLocationForegroundDrawable = myLocationForegroundDrawable; + return this; + } + + /** + * Set the background drawable of MyLocationView. + * <p> + * Padding can be added to provide an offset to the background. + * </p> + * + * @param myLocationBackgroundDrawable the drawable to show as background + * @return This + */ + public MapboxMapOptions myLocationBackgroundDrawable(Drawable myLocationBackgroundDrawable) { + this.myLocationBackgroundDrawable = myLocationBackgroundDrawable; + return this; + } + + /** + * Set the foreground tint color of MyLocationView. + * <p> + * The color will tint both the foreground and the bearing foreground drawable. + * </p> + * + * @param myLocationForegroundTintColor the color to tint the foreground drawable + * @return This + */ + public MapboxMapOptions myLocationForegroundTintColor(@ColorInt int myLocationForegroundTintColor) { + this.myLocationForegroundTintColor = myLocationForegroundTintColor; + return this; + } + + /** + * Set the background tint color of MyLocationView. + * + * @param myLocationBackgroundTintColor the color to tint the background + * @return This + */ + public MapboxMapOptions myLocationBackgroundTintColor(@ColorInt int myLocationBackgroundTintColor) { + this.myLocationBackgroundTintColor = myLocationBackgroundTintColor; + return this; + } + + /** + * Set the MyLocationView padding. + * + * @param myLocationBackgroundPadding the color to tint the background + * @return This + */ + public MapboxMapOptions myLocationBackgroundPadding(int[] myLocationBackgroundPadding) { + this.myLocationBackgroundPadding = myLocationBackgroundPadding; + return this; + } + + /** + * Set the MyLocationView accuracy circle tint color. + * + * @param myLocationAccuracyTintColor the color to tint the accuracy circle + * @return This + */ + public MapboxMapOptions myLocationAccuracyTint(@ColorInt int myLocationAccuracyTintColor) { + this.myLocationAccuracyTintColor = myLocationAccuracyTintColor; + return this; + } + + /** + * Set the MyLocationView accuracy alpha value. + * + * @param alpha the alpha value + * @return This + */ + public MapboxMapOptions myLocationAccuracyAlpha(@IntRange(from = 0, to = 255) int alpha) { + this.myLocationAccuracyAlpha = alpha; + return this; + } + + /** + * 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> + * + * @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; + return this; + } + + /** + * Get the current configured API endpoint base URL. + * + * @return Base URL to be used API endpoint. + */ + public String getApiBaseUrl() { + return apiBaseUrl; + } + + /** + * Get the current configured initial camera position for a map view. + * + * @return CameraPosition to be initially used. + */ + public CameraPosition getCamera() { + return cameraPosition; + } + + /** + * Get the current configured min zoom for a map view. + * + * @return Mininum zoom level to be used. + */ + public double getMinZoomPreference() { + return minZoom; + } + + /** + * Get the current configured maximum zoom for a map view. + * + * @return Maximum zoom to be used. + */ + public double getMaxZoomPreference() { + return maxZoom; + } + + /** + * Get the current configured visibility state for mapbox_compass_icon for a map view. + * + * @return Visibility state of the mapbox_compass_icon + */ + public boolean getCompassEnabled() { + return compassEnabled; + } + + /** + * Get the current configured gravity state for mapbox_compass_icon for a map view. + * + * @return Gravity state of the mapbox_compass_icon + */ + public int getCompassGravity() { + return compassGravity; + } + + /** + * Get the current configured margins for mapbox_compass_icon for a map view. + * + * @return Margins state of the mapbox_compass_icon + */ + public int[] getCompassMargins() { + return compassMargins; + } + + /** + * Get the current configured state for fading the mapbox_compass_icon when facing north. + * + * @return True if mapbox_compass_icon fades to invisible when facing north + */ + public boolean getCompassFadeFacingNorth() { + return fadeCompassFacingNorth; + } + + /** + * Get the current configured visibility state for mapbox_compass_icon for a map view. + * + * @return Visibility state of the mapbox_compass_icon + */ + public boolean getLogoEnabled() { + return logoEnabled; + } + + /** + * Get the current configured gravity state for logo for a map view. + * + * @return Gravity state of the logo + */ + public int getLogoGravity() { + return logoGravity; + } + + /** + * Get the current configured margins for logo for a map view. + * + * @return Margins state of the logo + */ + public int[] getLogoMargins() { + return logoMargins; + } + + /** + * <p> + * DEPRECATED @see MapboxAccountManager#start(String) + * </p> + * Get the current configured access token for a map view. + * + * @return Access token to be used. + */ + @Deprecated + public String getAccessToken() { + return accessToken; + } + + /** + * Get the current configured style url for a map view. + * + * @return Style url to be used. + */ + public String getStyle() { + return style; + } + + /** + * Get the current configured rotate gesture state for a map view. + * + * @return True indicates gesture is enabled + */ + public boolean getRotateGesturesEnabled() { + return rotateGesturesEnabled; + } + + /** + * Get the current configured scroll gesture state for a map view. + * + * @return True indicates gesture is enabled + */ + public boolean getScrollGesturesEnabled() { + return scrollGesturesEnabled; + } + + /** + * Get the current configured tilt gesture state for a map view. + * + * @return True indicates gesture is enabled + */ + public boolean getTiltGesturesEnabled() { + return tiltGesturesEnabled; + } + + /** + * Get the current configured zoom controls state for a map view. + * + * @return True indicates gesture is enabled + */ + public boolean getZoomControlsEnabled() { + return zoomControlsEnabled; + } + + /** + * Get the current configured zoom gesture state for a map view. + * + * @return True indicates gesture is enabled + */ + public boolean getZoomGesturesEnabled() { + return zoomGesturesEnabled; + } + + /** + * Get the current configured visibility state for attribution for a map view. + * + * @return Visibility state of the attribution + */ + public boolean getAttributionEnabled() { + return attributionEnabled; + } + + /** + * Get the current configured gravity state for attribution for a map view. + * + * @return Gravity state of the logo + */ + public int getAttributionGravity() { + return attributionGravity; + } + + /** + * Get the current configured margins for attribution for a map view. + * + * @return Margins state of the logo + */ + public int[] getAttributionMargins() { + return attributionMargins; + } + + /** + * Get the current configured tint color for attribution for a map view. + * + * @return the tint color + */ + @ColorInt + public int getAttributionTintColor() { + return attributionTintColor; + } + + /** + * Get the current configured user location view state for a map view. + * + * @return True and user location will be shown + */ + public boolean getLocationEnabled() { + return myLocationEnabled; + } + + /** + * Get the current configured MyLocationView foreground drawable. + * + * @return the drawable used as foreground + */ + public Drawable getMyLocationForegroundDrawable() { + return myLocationForegroundDrawable; + } + + /** + * Get the current configured MyLocationView foreground bearing drawable. + * + * @return the drawable used as foreground when bearing is enabled + */ + public Drawable getMyLocationForegroundBearingDrawable() { + return myLocationForegroundBearingDrawable; + } + + /** + * Get the current configured MyLocationView background drawable. + * + * @return the drawable used as background + */ + public Drawable getMyLocationBackgroundDrawable() { + return myLocationBackgroundDrawable; + } + + /** + * Get the current configured MyLocationView foreground tint color. + * + * @return the tint color + */ + public int getMyLocationForegroundTintColor() { + return myLocationForegroundTintColor; + } + + /** + * Get the current configured MyLocationView background tint color. + * + * @return the tint color + */ + public int getMyLocationBackgroundTintColor() { + return myLocationBackgroundTintColor; + } + + /** + * Get the current configured MyLocationView background padding. + * + * @return an array describing the padding in a LTRB manner + */ + public int[] getMyLocationBackgroundPadding() { + return myLocationBackgroundPadding; + } + + /** + * Get the current configured MyLocationView accuracy circle color tint value. + * + * @return the tint color + */ + public int getMyLocationAccuracyTintColor() { + return myLocationAccuracyTintColor; + } + + /** + * Get the current configured MyLocationView accuracy circle alpha value. + * + * @return the alpha value + */ + public int getMyLocationAccuracyAlpha() { + return myLocationAccuracyAlpha; + } + + /** + * Get the current configured debug state for a map view. + * + * @return True indicates debug is enabled. + */ + public boolean getDebugActive() { + return debugActive; + } + + /** + * Returns true if TextureView is being used a render view. + * + * @return True if TextureView is used. + * @deprecated As of the 4.2.0 release, using TextureView is deprecated. + */ + public boolean getTextureMode() { + return textureMode; + } + + public static final Parcelable.Creator<MapboxMapOptions> CREATOR = + new Parcelable.Creator<MapboxMapOptions>() { + public MapboxMapOptions createFromParcel(Parcel in) { + return new MapboxMapOptions(in); + } + + public MapboxMapOptions[] newArray(int size) { + return new MapboxMapOptions[size]; + } + }; - /** - * Get the current configured style url for a map view. - * - * @return Style url to be used. - */ - public String getStyle() { - return style; - } + @Override + public int describeContents() { + return 0; + } - /** - * Get the current configured rotate gesture state for a map view. - * - * @return True indicates gesture is enabled - */ - public boolean getRotateGesturesEnabled() { - return rotateGesturesEnabled; - } + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(cameraPosition, flags); + dest.writeByte((byte) (debugActive ? 1 : 0)); - /** - * Get the current configured scroll gesture state for a map view. - * - * @return True indicates gesture is enabled - */ - public boolean getScrollGesturesEnabled() { - return scrollGesturesEnabled; - } + dest.writeByte((byte) (compassEnabled ? 1 : 0)); + dest.writeInt(compassGravity); + dest.writeIntArray(compassMargins); + dest.writeByte((byte) (fadeCompassFacingNorth ? 1 : 0)); - /** - * Get the current configured tilt gesture state for a map view. - * - * @return True indicates gesture is enabled - */ - public boolean getTiltGesturesEnabled() { - return tiltGesturesEnabled; - } + dest.writeByte((byte) (logoEnabled ? 1 : 0)); + dest.writeInt(logoGravity); + dest.writeIntArray(logoMargins); - /** - * Get the current configured zoom controls state for a map view. - * - * @return True indicates gesture is enabled - */ - public boolean getZoomControlsEnabled() { - return zoomControlsEnabled; - } + dest.writeByte((byte) (attributionEnabled ? 1 : 0)); + dest.writeInt(attributionGravity); + dest.writeIntArray(attributionMargins); + dest.writeInt(attributionTintColor); - /** - * Get the current configured zoom gesture state for a map view. - * - * @return True indicates gesture is enabled - */ - public boolean getZoomGesturesEnabled() { - return zoomGesturesEnabled; - } + dest.writeDouble(minZoom); + dest.writeDouble(maxZoom); - /** - * Get the current configured visibility state for attribution for a map view. - * - * @return Visibility state of the attribution - */ - public boolean getAttributionEnabled() { - return attributionEnabled; - } + dest.writeByte((byte) (rotateGesturesEnabled ? 1 : 0)); + dest.writeByte((byte) (scrollGesturesEnabled ? 1 : 0)); + dest.writeByte((byte) (tiltGesturesEnabled ? 1 : 0)); + dest.writeByte((byte) (zoomControlsEnabled ? 1 : 0)); + dest.writeByte((byte) (zoomGesturesEnabled ? 1 : 0)); - /** - * Get the current configured gravity state for attribution for a map view. - * - * @return Gravity state of the logo - */ - public int getAttributionGravity() { - return attributionGravity; - } - - /** - * Get the current configured margins for attribution for a map view. - * - * @return Margins state of the logo - */ - public int[] getAttributionMargins() { - return attributionMargins; - } + dest.writeByte((byte) (myLocationEnabled ? 1 : 0)); - /** - * Get the current configured tint color for attribution for a map view. - * - * @return the tint color - */ - @ColorInt - public int getAttributionTintColor() { - return attributionTintColor; - } + dest.writeParcelable(myLocationForegroundDrawable != null + ? getBitmapFromDrawable(myLocationForegroundDrawable) : null, flags); + dest.writeParcelable(myLocationForegroundBearingDrawable != null + ? getBitmapFromDrawable(myLocationForegroundBearingDrawable) : null, flags); + dest.writeParcelable(myLocationBackgroundDrawable != null + ? getBitmapFromDrawable(myLocationBackgroundDrawable) : null, flags); + dest.writeInt(myLocationForegroundTintColor); + dest.writeInt(myLocationBackgroundTintColor); + dest.writeIntArray(myLocationBackgroundPadding); + dest.writeInt(myLocationAccuracyAlpha); + dest.writeInt(myLocationAccuracyTintColor); - /** - * Get the current configured user location view state for a map view. - * - * @return True and user location will be shown - */ - public boolean getLocationEnabled() { - return myLocationEnabled; - } - - /** - * Get the current configured MyLocationView foreground drawable. - * - * @return the drawable used as foreground - */ - public Drawable getMyLocationForegroundDrawable() { - return myLocationForegroundDrawable; - } + dest.writeString(style); + dest.writeString(accessToken); + dest.writeString(apiBaseUrl); + dest.writeByte((byte) (textureMode ? 1 : 0)); + } - /** - * Get the current configured MyLocationView foreground bearing drawable. - * - * @return the drawable used as foreground when bearing is enabled - */ - public Drawable getMyLocationForegroundBearingDrawable() { - return myLocationForegroundBearingDrawable; + @Override + public boolean equals(Object object) { + if (this == object) { + return true; } - - /** - * Get the current configured MyLocationView background drawable. - * - * @return the drawable used as background - */ - public Drawable getMyLocationBackgroundDrawable() { - return myLocationBackgroundDrawable; + if (object == null || getClass() != object.getClass()) { + return false; } - /** - * Get the current configured MyLocationView foreground tint color. - * - * @return the tint color - */ - public int getMyLocationForegroundTintColor() { - return myLocationForegroundTintColor; - } + MapboxMapOptions options = (MapboxMapOptions) object; - /** - * Get the current configured MyLocationView background tint color. - * - * @return the tint color - */ - public int getMyLocationBackgroundTintColor() { - return myLocationBackgroundTintColor; + if (debugActive != options.debugActive) { + return false; } - - /** - * Get the current configured MyLocationView background padding. - * - * @return an array describing the padding in a LTRB manner - */ - public int[] getMyLocationBackgroundPadding() { - return myLocationBackgroundPadding; + if (compassEnabled != options.compassEnabled) { + return false; } - - /** - * Get the current configured MyLocationView accuracy circle color tint value. - * - * @return the tint color - */ - public int getMyLocationAccuracyTintColor() { - return myLocationAccuracyTintColor; + if (fadeCompassFacingNorth != options.fadeCompassFacingNorth) { + return false; } - - /** - * Get the current configured MyLocationView accuracy circle alpha value. - * - * @return the alpha value - */ - public int getMyLocationAccuracyAlpha() { - return myLocationAccuracyAlpha; + if (compassGravity != options.compassGravity) { + return false; } - - /** - * Get the current configured debug state for a map view. - * - * @return True indicates debug is enabled. - */ - public boolean getDebugActive() { - return debugActive; + if (logoEnabled != options.logoEnabled) { + return false; } - - /** - * Returns true if TextureView is being used a render view. - * - * @return True if TextureView is used. - * @deprecated As of the 4.2.0 release, using TextureView is deprecated. - */ - public boolean getTextureMode() { - return textureMode; + if (logoGravity != options.logoGravity) { + return false; } - - public static final Parcelable.Creator<MapboxMapOptions> CREATOR - = new Parcelable.Creator<MapboxMapOptions>() { - public MapboxMapOptions createFromParcel(Parcel in) { - return new MapboxMapOptions(in); - } - - public MapboxMapOptions[] newArray(int size) { - return new MapboxMapOptions[size]; - } - }; - - @Override - public int describeContents() { - return 0; + if (attributionTintColor != options.attributionTintColor) { + return false; } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(cameraPosition, flags); - dest.writeByte((byte) (debugActive ? 1 : 0)); - - dest.writeByte((byte) (compassEnabled ? 1 : 0)); - dest.writeInt(compassGravity); - dest.writeIntArray(compassMargins); - dest.writeByte((byte) (fadeCompassFacingNorth ? 1 : 0)); - - dest.writeByte((byte) (logoEnabled ? 1 : 0)); - dest.writeInt(logoGravity); - dest.writeIntArray(logoMargins); - - dest.writeByte((byte) (attributionEnabled ? 1 : 0)); - dest.writeInt(attributionGravity); - dest.writeIntArray(attributionMargins); - dest.writeInt(attributionTintColor); - - dest.writeDouble(minZoom); - dest.writeDouble(maxZoom); - - dest.writeByte((byte) (rotateGesturesEnabled ? 1 : 0)); - dest.writeByte((byte) (scrollGesturesEnabled ? 1 : 0)); - dest.writeByte((byte) (tiltGesturesEnabled ? 1 : 0)); - dest.writeByte((byte) (zoomControlsEnabled ? 1 : 0)); - dest.writeByte((byte) (zoomGesturesEnabled ? 1 : 0)); - - dest.writeByte((byte) (myLocationEnabled ? 1 : 0)); - - dest.writeParcelable(myLocationForegroundDrawable != null ? getBitmapFromDrawable(myLocationForegroundDrawable) : null, flags); - dest.writeParcelable(myLocationForegroundBearingDrawable != null ? getBitmapFromDrawable(myLocationForegroundBearingDrawable) : null, flags); - dest.writeParcelable(myLocationBackgroundDrawable != null ? getBitmapFromDrawable(myLocationBackgroundDrawable) : null, flags); - dest.writeInt(myLocationForegroundTintColor); - dest.writeInt(myLocationBackgroundTintColor); - dest.writeIntArray(myLocationBackgroundPadding); - dest.writeInt(myLocationAccuracyAlpha); - dest.writeInt(myLocationAccuracyTintColor); - - dest.writeString(style); - dest.writeString(accessToken); - dest.writeString(apiBaseUrl); - dest.writeByte((byte) (textureMode ? 1 : 0)); + if (attributionEnabled != options.attributionEnabled) { + return false; } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - MapboxMapOptions options = (MapboxMapOptions) o; - - if (debugActive != options.debugActive) return false; - if (compassEnabled != options.compassEnabled) return false; - if (fadeCompassFacingNorth != options.fadeCompassFacingNorth) return false; - if (compassGravity != options.compassGravity) return false; - if (logoEnabled != options.logoEnabled) return false; - if (logoGravity != options.logoGravity) return false; - if (attributionTintColor != options.attributionTintColor) return false; - if (attributionEnabled != options.attributionEnabled) return false; - if (attributionGravity != options.attributionGravity) return false; - if (Double.compare(options.minZoom, minZoom) != 0) return false; - if (Double.compare(options.maxZoom, maxZoom) != 0) return false; - if (rotateGesturesEnabled != options.rotateGesturesEnabled) return false; - if (scrollGesturesEnabled != options.scrollGesturesEnabled) return false; - if (tiltGesturesEnabled != options.tiltGesturesEnabled) return false; - if (zoomGesturesEnabled != options.zoomGesturesEnabled) return false; - if (zoomControlsEnabled != options.zoomControlsEnabled) return false; - if (myLocationEnabled != options.myLocationEnabled) return false; - if (myLocationForegroundTintColor != options.myLocationForegroundTintColor) return false; - if (myLocationBackgroundTintColor != options.myLocationBackgroundTintColor) return false; - if (myLocationAccuracyTintColor != options.myLocationAccuracyTintColor) return false; - if (myLocationAccuracyAlpha != options.myLocationAccuracyAlpha) return false; - if (cameraPosition != null ? !cameraPosition.equals(options.cameraPosition) : options.cameraPosition != null) - return false; - if (!Arrays.equals(compassMargins, options.compassMargins)) return false; - if (!Arrays.equals(logoMargins, options.logoMargins)) return false; - if (!Arrays.equals(attributionMargins, options.attributionMargins)) return false; - if (myLocationForegroundDrawable != null ? !myLocationForegroundDrawable.equals(options.myLocationForegroundDrawable) : options.myLocationForegroundDrawable != null) - return false; - if (myLocationForegroundBearingDrawable != null ? !myLocationForegroundBearingDrawable.equals(options.myLocationForegroundBearingDrawable) : options.myLocationForegroundBearingDrawable != null) - return false; - if (myLocationBackgroundDrawable != null ? !myLocationBackgroundDrawable.equals(options.myLocationBackgroundDrawable) : options.myLocationBackgroundDrawable != null) - return false; - if (!Arrays.equals(myLocationBackgroundPadding, options.myLocationBackgroundPadding)) - return false; - if (style != null ? !style.equals(options.style) : options.style != null) return false; - if (apiBaseUrl != null ? !apiBaseUrl.equals(options.apiBaseUrl) : options.apiBaseUrl != null) - return false; - return accessToken != null ? accessToken.equals(options.accessToken) : options.accessToken == null; - + if (attributionGravity != options.attributionGravity) { + return false; } - - @Override - public int hashCode() { - int result; - long temp; - result = cameraPosition != null ? cameraPosition.hashCode() : 0; - result = 31 * result + (debugActive ? 1 : 0); - result = 31 * result + (compassEnabled ? 1 : 0); - result = 31 * result + (fadeCompassFacingNorth ? 1 : 0); - result = 31 * result + compassGravity; - result = 31 * result + Arrays.hashCode(compassMargins); - result = 31 * result + (logoEnabled ? 1 : 0); - result = 31 * result + logoGravity; - result = 31 * result + Arrays.hashCode(logoMargins); - result = 31 * result + attributionTintColor; - result = 31 * result + (attributionEnabled ? 1 : 0); - result = 31 * result + attributionGravity; - result = 31 * result + Arrays.hashCode(attributionMargins); - temp = Double.doubleToLongBits(minZoom); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(maxZoom); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - result = 31 * result + (rotateGesturesEnabled ? 1 : 0); - result = 31 * result + (scrollGesturesEnabled ? 1 : 0); - result = 31 * result + (tiltGesturesEnabled ? 1 : 0); - result = 31 * result + (zoomGesturesEnabled ? 1 : 0); - result = 31 * result + (zoomControlsEnabled ? 1 : 0); - result = 31 * result + (myLocationEnabled ? 1 : 0); - result = 31 * result + (myLocationForegroundDrawable != null ? myLocationForegroundDrawable.hashCode() : 0); - result = 31 * result + (myLocationForegroundBearingDrawable != null ? myLocationForegroundBearingDrawable.hashCode() : 0); - result = 31 * result + (myLocationBackgroundDrawable != null ? myLocationBackgroundDrawable.hashCode() : 0); - result = 31 * result + myLocationForegroundTintColor; - result = 31 * result + myLocationBackgroundTintColor; - result = 31 * result + Arrays.hashCode(myLocationBackgroundPadding); - result = 31 * result + myLocationAccuracyTintColor; - result = 31 * result + myLocationAccuracyAlpha; - 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 + (accessToken != null ? accessToken.hashCode() : 0); - return result; + if (Double.compare(options.minZoom, minZoom) != 0) { + return false; } + if (Double.compare(options.maxZoom, maxZoom) != 0) { + return false; + } + if (rotateGesturesEnabled != options.rotateGesturesEnabled) { + return false; + } + if (scrollGesturesEnabled != options.scrollGesturesEnabled) { + return false; + } + if (tiltGesturesEnabled != options.tiltGesturesEnabled) { + return false; + } + if (zoomGesturesEnabled != options.zoomGesturesEnabled) { + return false; + } + if (zoomControlsEnabled != options.zoomControlsEnabled) { + return false; + } + if (myLocationEnabled != options.myLocationEnabled) { + return false; + } + if (myLocationForegroundTintColor != options.myLocationForegroundTintColor) { + return false; + } + if (myLocationBackgroundTintColor != options.myLocationBackgroundTintColor) { + return false; + } + if (myLocationAccuracyTintColor != options.myLocationAccuracyTintColor) { + return false; + } + if (myLocationAccuracyAlpha != options.myLocationAccuracyAlpha) { + return false; + } + if (cameraPosition != null ? !cameraPosition.equals(options.cameraPosition) : options.cameraPosition != null) { + return false; + } + if (!Arrays.equals(compassMargins, options.compassMargins)) { + return false; + } + if (!Arrays.equals(logoMargins, options.logoMargins)) { + return false; + } + if (!Arrays.equals(attributionMargins, options.attributionMargins)) { + return false; + } + if (myLocationForegroundDrawable != null + ? !myLocationForegroundDrawable.equals(options.myLocationForegroundDrawable) + : options.myLocationForegroundDrawable != null) { + return false; + } + if (myLocationForegroundBearingDrawable != null + ? !myLocationForegroundBearingDrawable.equals(options.myLocationForegroundBearingDrawable) + : options.myLocationForegroundBearingDrawable != null) { + return false; + } + if (myLocationBackgroundDrawable != null + ? !myLocationBackgroundDrawable.equals(options.myLocationBackgroundDrawable) + : options.myLocationBackgroundDrawable != null) { + return false; + } + if (!Arrays.equals(myLocationBackgroundPadding, options.myLocationBackgroundPadding)) { + return false; + } + if (style != null ? !style.equals(options.style) : options.style != null) { + return false; + } + if (apiBaseUrl != null ? !apiBaseUrl.equals(options.apiBaseUrl) : options.apiBaseUrl != null) { + return false; + } + return accessToken != null ? accessToken.equals(options.accessToken) : options.accessToken == null; + + } + + @Override + public int hashCode() { + int result; + long temp; + result = cameraPosition != null ? cameraPosition.hashCode() : 0; + result = 31 * result + (debugActive ? 1 : 0); + result = 31 * result + (compassEnabled ? 1 : 0); + result = 31 * result + (fadeCompassFacingNorth ? 1 : 0); + result = 31 * result + compassGravity; + result = 31 * result + Arrays.hashCode(compassMargins); + result = 31 * result + (logoEnabled ? 1 : 0); + result = 31 * result + logoGravity; + result = 31 * result + Arrays.hashCode(logoMargins); + result = 31 * result + attributionTintColor; + result = 31 * result + (attributionEnabled ? 1 : 0); + result = 31 * result + attributionGravity; + result = 31 * result + Arrays.hashCode(attributionMargins); + temp = Double.doubleToLongBits(minZoom); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(maxZoom); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + result = 31 * result + (rotateGesturesEnabled ? 1 : 0); + result = 31 * result + (scrollGesturesEnabled ? 1 : 0); + result = 31 * result + (tiltGesturesEnabled ? 1 : 0); + result = 31 * result + (zoomGesturesEnabled ? 1 : 0); + result = 31 * result + (zoomControlsEnabled ? 1 : 0); + result = 31 * result + (myLocationEnabled ? 1 : 0); + result = 31 * result + (myLocationForegroundDrawable != null ? myLocationForegroundDrawable.hashCode() : 0); + result = 31 * result + (myLocationForegroundBearingDrawable != null + ? myLocationForegroundBearingDrawable.hashCode() : 0); + result = 31 * result + (myLocationBackgroundDrawable != null ? myLocationBackgroundDrawable.hashCode() : 0); + result = 31 * result + myLocationForegroundTintColor; + result = 31 * result + myLocationBackgroundTintColor; + result = 31 * result + Arrays.hashCode(myLocationBackgroundPadding); + result = 31 * result + myLocationAccuracyTintColor; + result = 31 * result + myLocationAccuracyAlpha; + 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 + (accessToken != null ? accessToken.hashCode() : 0); + return result; + } } 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 6189087686..95c0ae5327 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 @@ -35,796 +35,809 @@ import timber.log.Timber; // Class that wraps the native methods for convenience final class NativeMapView { - // Flag to indicating destroy was called - private boolean destroyed = false; + // Flag to indicating destroy was called + private boolean destroyed = false; - // Holds the pointer to JNI NativeMapView - private long nativeMapViewPtr = 0; + // Holds the pointer to JNI NativeMapView + private long nativeMapViewPtr = 0; - // Used for callbacks - private MapView mapView; + // Used for callbacks + private MapView mapView; - // Device density - private final float pixelRatio; + // Device density + private final float pixelRatio; - // Listeners for Map change events - private CopyOnWriteArrayList<MapView.OnMapChangedListener> onMapChangedListeners; + // Listeners for Map change events + private CopyOnWriteArrayList<MapView.OnMapChangedListener> onMapChangedListeners; - // - // Static methods - // + // + // Static methods + // - static { - System.loadLibrary("mapbox-gl"); - } + static { + System.loadLibrary("mapbox-gl"); + } - // - // Constructors - // - - public NativeMapView(MapView mapView) { - Context context = mapView.getContext(); - String dataPath = OfflineManager.getDatabasePath(context); - - // With the availability of offline, we're unifying the ambient (cache) and the offline - // databases to be in the same folder, outside cache, to avoid automatic deletion from - // the system - String cachePath = dataPath; - - pixelRatio = context.getResources().getDisplayMetrics().density; - String apkPath = context.getPackageCodePath(); - 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; - nativeMapViewPtr = nativeCreate(cachePath, dataPath, apkPath, pixelRatio, availableProcessors, totalMemory); - } + // + // Constructors + // - // - // Methods - // + public NativeMapView(MapView mapView) { + Context context = mapView.getContext(); + String dataPath = OfflineManager.getDatabasePath(context); - public void destroy() { - nativeDestroy(nativeMapViewPtr); - nativeMapViewPtr = 0; - mapView = null; - destroyed = true; - } + // With the availability of offline, we're unifying the ambient (cache) and the offline + // databases to be in the same folder, outside cache, to avoid automatic deletion from + // the system + String cachePath = dataPath; - public boolean wasDestroyed() { - return destroyed; + pixelRatio = context.getResources().getDisplayMetrics().density; + String apkPath = context.getPackageCodePath(); + 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; } - public void initializeDisplay() { - nativeInitializeDisplay(nativeMapViewPtr); + if (availableProcessors < 0) { + throw new IllegalArgumentException("availableProcessors cannot be negative."); } - public void terminateDisplay() { - nativeTerminateDisplay(nativeMapViewPtr); + if (totalMemory < 0) { + throw new IllegalArgumentException("totalMemory cannot be negative."); } + onMapChangedListeners = new CopyOnWriteArrayList<>(); + this.mapView = mapView; + nativeMapViewPtr = nativeCreate(cachePath, dataPath, apkPath, pixelRatio, availableProcessors, totalMemory); + } - public void initializeContext() { - nativeInitializeContext(nativeMapViewPtr); - } + // + // Methods + // - public void terminateContext() { - nativeTerminateContext(nativeMapViewPtr); - } + public void destroy() { + nativeDestroy(nativeMapViewPtr); + nativeMapViewPtr = 0; + mapView = null; + destroyed = true; + } - public void createSurface(Surface surface) { - nativeCreateSurface(nativeMapViewPtr, surface); - } + public boolean wasDestroyed() { + return destroyed; + } - public void destroySurface() { - nativeDestroySurface(nativeMapViewPtr); - } + public void initializeDisplay() { + nativeInitializeDisplay(nativeMapViewPtr); + } - public void update() { - nativeUpdate(nativeMapViewPtr); - } + public void terminateDisplay() { + nativeTerminateDisplay(nativeMapViewPtr); + } - public void render() { - nativeRender(nativeMapViewPtr); - } + public void initializeContext() { + nativeInitializeContext(nativeMapViewPtr); + } - public void resizeView(int width, int height) { - width = (int) (width / pixelRatio); - height = (int) (height / pixelRatio); - - if (width < 0) { - throw new IllegalArgumentException("width cannot be negative."); - } - - if (height < 0) { - throw new IllegalArgumentException("height cannot be negative."); - } - - 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); - 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); - height = 65535; - } - nativeViewResize(nativeMapViewPtr, width, height); - } + public void terminateContext() { + nativeTerminateContext(nativeMapViewPtr); + } - public void resizeFramebuffer(int fbWidth, int fbHeight) { - 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."); - } - nativeFramebufferResize(nativeMapViewPtr, fbWidth, fbHeight); - } + public void createSurface(Surface surface) { + nativeCreateSurface(nativeMapViewPtr, surface); + } - public void addClass(String clazz) { - nativeAddClass(nativeMapViewPtr, clazz); - } + public void destroySurface() { + nativeDestroySurface(nativeMapViewPtr); + } - public void removeClass(String clazz) { - nativeRemoveClass(nativeMapViewPtr, clazz); - } + public void update() { + nativeUpdate(nativeMapViewPtr); + } - public boolean hasClass(String clazz) { - return nativeHasClass(nativeMapViewPtr, clazz); - } + public void render() { + nativeRender(nativeMapViewPtr); + } - public void setClasses(List<String> classes) { - nativeSetClasses(nativeMapViewPtr, classes); - } + public void resizeView(int width, int height) { + width = (int) (width / pixelRatio); + height = (int) (height / pixelRatio); - public List<String> getClasses() { - return nativeGetClasses(nativeMapViewPtr); + if (width < 0) { + throw new IllegalArgumentException("width cannot be negative."); } - public void setStyleUrl(String url) { - nativeSetStyleUrl(nativeMapViewPtr, url); + if (height < 0) { + throw new IllegalArgumentException("height cannot be negative."); } - public String getStyleUrl() { - return nativeGetStyleUrl(nativeMapViewPtr); + 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); + width = 65535; } - public void setStyleJson(String newStyleJson) { - nativeSetStyleJson(nativeMapViewPtr, newStyleJson); + 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); + height = 65535; } + nativeViewResize(nativeMapViewPtr, width, height); + } - public String getStyleJson() { - return nativeGetStyleJson(nativeMapViewPtr); + public void resizeFramebuffer(int fbWidth, int fbHeight) { + if (fbWidth < 0) { + throw new IllegalArgumentException("fbWidth cannot be negative."); } - public void setAccessToken(String accessToken) { - nativeSetAccessToken(nativeMapViewPtr, accessToken); + if (fbHeight < 0) { + throw new IllegalArgumentException("fbHeight cannot be negative."); } - public String getAccessToken() { - return nativeGetAccessToken(nativeMapViewPtr); + if (fbWidth > 65535) { + throw new IllegalArgumentException( + "fbWidth cannot be greater than 65535."); } - public void cancelTransitions() { - nativeCancelTransitions(nativeMapViewPtr); + if (fbHeight > 65535) { + throw new IllegalArgumentException( + "fbHeight cannot be greater than 65535."); } + nativeFramebufferResize(nativeMapViewPtr, fbWidth, fbHeight); + } - public void setGestureInProgress(boolean inProgress) { - nativeSetGestureInProgress(nativeMapViewPtr, inProgress); - } + public void addClass(String clazz) { + nativeAddClass(nativeMapViewPtr, clazz); + } - public void moveBy(double dx, double dy) { - moveBy(dx, dy, 0); - } + public void removeClass(String clazz) { + nativeRemoveClass(nativeMapViewPtr, clazz); + } - public void moveBy(double dx, double dy, long duration) { - nativeMoveBy(nativeMapViewPtr, dx / pixelRatio, dy / pixelRatio, duration); - } + public boolean hasClass(String clazz) { + return nativeHasClass(nativeMapViewPtr, clazz); + } - public void setLatLng(LatLng latLng) { - setLatLng(latLng, 0); - } + public void setClasses(List<String> classes) { + nativeSetClasses(nativeMapViewPtr, classes); + } - public void setLatLng(LatLng latLng, long duration) { - nativeSetLatLng(nativeMapViewPtr, latLng.getLatitude(), latLng.getLongitude(), duration); - } + public List<String> getClasses() { + return nativeGetClasses(nativeMapViewPtr); + } - public LatLng getLatLng() { - return nativeGetLatLng(nativeMapViewPtr); - } + public void setStyleUrl(String url) { + nativeSetStyleUrl(nativeMapViewPtr, url); + } - public void resetPosition() { - nativeResetPosition(nativeMapViewPtr); - } + public String getStyleUrl() { + return nativeGetStyleUrl(nativeMapViewPtr); + } - public double getPitch() { - return nativeGetPitch(nativeMapViewPtr); - } + public void setStyleJson(String newStyleJson) { + nativeSetStyleJson(nativeMapViewPtr, newStyleJson); + } - public void setPitch(double pitch, long duration) { - nativeSetPitch(nativeMapViewPtr, pitch, duration); - } + public String getStyleJson() { + return nativeGetStyleJson(nativeMapViewPtr); + } - public void scaleBy(double ds) { - scaleBy(ds, Double.NaN, Double.NaN); - } + public void setAccessToken(String accessToken) { + nativeSetAccessToken(nativeMapViewPtr, accessToken); + } - public void scaleBy(double ds, double cx, double cy) { - scaleBy(ds, cx, cy, 0); - } + public String getAccessToken() { + return nativeGetAccessToken(nativeMapViewPtr); + } - public void scaleBy(double ds, double cx, double cy, long duration) { - nativeScaleBy(nativeMapViewPtr, ds, cx / pixelRatio, cy / pixelRatio, duration); - } + public void cancelTransitions() { + nativeCancelTransitions(nativeMapViewPtr); + } - public void setScale(double scale) { - setScale(scale, Double.NaN, Double.NaN); - } + public void setGestureInProgress(boolean inProgress) { + nativeSetGestureInProgress(nativeMapViewPtr, inProgress); + } - public void setScale(double scale, double cx, double cy) { - setScale(scale, cx, cy, 0); - } + public void moveBy(double dx, double dy) { + moveBy(dx, dy, 0); + } - public void setScale(double scale, double cx, double cy, long duration) { - nativeSetScale(nativeMapViewPtr, scale, cx / pixelRatio, cy / pixelRatio, duration); - } + public void moveBy(double dx, double dy, long duration) { + nativeMoveBy(nativeMapViewPtr, dx / pixelRatio, dy / pixelRatio, duration); + } - public double getScale() { - return nativeGetScale(nativeMapViewPtr); - } + public void setLatLng(LatLng latLng) { + setLatLng(latLng, 0); + } - public void setZoom(double zoom) { - setZoom(zoom, 0); - } + public void setLatLng(LatLng latLng, long duration) { + nativeSetLatLng(nativeMapViewPtr, latLng.getLatitude(), latLng.getLongitude(), duration); + } - public void setZoom(double zoom, long duration) { - nativeSetZoom(nativeMapViewPtr, zoom, duration); - } + public LatLng getLatLng() { + return nativeGetLatLng(nativeMapViewPtr); + } - public double getZoom() { - return nativeGetZoom(nativeMapViewPtr); - } + public void resetPosition() { + nativeResetPosition(nativeMapViewPtr); + } - public void resetZoom() { - nativeResetZoom(nativeMapViewPtr); - } + public double getPitch() { + return nativeGetPitch(nativeMapViewPtr); + } - public void setMinZoom(double zoom) { - nativeSetMinZoom(nativeMapViewPtr, zoom); - } + public void setPitch(double pitch, long duration) { + nativeSetPitch(nativeMapViewPtr, pitch, duration); + } - public double getMinZoom() { - return nativeGetMinZoom(nativeMapViewPtr); - } + public void scaleBy(double ds) { + scaleBy(ds, Double.NaN, Double.NaN); + } - public void setMaxZoom(double zoom) { - nativeSetMaxZoom(nativeMapViewPtr, zoom); - } + public void scaleBy(double ds, double cx, double cy) { + scaleBy(ds, cx, cy, 0); + } - public double getMaxZoom() { - return nativeGetMaxZoom(nativeMapViewPtr); - } + public void scaleBy(double ds, double cx, double cy, long duration) { + nativeScaleBy(nativeMapViewPtr, ds, cx / pixelRatio, cy / pixelRatio, duration); + } + + public void setScale(double scale) { + setScale(scale, Double.NaN, Double.NaN); + } + + public void setScale(double scale, double cx, double cy) { + setScale(scale, cx, cy, 0); + } + + public void setScale(double scale, double cx, double cy, long duration) { + nativeSetScale(nativeMapViewPtr, scale, cx / pixelRatio, cy / pixelRatio, duration); + } + + public double getScale() { + return nativeGetScale(nativeMapViewPtr); + } + + public void setZoom(double zoom) { + setZoom(zoom, 0); + } + + public void setZoom(double zoom, long duration) { + nativeSetZoom(nativeMapViewPtr, zoom, duration); + } + + public double getZoom() { + return nativeGetZoom(nativeMapViewPtr); + } + + public void resetZoom() { + nativeResetZoom(nativeMapViewPtr); + } + + public void setMinZoom(double zoom) { + nativeSetMinZoom(nativeMapViewPtr, zoom); + } + + public double getMinZoom() { + return nativeGetMinZoom(nativeMapViewPtr); + } + + public void setMaxZoom(double zoom) { + nativeSetMaxZoom(nativeMapViewPtr, zoom); + } + + public double getMaxZoom() { + return nativeGetMaxZoom(nativeMapViewPtr); + } + + public void rotateBy(double sx, double sy, double ex, double ey) { + rotateBy(sx, sy, ex, ey, 0); + } + + public void rotateBy(double sx, double sy, double ex, double ey, + long duration) { + nativeRotateBy(nativeMapViewPtr, sx / pixelRatio, sy / pixelRatio, ex, ey, duration); + } + + public void setContentPadding(int[] padding) { + nativeSetContentPadding(nativeMapViewPtr, + padding[1] / pixelRatio, + padding[0] / pixelRatio, + padding[3] / pixelRatio, + padding[2] / pixelRatio); + } + + public void setBearing(double degrees) { + setBearing(degrees, 0); + } + + public void setBearing(double degrees, long duration) { + nativeSetBearing(nativeMapViewPtr, degrees, duration); + } + + public void setBearing(double degrees, double cx, double cy) { + nativeSetBearingXY(nativeMapViewPtr, degrees, cx / pixelRatio, cy / pixelRatio); + } + + public double getBearing() { + return nativeGetBearing(nativeMapViewPtr); + } + + public void resetNorth() { + nativeResetNorth(nativeMapViewPtr); + } + + public long addMarker(Marker marker) { + Marker[] markers = {marker}; + return nativeAddMarkers(nativeMapViewPtr, markers)[0]; + } + + public long[] addMarkers(List<Marker> markers) { + return nativeAddMarkers(nativeMapViewPtr, markers.toArray(new Marker[markers.size()])); + } + + public long addPolyline(Polyline polyline) { + Polyline[] polylines = {polyline}; + return nativeAddPolylines(nativeMapViewPtr, polylines)[0]; + } + + public long[] addPolylines(List<Polyline> polylines) { + return nativeAddPolylines(nativeMapViewPtr, polylines.toArray(new Polyline[polylines.size()])); + } + + public long addPolygon(Polygon polygon) { + Polygon[] polygons = {polygon}; + return nativeAddPolygons(nativeMapViewPtr, polygons)[0]; + } + + public long[] addPolygons(List<Polygon> polygons) { + return nativeAddPolygons(nativeMapViewPtr, polygons.toArray(new Polygon[polygons.size()])); + } + + public void updateMarker(Marker marker) { + LatLng position = marker.getPosition(); + Icon icon = marker.getIcon(); + nativeUpdateMarker(nativeMapViewPtr, marker.getId(), position.getLatitude(), position.getLongitude(), icon.getId()); + } + + public void updatePolygon(Polygon polygon) { + nativeUpdatePolygon(nativeMapViewPtr, polygon.getId(), polygon); + } + + public void updatePolyline(Polyline polyline) { + nativeUpdatePolyline(nativeMapViewPtr, polyline.getId(), polyline); + } + + public void removeAnnotation(long id) { + long[] ids = {id}; + removeAnnotations(ids); + } + + public void removeAnnotations(long[] ids) { + nativeRemoveAnnotations(nativeMapViewPtr, ids); + } + + public long[] queryPointAnnotations(RectF rect) { + return nativeQueryPointAnnotations(nativeMapViewPtr, rect); + } + + public void addAnnotationIcon(String symbol, int width, int height, float scale, byte[] pixels) { + nativeAddAnnotationIcon(nativeMapViewPtr, symbol, width, height, scale, pixels); + } + + public void setVisibleCoordinateBounds(LatLng[] coordinates, RectF padding, double direction, long duration) { + nativeSetVisibleCoordinateBounds(nativeMapViewPtr, coordinates, padding, direction, duration); + } + + public void onLowMemory() { + nativeOnLowMemory(nativeMapViewPtr); + } + + public void setDebug(boolean debug) { + nativeSetDebug(nativeMapViewPtr, debug); + } + + public void cycleDebugOptions() { + nativeToggleDebug(nativeMapViewPtr); + } + + public boolean getDebug() { + return nativeGetDebug(nativeMapViewPtr); + } + + public boolean isFullyLoaded() { + return nativeIsFullyLoaded(nativeMapViewPtr); + } + + public void setReachability(boolean status) { + nativeSetReachability(nativeMapViewPtr, status); + } + + public double getMetersPerPixelAtLatitude(double lat) { + return nativeGetMetersPerPixelAtLatitude(nativeMapViewPtr, lat, getZoom()); + } + + public ProjectedMeters projectedMetersForLatLng(LatLng latLng) { + return nativeProjectedMetersForLatLng(nativeMapViewPtr, latLng.getLatitude(), latLng.getLongitude()); + } + + public LatLng latLngForProjectedMeters(ProjectedMeters projectedMeters) { + return nativeLatLngForProjectedMeters(nativeMapViewPtr, projectedMeters.getNorthing(), + projectedMeters.getEasting()); + } + + public PointF pixelForLatLng(LatLng latLng) { + PointF pointF = nativePixelForLatLng(nativeMapViewPtr, latLng.getLatitude(), latLng.getLongitude()); + pointF.set(pointF.x * pixelRatio, pointF.y * pixelRatio); + return pointF; + } + + public LatLng latLngForPixel(PointF pixel) { + return nativeLatLngForPixel(nativeMapViewPtr, pixel.x / pixelRatio, pixel.y / pixelRatio); + } + + public double getTopOffsetPixelsForAnnotationSymbol(String symbolName) { + return nativeGetTopOffsetPixelsForAnnotationSymbol(nativeMapViewPtr, symbolName); + } + + public void jumpTo(double angle, LatLng center, double pitch, double zoom) { + nativeJumpTo(nativeMapViewPtr, angle, center.getLatitude(), center.getLongitude(), pitch, zoom); + } + + public void easeTo(double angle, LatLng center, long duration, double pitch, double zoom, + boolean easingInterpolator) { + nativeEaseTo(nativeMapViewPtr, angle, center.getLatitude(), center.getLongitude(), duration, pitch, zoom, + easingInterpolator); + } + + public void flyTo(double angle, LatLng center, long duration, double pitch, double zoom) { + nativeFlyTo(nativeMapViewPtr, angle, center.getLatitude(), center.getLongitude(), duration, pitch, zoom); + } + + public double[] getCameraValues() { + return nativeGetCameraValues(nativeMapViewPtr); + } + + // Runtime style Api + + public Layer getLayer(String layerId) { + return nativeGetLayer(nativeMapViewPtr, layerId); + } + + public void addLayer(@NonNull Layer layer, @Nullable String before) { + nativeAddLayer(nativeMapViewPtr, layer.getNativePtr(), before); + } + + public void removeLayer(@NonNull String layerId) throws NoSuchLayerException { + nativeRemoveLayerById(nativeMapViewPtr, layerId); + } + + public void removeLayer(@NonNull Layer layer) throws NoSuchLayerException { + nativeRemoveLayer(nativeMapViewPtr, layer.getNativePtr()); + } + + public Source getSource(@NonNull String sourceId) { + return nativeGetSource(nativeMapViewPtr, sourceId); + } + + public void addSource(@NonNull Source source) { + nativeAddSource(nativeMapViewPtr, source.getNativePtr()); + } - public void rotateBy(double sx, double sy, double ex, double ey) { - rotateBy(sx, sy, ex, ey, 0); - } + public void removeSource(@NonNull String sourceId) throws NoSuchSourceException { + nativeRemoveSourceById(nativeMapViewPtr, sourceId); + } - public void rotateBy(double sx, double sy, double ex, double ey, - long duration) { - nativeRotateBy(nativeMapViewPtr, sx/pixelRatio, sy/pixelRatio, ex, ey, duration); - } + public void removeSource(@NonNull Source source) throws NoSuchSourceException { + nativeRemoveSource(nativeMapViewPtr, source.getNativePtr()); + } - public void setContentPadding(int[] padding) { - nativeSetContentPadding(nativeMapViewPtr, - padding[1] / pixelRatio, - padding[0] / pixelRatio, - padding[3] / pixelRatio, - padding[2] / pixelRatio); + public void addImage(@NonNull String name, @NonNull Bitmap image) { + //Check/correct config + if (image.getConfig() != Bitmap.Config.ARGB_8888) { + image = image.copy(Bitmap.Config.ARGB_8888, false); } - public void setBearing(double degrees) { - setBearing(degrees, 0); - } + //Get pixels + ByteBuffer buffer = ByteBuffer.allocate(image.getByteCount()); + image.copyPixelsToBuffer(buffer); - public void setBearing(double degrees, long duration) { - nativeSetBearing(nativeMapViewPtr, degrees, duration); - } + //Determine pixel ratio + float density = image.getDensity() == Bitmap.DENSITY_NONE ? Bitmap.DENSITY_NONE : image.getDensity(); + float pixelRatio = density / DisplayMetrics.DENSITY_DEFAULT; - public void setBearing(double degrees, double cx, double cy) { - nativeSetBearingXY(nativeMapViewPtr, degrees, cx/pixelRatio, cy/pixelRatio); - } + nativeAddImage(nativeMapViewPtr, name, image.getWidth(), image.getHeight(), pixelRatio, buffer.array()); + } - public double getBearing() { - return nativeGetBearing(nativeMapViewPtr); - } + public void removeImage(String name) { + nativeRemoveImage(nativeMapViewPtr, name); + } - public void resetNorth() { - nativeResetNorth(nativeMapViewPtr); - } + // Feature querying - public long addMarker(Marker marker) { - Marker[] markers = {marker}; - return nativeAddMarkers(nativeMapViewPtr, markers)[0]; - } + @NonNull + public List<Feature> queryRenderedFeatures(PointF coordinates, String... layerIds) { + Feature[] features = nativeQueryRenderedFeaturesForPoint(nativeMapViewPtr, coordinates.x / pixelRatio, + coordinates.y / pixelRatio, layerIds); + return features != null ? Arrays.asList(features) : new ArrayList<Feature>(); + } - public long[] addMarkers(List<Marker> markers) { - return nativeAddMarkers(nativeMapViewPtr, markers.toArray(new Marker[markers.size()])); - } + @NonNull + public List<Feature> queryRenderedFeatures(RectF coordinates, String... layerIds) { + Feature[] features = nativeQueryRenderedFeaturesForBox( + nativeMapViewPtr, + coordinates.left / pixelRatio, + coordinates.top / pixelRatio, + coordinates.right / pixelRatio, + coordinates.bottom / pixelRatio, + layerIds); + return features != null ? Arrays.asList(features) : new ArrayList<Feature>(); + } - public long addPolyline(Polyline polyline) { - Polyline[] polylines = {polyline}; - return nativeAddPolylines(nativeMapViewPtr, polylines)[0]; - } + public void scheduleTakeSnapshot() { + nativeScheduleTakeSnapshot(nativeMapViewPtr); + } - public long[] addPolylines(List<Polyline> polylines) { - return nativeAddPolylines(nativeMapViewPtr, polylines.toArray(new Polyline[polylines.size()])); - } + public void setApiBaseUrl(String baseUrl) { + nativeSetAPIBaseURL(nativeMapViewPtr, baseUrl); + } - public long addPolygon(Polygon polygon) { - Polygon[] polygons = {polygon}; - return nativeAddPolygons(nativeMapViewPtr, polygons)[0]; - } + public float getPixelRatio() { + return pixelRatio; + } - public long[] addPolygons(List<Polygon> polygons) { - return nativeAddPolygons(nativeMapViewPtr, polygons.toArray(new Polygon[polygons.size()])); - } + public Context getContext() { + return mapView.getContext(); + } - public void updateMarker(Marker marker) { - LatLng position = marker.getPosition(); - Icon icon = marker.getIcon(); - nativeUpdateMarker(nativeMapViewPtr, marker.getId(), position.getLatitude(), position.getLongitude(), icon.getId()); - } + // + // Callbacks + // - public void updatePolygon(Polygon polygon) { - nativeUpdatePolygon(nativeMapViewPtr, polygon.getId(), polygon); - } + protected void onInvalidate() { + mapView.onInvalidate(); + } - public void updatePolyline(Polyline polyline) { - nativeUpdatePolyline(nativeMapViewPtr, polyline.getId(), polyline); - } + protected void onMapChanged(int rawChange) { + mapView.onMapChanged(rawChange); + } - public void removeAnnotation(long id) { - long[] ids = {id}; - removeAnnotations(ids); - } + protected void onFpsChanged(double fps) { + mapView.onFpsChanged(fps); + } - public void removeAnnotations(long[] ids) { - nativeRemoveAnnotations(nativeMapViewPtr, ids); - } + protected void onSnapshotReady(byte[] bytes) { + mapView.onSnapshotReady(bytes); + } - public long[] queryPointAnnotations(RectF rect) { - return nativeQueryPointAnnotations(nativeMapViewPtr, rect); - } + // + // JNI methods + // - public void addAnnotationIcon(String symbol, int width, int height, float scale, byte[] pixels) { - nativeAddAnnotationIcon(nativeMapViewPtr, symbol, width, height, scale, pixels); - } + private native long nativeCreate(String cachePath, String dataPath, String apkPath, float pixelRatio, + int availableProcessors, long totalMemory); - public void setVisibleCoordinateBounds(LatLng[] coordinates, RectF padding, double direction, long duration) { - nativeSetVisibleCoordinateBounds(nativeMapViewPtr, coordinates, padding, direction, duration); - } + private native void nativeDestroy(long nativeMapViewPtr); - public void onLowMemory() { - nativeOnLowMemory(nativeMapViewPtr); - } + private native void nativeInitializeDisplay(long nativeMapViewPtr); - public void setDebug(boolean debug) { - nativeSetDebug(nativeMapViewPtr, debug); - } + private native void nativeTerminateDisplay(long nativeMapViewPtr); - public void cycleDebugOptions() { - nativeToggleDebug(nativeMapViewPtr); - } + private native void nativeInitializeContext(long nativeMapViewPtr); - public boolean getDebug() { - return nativeGetDebug(nativeMapViewPtr); - } + private native void nativeTerminateContext(long nativeMapViewPtr); - public boolean isFullyLoaded() { - return nativeIsFullyLoaded(nativeMapViewPtr); - } + private native void nativeCreateSurface(long nativeMapViewPtr, + Surface surface); - public void setReachability(boolean status) { - nativeSetReachability(nativeMapViewPtr, status); - } + private native void nativeDestroySurface(long nativeMapViewPtr); - public double getMetersPerPixelAtLatitude(double lat) { - return nativeGetMetersPerPixelAtLatitude(nativeMapViewPtr, lat, getZoom()); - } + private native void nativeUpdate(long nativeMapViewPtr); - public ProjectedMeters projectedMetersForLatLng(LatLng latLng) { - return nativeProjectedMetersForLatLng(nativeMapViewPtr, latLng.getLatitude(), latLng.getLongitude()); - } + private native void nativeRender(long nativeMapViewPtr); - public LatLng latLngForProjectedMeters(ProjectedMeters projectedMeters) { - return nativeLatLngForProjectedMeters(nativeMapViewPtr, projectedMeters.getNorthing(), projectedMeters.getEasting()); - } + private native void nativeViewResize(long nativeMapViewPtr, int width, int height); - public PointF pixelForLatLng(LatLng latLng) { - PointF pointF = nativePixelForLatLng(nativeMapViewPtr, latLng.getLatitude(), latLng.getLongitude()); - pointF.set(pointF.x * pixelRatio, pointF.y * pixelRatio); - return pointF; - } + private native void nativeFramebufferResize(long nativeMapViewPtr, int fbWidth, int fbHeight); - public LatLng latLngForPixel(PointF pixel) { - return nativeLatLngForPixel(nativeMapViewPtr, pixel.x / pixelRatio, pixel.y / pixelRatio); - } + private native void nativeAddClass(long nativeMapViewPtr, String clazz); - public double getTopOffsetPixelsForAnnotationSymbol(String symbolName) { - return nativeGetTopOffsetPixelsForAnnotationSymbol(nativeMapViewPtr, symbolName); - } + private native void nativeRemoveClass(long nativeMapViewPtr, String clazz); - public void jumpTo(double angle, LatLng center, double pitch, double zoom) { - nativeJumpTo(nativeMapViewPtr, angle, center.getLatitude(), center.getLongitude(), pitch, zoom); - } + private native boolean nativeHasClass(long nativeMapViewPtr, String clazz); - public void easeTo(double angle, LatLng center, long duration, double pitch, double zoom, boolean easingInterpolator) { - nativeEaseTo(nativeMapViewPtr, angle, center.getLatitude(), center.getLongitude(), duration, pitch, zoom, easingInterpolator); - } + private native void nativeSetClasses(long nativeMapViewPtr, + List<String> classes); - public void flyTo(double angle, LatLng center, long duration, double pitch, double zoom) { - nativeFlyTo(nativeMapViewPtr, angle, center.getLatitude(), center.getLongitude(), duration, pitch, zoom); - } + private native List<String> nativeGetClasses(long nativeMapViewPtr); - public double[] getCameraValues() { - return nativeGetCameraValues(nativeMapViewPtr); - } + private native void nativeSetStyleUrl(long nativeMapViewPtr, String url); - // Runtime style Api + private native String nativeGetStyleUrl(long nativeMapViewPtr); - public Layer getLayer(String layerId) { - return nativeGetLayer(nativeMapViewPtr, layerId); - } - - public void addLayer(@NonNull Layer layer, @Nullable String before) { - nativeAddLayer(nativeMapViewPtr, layer.getNativePtr(), before); - } + private native void nativeSetStyleJson(long nativeMapViewPtr, String newStyleJson); - public void removeLayer(@NonNull String layerId) throws NoSuchLayerException { - nativeRemoveLayerById(nativeMapViewPtr, layerId); - } + private native String nativeGetStyleJson(long nativeMapViewPtr); - public void removeLayer(@NonNull Layer layer) throws NoSuchLayerException { - nativeRemoveLayer(nativeMapViewPtr, layer.getNativePtr()); - } + private native void nativeSetAccessToken(long nativeMapViewPtr, String accessToken); - public Source getSource(@NonNull String sourceId) { - return nativeGetSource(nativeMapViewPtr, sourceId); - } + private native String nativeGetAccessToken(long nativeMapViewPtr); - public void addSource(@NonNull Source source) { - nativeAddSource(nativeMapViewPtr, source.getNativePtr()); - } + private native void nativeCancelTransitions(long nativeMapViewPtr); - public void removeSource(@NonNull String sourceId) throws NoSuchSourceException { - nativeRemoveSourceById(nativeMapViewPtr, sourceId); - } - - public void removeSource(@NonNull Source source) throws NoSuchSourceException { - nativeRemoveSource(nativeMapViewPtr, source.getNativePtr()); - } - - public void addImage(@NonNull String name, @NonNull Bitmap image) { - //Check/correct config - if (image.getConfig() != Bitmap.Config.ARGB_8888) { - image = image.copy(Bitmap.Config.ARGB_8888, false); - } - - //Get pixels - ByteBuffer buffer = ByteBuffer.allocate(image.getByteCount()); - image.copyPixelsToBuffer(buffer); - - //Determine pixel ratio - float density = image.getDensity() == Bitmap.DENSITY_NONE ? Bitmap.DENSITY_NONE : image.getDensity(); - float pixelRatio = density / DisplayMetrics.DENSITY_DEFAULT; - - nativeAddImage(nativeMapViewPtr, name, image.getWidth(), image.getHeight(), pixelRatio, buffer.array()); - } - - public void removeImage(String name) { - nativeRemoveImage(nativeMapViewPtr, name); - } + private native void nativeSetGestureInProgress(long nativeMapViewPtr, boolean inProgress); - // Feature querying + private native void nativeMoveBy(long nativeMapViewPtr, double dx, + double dy, long duration); - @NonNull - public List<Feature> queryRenderedFeatures(PointF coordinates, String... layerIds) { - Feature[] features = nativeQueryRenderedFeaturesForPoint(nativeMapViewPtr, coordinates.x / pixelRatio, coordinates.y / pixelRatio, layerIds); - return features != null ? Arrays.asList(features) : new ArrayList<Feature>(); - } - - @NonNull - public List<Feature> queryRenderedFeatures(RectF coordinates, String... layerIds) { - Feature[] features = nativeQueryRenderedFeaturesForBox( - nativeMapViewPtr, - coordinates.left / pixelRatio, - coordinates.top / pixelRatio, - coordinates.right / pixelRatio, - coordinates.bottom / pixelRatio, - layerIds); - return features != null ? Arrays.asList(features) : new ArrayList<Feature>(); - } - - public void scheduleTakeSnapshot() { - nativeScheduleTakeSnapshot(nativeMapViewPtr); - } - - public void setApiBaseUrl(String baseUrl) { - nativeSetAPIBaseURL(nativeMapViewPtr, baseUrl); - } - - public float getPixelRatio() { - return pixelRatio; - } - - public Context getContext() { - return mapView.getContext(); - } - - // - // Callbacks - // - - protected void onInvalidate() { - mapView.onInvalidate(); - } - - protected void onMapChanged(int rawChange) { - mapView.onMapChanged(rawChange); - } - - protected void onFpsChanged(double fps) { - mapView.onFpsChanged(fps); - } - - protected void onSnapshotReady(byte[] bytes) { - mapView.onSnapshotReady(bytes); - } - - // - // JNI methods - // - - private native long nativeCreate(String cachePath, String dataPath, String apkPath, float pixelRatio, int availableProcessors, long totalMemory); - - private native void nativeDestroy(long nativeMapViewPtr); - - private native void nativeInitializeDisplay(long nativeMapViewPtr); - - private native void nativeTerminateDisplay(long nativeMapViewPtr); - - private native void nativeInitializeContext(long nativeMapViewPtr); - - private native void nativeTerminateContext(long nativeMapViewPtr); - - private native void nativeCreateSurface(long nativeMapViewPtr, - Surface surface); - - private native void nativeDestroySurface(long nativeMapViewPtr); - - private native void nativeUpdate(long nativeMapViewPtr); - - private native void nativeRender(long nativeMapViewPtr); - - private native void nativeViewResize(long nativeMapViewPtr, int width, int height); - - private native void nativeFramebufferResize(long nativeMapViewPtr, int fbWidth, int fbHeight); - - private native void nativeAddClass(long nativeMapViewPtr, String clazz); - - private native void nativeRemoveClass(long nativeMapViewPtr, String clazz); - - private native boolean nativeHasClass(long nativeMapViewPtr, String clazz); - - private native void nativeSetClasses(long nativeMapViewPtr, - List<String> classes); - - private native List<String> nativeGetClasses(long nativeMapViewPtr); - - private native void nativeSetStyleUrl(long nativeMapViewPtr, String url); - - private native String nativeGetStyleUrl(long nativeMapViewPtr); - - private native void nativeSetStyleJson(long nativeMapViewPtr, String newStyleJson); - - private native String nativeGetStyleJson(long nativeMapViewPtr); - - private native void nativeSetAccessToken(long nativeMapViewPtr, String accessToken); - - private native String nativeGetAccessToken(long nativeMapViewPtr); - - private native void nativeCancelTransitions(long nativeMapViewPtr); + private native void nativeSetLatLng(long nativeMapViewPtr, double latitude, double longitude, + long duration); - private native void nativeSetGestureInProgress(long nativeMapViewPtr, boolean inProgress); + private native LatLng nativeGetLatLng(long nativeMapViewPtr); - private native void nativeMoveBy(long nativeMapViewPtr, double dx, - double dy, long duration); + private native void nativeResetPosition(long nativeMapViewPtr); - private native void nativeSetLatLng(long nativeMapViewPtr, double latitude, double longitude, - long duration); + private native double nativeGetPitch(long nativeMapViewPtr); - private native LatLng nativeGetLatLng(long nativeMapViewPtr); + private native void nativeSetPitch(long nativeMapViewPtr, double pitch, long duration); - private native void nativeResetPosition(long nativeMapViewPtr); + private native void nativeScaleBy(long nativeMapViewPtr, double ds, + double cx, double cy, long duration); - private native double nativeGetPitch(long nativeMapViewPtr); + private native void nativeSetScale(long nativeMapViewPtr, double scale, + double cx, double cy, long duration); - private native void nativeSetPitch(long nativeMapViewPtr, double pitch, long duration); + private native double nativeGetScale(long nativeMapViewPtr); - private native void nativeScaleBy(long nativeMapViewPtr, double ds, - double cx, double cy, long duration); + private native void nativeSetZoom(long nativeMapViewPtr, double zoom, + long duration); - private native void nativeSetScale(long nativeMapViewPtr, double scale, - double cx, double cy, long duration); + private native double nativeGetZoom(long nativeMapViewPtr); - private native double nativeGetScale(long nativeMapViewPtr); + private native void nativeResetZoom(long nativeMapViewPtr); - private native void nativeSetZoom(long nativeMapViewPtr, double zoom, - long duration); + private native void nativeSetMinZoom(long nativeMapViewPtr, double zoom); - private native double nativeGetZoom(long nativeMapViewPtr); + private native double nativeGetMinZoom(long nativeMapViewPtr); - private native void nativeResetZoom(long nativeMapViewPtr); + private native void nativeSetMaxZoom(long nativeMapViewPtr, double zoom); - private native void nativeSetMinZoom(long nativeMapViewPtr, double zoom); + private native double nativeGetMaxZoom(long nativeMapViewPtr); - private native double nativeGetMinZoom(long nativeMapViewPtr); + private native void nativeRotateBy(long nativeMapViewPtr, double sx, + double sy, double ex, double ey, long duration); - private native void nativeSetMaxZoom(long nativeMapViewPtr, double zoom); + private native void nativeSetContentPadding(long nativeMapViewPtr, double top, double left, double bottom, + double right); - private native double nativeGetMaxZoom(long nativeMapViewPtr); + private native void nativeSetBearing(long nativeMapViewPtr, double degrees, + long duration); - private native void nativeRotateBy(long nativeMapViewPtr, double sx, - double sy, double ex, double ey, long duration); + private native void nativeSetBearingXY(long nativeMapViewPtr, double degrees, + double cx, double cy); - private native void nativeSetContentPadding(long nativeMapViewPtr, double top, double left, double bottom, double right); + private native double nativeGetBearing(long nativeMapViewPtr); - private native void nativeSetBearing(long nativeMapViewPtr, double degrees, - long duration); + private native void nativeResetNorth(long nativeMapViewPtr); - private native void nativeSetBearingXY(long nativeMapViewPtr, double degrees, - double cx, double cy); + private native void nativeUpdateMarker(long nativeMapViewPtr, long markerId, double lat, double lon, String iconId); - private native double nativeGetBearing(long nativeMapViewPtr); + private native long[] nativeAddMarkers(long nativeMapViewPtr, Marker[] markers); - private native void nativeResetNorth(long nativeMapViewPtr); + private native long[] nativeAddPolylines(long nativeMapViewPtr, Polyline[] polylines); - private native void nativeUpdateMarker(long nativeMapViewPtr, long markerId, double lat, double lon, String iconId); + private native long[] nativeAddPolygons(long nativeMapViewPtr, Polygon[] polygons); - private native long[] nativeAddMarkers(long nativeMapViewPtr, Marker[] markers); + private native void nativeRemoveAnnotations(long nativeMapViewPtr, long[] id); - private native long[] nativeAddPolylines(long nativeMapViewPtr, Polyline[] polylines); + private native long[] nativeQueryPointAnnotations(long nativeMapViewPtr, RectF rect); - private native long[] nativeAddPolygons(long nativeMapViewPtr, Polygon[] polygons); + private native void nativeAddAnnotationIcon(long nativeMapViewPtr, String symbol, + int width, int height, float scale, byte[] pixels); - private native void nativeRemoveAnnotations(long nativeMapViewPtr, long[] id); + private native void nativeSetVisibleCoordinateBounds(long nativeMapViewPtr, LatLng[] coordinates, + RectF padding, double direction, long duration); - private native long[] nativeQueryPointAnnotations(long nativeMapViewPtr, RectF rect); + private native void nativeOnLowMemory(long nativeMapViewPtr); - private native void nativeAddAnnotationIcon(long nativeMapViewPtr, String symbol, - int width, int height, float scale, byte[] pixels); + private native void nativeSetDebug(long nativeMapViewPtr, boolean debug); - private native void nativeSetVisibleCoordinateBounds(long nativeMapViewPtr, LatLng[] coordinates, - RectF padding, double direction, long duration); + private native void nativeToggleDebug(long nativeMapViewPtr); - private native void nativeOnLowMemory(long nativeMapViewPtr); + private native boolean nativeGetDebug(long nativeMapViewPtr); - private native void nativeSetDebug(long nativeMapViewPtr, boolean debug); + private native boolean nativeIsFullyLoaded(long nativeMapViewPtr); - private native void nativeToggleDebug(long nativeMapViewPtr); + private native void nativeSetReachability(long nativeMapViewPtr, boolean status); - private native boolean nativeGetDebug(long nativeMapViewPtr); + private native double nativeGetMetersPerPixelAtLatitude(long nativeMapViewPtr, double lat, double zoom); - private native boolean nativeIsFullyLoaded(long nativeMapViewPtr); + private native ProjectedMeters nativeProjectedMetersForLatLng(long nativeMapViewPtr, double latitude, + double longitude); - private native void nativeSetReachability(long nativeMapViewPtr, boolean status); + private native LatLng nativeLatLngForProjectedMeters(long nativeMapViewPtr, double northing, double easting); - private native double nativeGetMetersPerPixelAtLatitude(long nativeMapViewPtr, double lat, double zoom); + private native PointF nativePixelForLatLng(long nativeMapViewPtr, double lat, double lon); - private native ProjectedMeters nativeProjectedMetersForLatLng(long nativeMapViewPtr, double latitude, double longitude); + private native LatLng nativeLatLngForPixel(long nativeMapViewPtr, float x, float y); - private native LatLng nativeLatLngForProjectedMeters(long nativeMapViewPtr, double northing, double easting); + private native double nativeGetTopOffsetPixelsForAnnotationSymbol(long nativeMapViewPtr, String symbolName); - private native PointF nativePixelForLatLng(long nativeMapViewPtr, double lat, double lon); + private native void nativeJumpTo(long nativeMapViewPtr, double angle, double latitude, double longitude, + double pitch, double zoom); - private native LatLng nativeLatLngForPixel(long nativeMapViewPtr, float x, float y); + private native void nativeEaseTo(long nativeMapViewPtr, double angle, double latitude, double longitude, + long duration, double pitch, double zoom, boolean easingInterpolator); - private native double nativeGetTopOffsetPixelsForAnnotationSymbol(long nativeMapViewPtr, String symbolName); + private native void nativeFlyTo(long nativeMapViewPtr, double angle, double latitude, double longitude, + long duration, double pitch, double zoom); - private native void nativeJumpTo(long nativeMapViewPtr, double angle, double latitude, double longitude, double pitch, double zoom); + private native double[] nativeGetCameraValues(long nativeMapViewPtr); - private native void nativeEaseTo(long nativeMapViewPtr, double angle, double latitude, double longitude, long duration, double pitch, double zoom, boolean easingInterpolator); + private native Layer nativeGetLayer(long nativeMapViewPtr, String layerId); - private native void nativeFlyTo(long nativeMapViewPtr, double angle, double latitude, double longitude, long duration, double pitch, double zoom); + private native void nativeAddLayer(long nativeMapViewPtr, long layerPtr, String before); - private native double[] nativeGetCameraValues(long nativeMapViewPtr); + private native void nativeRemoveLayerById(long nativeMapViewPtr, String layerId) throws NoSuchLayerException; - private native Layer nativeGetLayer(long nativeMapViewPtr, String layerId); + private native void nativeRemoveLayer(long nativeMapViewPtr, long layerId) throws NoSuchLayerException; - private native void nativeAddLayer(long nativeMapViewPtr, long layerPtr, String before); + private native Source nativeGetSource(long nativeMapViewPtr, String sourceId); - private native void nativeRemoveLayerById(long nativeMapViewPtr, String layerId) throws NoSuchLayerException; + private native void nativeAddSource(long nativeMapViewPtr, long nativeSourcePtr); - private native void nativeRemoveLayer(long nativeMapViewPtr, long layerId) throws NoSuchLayerException; + private native void nativeRemoveSourceById(long nativeMapViewPtr, String sourceId) throws NoSuchSourceException; - private native Source nativeGetSource(long nativeMapViewPtr, String sourceId); + private native void nativeRemoveSource(long nativeMapViewPtr, long sourcePtr) throws NoSuchSourceException; - private native void nativeAddSource(long nativeMapViewPtr, long nativeSourcePtr); + private native void nativeAddImage(long nativeMapViewPtr, String name, int width, int height, float pixelRatio, + byte[] array); - private native void nativeRemoveSourceById(long nativeMapViewPtr, String sourceId) throws NoSuchSourceException; + private native void nativeRemoveImage(long nativeMapViewPtr, String name); - private native void nativeRemoveSource(long nativeMapViewPtr, long sourcePtr) throws NoSuchSourceException; + private native void nativeUpdatePolygon(long nativeMapViewPtr, long polygonId, Polygon polygon); - private native void nativeAddImage(long nativeMapViewPtr, String name, int width, int height, float pixelRatio, byte[] array); + private native void nativeUpdatePolyline(long nativeMapviewPtr, long polylineId, Polyline polyline); - private native void nativeRemoveImage(long nativeMapViewPtr, String name); + private native void nativeScheduleTakeSnapshot(long nativeMapViewPtr); - private native void nativeUpdatePolygon(long nativeMapViewPtr, long polygonId, Polygon polygon); + private native Feature[] nativeQueryRenderedFeaturesForPoint(long nativeMapViewPtr, float x, float y, String[] + layerIds); - private native void nativeUpdatePolyline(long nativeMapviewPtr, long polylineId, Polyline polyline); + private native Feature[] nativeQueryRenderedFeaturesForBox(long nativeMapViewPtr, float left, float top, float right, + float bottom, String[] layerIds); - private native void nativeScheduleTakeSnapshot(long nativeMapViewPtr); + private native void nativeSetAPIBaseURL(long nativeMapViewPtr, String baseUrl); - private native Feature[] nativeQueryRenderedFeaturesForPoint(long nativeMapViewPtr, float x, float y, String[] layerIds); + int getWidth() { + return mapView.getWidth(); + } - private native Feature[] nativeQueryRenderedFeaturesForBox(long nativeMapViewPtr, float left, float top, float right, float bottom, String[] layerIds); + int getHeight() { + return mapView.getHeight(); + } - private native void nativeSetAPIBaseURL(long nativeMapViewPtr, String baseUrl); + // + // MapChangeEvents + // - int getWidth() { - return mapView.getWidth(); - } - - int getHeight() { - return mapView.getHeight(); - } + void addOnMapChangedListener(@NonNull MapView.OnMapChangedListener listener) { + onMapChangedListeners.add(listener); + } - // - // MapChangeEvents - // - - void addOnMapChangedListener(@NonNull MapView.OnMapChangedListener listener) { - onMapChangedListeners.add(listener); - } - - void removeOnMapChangedListener(@NonNull MapView.OnMapChangedListener listener) { - onMapChangedListeners.remove(listener); - } + void removeOnMapChangedListener(@NonNull MapView.OnMapChangedListener listener) { + onMapChangedListeners.remove(listener); + } - void onMapChangedEventDispatch(int mapChange) { - if (onMapChangedListeners != null) { - for (MapView.OnMapChangedListener onMapChangedListener : onMapChangedListeners) { - onMapChangedListener.onMapChanged(mapChange); - } - } + void onMapChangedEventDispatch(int mapChange) { + if (onMapChangedListeners != null) { + for (MapView.OnMapChangedListener onMapChangedListener : onMapChangedListeners) { + onMapChangedListener.onMapChanged(mapChange); + } } + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/OnMapReadyCallback.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/OnMapReadyCallback.java index 7ace9ec2d3..fc92e5028b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/OnMapReadyCallback.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/OnMapReadyCallback.java @@ -4,16 +4,17 @@ package com.mapbox.mapboxsdk.maps; * Interface definition for a callback to be invoked when the map is ready to be used. * <p> * Once an instance of this interface is set on a {@link MapFragment} or {@link MapView} object, - * the onMapReady(MapboxMap) method is triggered when the map is ready to be used and provides an instance of {@link MapboxMap}. + * the onMapReady(MapboxMap) method is triggered when the map is ready to be used and provides an instance of + * {@link MapboxMap}. * </p> */ public interface OnMapReadyCallback { - /** - * Called when the map is ready to be used. - * - * @param mapboxMap An instance of MapboxMap associated with the {@link MapFragment} or - * {@link MapView} that defines the callback. - */ - void onMapReady(MapboxMap mapboxMap); + /** + * Called when the map is ready to be used. + * + * @param mapboxMap An instance of MapboxMap associated with the {@link MapFragment} or + * {@link MapView} that defines the callback. + */ + void onMapReady(MapboxMap mapboxMap); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java index 5a28d0c7a0..b7f93cc913 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java @@ -7,7 +7,6 @@ import android.support.annotation.NonNull; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.geometry.VisibleRegion; -import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; /** * A projection is used to translate between on screen location and geographic coordinates on @@ -16,106 +15,106 @@ import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; */ public class Projection { - private final NativeMapView nativeMapView; - private int[] contentPadding; - - Projection(@NonNull NativeMapView nativeMapView) { - this.nativeMapView = nativeMapView; - this.contentPadding = new int[]{0, 0, 0, 0}; - } - - void setContentPadding(int[] contentPadding, int[] userLocationViewPadding) { - this.contentPadding = contentPadding; - - int[] padding = new int[]{ - contentPadding[0] + userLocationViewPadding[0], - contentPadding[1] + userLocationViewPadding[1], - contentPadding[2] + userLocationViewPadding[2], - contentPadding[3] + userLocationViewPadding[3] - }; - - nativeMapView.setContentPadding(padding); - } - - int[] getContentPadding() { - return contentPadding; - } - - public void invalidateContentPadding(int[] userLocationViewPadding) { - setContentPadding(contentPadding, userLocationViewPadding); - } - - /** - * <p> - * Returns the distance spanned by one pixel at the specified latitude and current zoom level. - * </p> - * The distance between pixels decreases as the latitude approaches the poles. - * This relationship parallels the relationship between longitudinal coordinates at different latitudes. - * - * @param latitude The latitude for which to return the value. - * @return The distance measured in meters. - */ - public double getMetersPerPixelAtLatitude(@FloatRange(from = -90, to = 90) double latitude) { - return nativeMapView.getMetersPerPixelAtLatitude(latitude); - } - - /** - * Returns the geographic location that corresponds to a screen location. - * The screen location is specified in screen pixels (not display pixels) relative to the - * top left of the map (not the top left of the whole screen). - * - * @param point A Point on the screen in screen pixels. - * @return The LatLng corresponding to the point on the screen, or null if the ray through - * the given screen point does not intersect the ground plane. - */ - public LatLng fromScreenLocation(PointF point) { - return nativeMapView.latLngForPixel(point); - } - - /** - * Gets a projection of the viewing frustum for converting between screen coordinates and - * geo-latitude/longitude coordinates. - * - * @return The projection of the viewing frustum in its current state. - */ - public VisibleRegion getVisibleRegion() { - LatLngBounds.Builder builder = new LatLngBounds.Builder(); - - float left = contentPadding[0]; - float right = nativeMapView.getWidth() - contentPadding[2]; - float top = contentPadding[1]; - float bottom = nativeMapView.getHeight() - contentPadding[3]; - - LatLng topLeft = fromScreenLocation(new PointF(left, top)); - LatLng topRight = fromScreenLocation(new PointF(right, top)); - LatLng bottomRight = fromScreenLocation(new PointF(right, bottom)); - LatLng bottomLeft = fromScreenLocation(new PointF(left, bottom)); - - builder.include(topLeft) - .include(topRight) - .include(bottomRight) - .include(bottomLeft); - - return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight, builder.build()); - } - - /** - * Returns a screen location that corresponds to a geographical coordinate (LatLng). - * The screen location is in screen pixels (not display pixels) relative to the top left - * of the map (not of the whole screen). - * - * @param location A LatLng on the map to convert to a screen location. - * @return A Point representing the screen location in screen pixels. - */ - public PointF toScreenLocation(LatLng location) { - return nativeMapView.pixelForLatLng(location); - } - - float getHeight() { - return nativeMapView.getHeight(); - } - - float getWidth() { - return nativeMapView.getWidth(); - } + private final NativeMapView nativeMapView; + private int[] contentPadding; + + Projection(@NonNull NativeMapView nativeMapView) { + this.nativeMapView = nativeMapView; + this.contentPadding = new int[] {0, 0, 0, 0}; + } + + void setContentPadding(int[] contentPadding, int[] userLocationViewPadding) { + this.contentPadding = contentPadding; + + int[] padding = new int[] { + contentPadding[0] + userLocationViewPadding[0], + contentPadding[1] + userLocationViewPadding[1], + contentPadding[2] + userLocationViewPadding[2], + contentPadding[3] + userLocationViewPadding[3] + }; + + nativeMapView.setContentPadding(padding); + } + + int[] getContentPadding() { + return contentPadding; + } + + public void invalidateContentPadding(int[] userLocationViewPadding) { + setContentPadding(contentPadding, userLocationViewPadding); + } + + /** + * <p> + * Returns the distance spanned by one pixel at the specified latitude and current zoom level. + * </p> + * The distance between pixels decreases as the latitude approaches the poles. + * This relationship parallels the relationship between longitudinal coordinates at different latitudes. + * + * @param latitude The latitude for which to return the value. + * @return The distance measured in meters. + */ + public double getMetersPerPixelAtLatitude(@FloatRange(from = -90, to = 90) double latitude) { + return nativeMapView.getMetersPerPixelAtLatitude(latitude); + } + + /** + * Returns the geographic location that corresponds to a screen location. + * The screen location is specified in screen pixels (not display pixels) relative to the + * top left of the map (not the top left of the whole screen). + * + * @param point A Point on the screen in screen pixels. + * @return The LatLng corresponding to the point on the screen, or null if the ray through + * the given screen point does not intersect the ground plane. + */ + public LatLng fromScreenLocation(PointF point) { + return nativeMapView.latLngForPixel(point); + } + + /** + * Gets a projection of the viewing frustum for converting between screen coordinates and + * geo-latitude/longitude coordinates. + * + * @return The projection of the viewing frustum in its current state. + */ + public VisibleRegion getVisibleRegion() { + LatLngBounds.Builder builder = new LatLngBounds.Builder(); + + float left = contentPadding[0]; + float right = nativeMapView.getWidth() - contentPadding[2]; + float top = contentPadding[1]; + float bottom = nativeMapView.getHeight() - contentPadding[3]; + + LatLng topLeft = fromScreenLocation(new PointF(left, top)); + LatLng topRight = fromScreenLocation(new PointF(right, top)); + LatLng bottomRight = fromScreenLocation(new PointF(right, bottom)); + LatLng bottomLeft = fromScreenLocation(new PointF(left, bottom)); + + builder.include(topLeft) + .include(topRight) + .include(bottomRight) + .include(bottomLeft); + + return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight, builder.build()); + } + + /** + * Returns a screen location that corresponds to a geographical coordinate (LatLng). + * The screen location is in screen pixels (not display pixels) relative to the top left + * of the map (not of the whole screen). + * + * @param location A LatLng on the map to convert to a screen location. + * @return A Point representing the screen location in screen pixels. + */ + public PointF toScreenLocation(LatLng location) { + return nativeMapView.pixelForLatLng(location); + } + + float getHeight() { + return nativeMapView.getHeight(); + } + + float getWidth() { + return nativeMapView.getWidth(); + } } 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 75d6a01191..c0052f8364 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 @@ -35,204 +35,206 @@ import com.mapbox.mapboxsdk.exceptions.InvalidAccessTokenException; */ public class SupportMapFragment extends Fragment { - private MapView map; - private OnMapReadyCallback onMapReadyCallback; - - /** - * Creates a MapFragment instance - * - * @return MapFragment created - */ - public static SupportMapFragment newInstance() { - return new SupportMapFragment(); + private MapView map; + private OnMapReadyCallback onMapReadyCallback; + + /** + * Creates a MapFragment instance + * + * @return MapFragment created + */ + public static SupportMapFragment newInstance() { + return new SupportMapFragment(); + } + + /** + * Creates a MapFragment instance + * + * @param mapboxMapOptions The configuration options to be used. + * @return MapFragment created. + */ + public static SupportMapFragment newInstance(@Nullable MapboxMapOptions mapboxMapOptions) { + SupportMapFragment mapFragment = new SupportMapFragment(); + Bundle bundle = new Bundle(); + bundle.putParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS, mapboxMapOptions); + mapFragment.setArguments(bundle); + return mapFragment; + } + + /** + * Creates the fragment view hierarchy. + * + * @param inflater Inflater used to inflate content. + * @param container The parent layout for the map fragment. + * @param savedInstanceState The saved instance state for the map fragment. + * @return The view created + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + Context context = inflater.getContext(); + MapboxMapOptions options = null; + + // Get bundle + Bundle bundle = getArguments(); + if (bundle != null && bundle.containsKey(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS)) { + options = bundle.getParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS); } - /** - * Creates a MapFragment instance - * - * @param mapboxMapOptions The configuration options to be used. - * @return MapFragment created. - */ - public static SupportMapFragment newInstance(@Nullable MapboxMapOptions mapboxMapOptions) { - SupportMapFragment mapFragment = new SupportMapFragment(); - Bundle bundle = new Bundle(); - bundle.putParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS, mapboxMapOptions); - mapFragment.setArguments(bundle); - return mapFragment; + // Assign an AccessToken if needed + if (options == null || options.getAccessToken() == null) { + String token = null; + if (MapboxAccountManager.getInstance() != null) { + token = MapboxAccountManager.getInstance().getAccessToken(); + } else { + token = getToken(inflater.getContext()); + } + if (TextUtils.isEmpty(token)) { + throw new InvalidAccessTokenException(); + } + if (options == null) { + options = new MapboxMapOptions().accessToken(token); + } else { + options.accessToken(token); + } } - /** - * Creates the fragment view hierarchy. - * - * @param inflater Inflater used to inflate content. - * @param container The parent layout for the map fragment. - * @param savedInstanceState The saved instance state for the map fragment. - * @return The view created - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - super.onCreateView(inflater, container, savedInstanceState); - Context context = inflater.getContext(); - MapboxMapOptions options = null; - - // Get bundle - Bundle bundle = getArguments(); - if (bundle != null && bundle.containsKey(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS)) { - options = bundle.getParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS); - } - - // Assign an AccessToken if needed - if (options == null || options.getAccessToken() == null) { - String token = null; - if (MapboxAccountManager.getInstance() != null) { - token = MapboxAccountManager.getInstance().getAccessToken(); - } else { - token = getToken(inflater.getContext()); - } - if (TextUtils.isEmpty(token)) { - throw new InvalidAccessTokenException(); - } - if (options == null) { - options = new MapboxMapOptions().accessToken(token); - } else { - options.accessToken(token); - } - } - - Drawable foregroundDrawable = options.getMyLocationForegroundDrawable(); - Drawable foregroundBearingDrawable = options.getMyLocationForegroundBearingDrawable(); - if (foregroundDrawable == null || foregroundBearingDrawable == null) { - if (foregroundDrawable == null) { - foregroundDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_default); - } - if (foregroundBearingDrawable == null) { - foregroundBearingDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_bearing); - } - options.myLocationForegroundDrawables(foregroundDrawable, foregroundBearingDrawable); - } - - if (options.getMyLocationBackgroundDrawable() == null) { - options.myLocationBackgroundDrawable(ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_bg_shape)); - } - - return map = new MapView(inflater.getContext(), options); + Drawable foregroundDrawable = options.getMyLocationForegroundDrawable(); + Drawable foregroundBearingDrawable = options.getMyLocationForegroundBearingDrawable(); + if (foregroundDrawable == null || foregroundBearingDrawable == null) { + if (foregroundDrawable == null) { + foregroundDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_default); + } + if (foregroundBearingDrawable == null) { + foregroundBearingDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_bearing); + } + options.myLocationForegroundDrawables(foregroundDrawable, foregroundBearingDrawable); } - /** - * <p> - * Returns the Mapbox access token set in the app resources. - * </p> - * It will first search the application manifest for a {@link MapboxConstants#KEY_META_DATA_MANIFEST} - * meta-data value. If not found it will then attempt to load the access token from the - * {@code res/raw/token.txt} development file. - * - * @param context The {@link Context} of the {@link android.app.Activity} or {@link android.app.Fragment}. - * @return The Mapbox access token or null if not found. - * @see MapboxConstants#KEY_META_DATA_MANIFEST - * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)} - */ - @Deprecated - private String getToken(@NonNull Context context) { - try { - // read out AndroidManifest - PackageManager packageManager = context.getPackageManager(); - ApplicationInfo appInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); - String token = appInfo.metaData.getString(MapboxConstants.KEY_META_DATA_MANIFEST); - if (token == null || token.isEmpty()) { - throw new IllegalArgumentException(); - } - return token; - } catch (Exception exception) { - // use fallback on string resource, used for development - int tokenResId = context.getResources().getIdentifier("mapbox_access_token", "string", context.getPackageName()); - return tokenResId != 0 ? context.getString(tokenResId) : null; - } + if (options.getMyLocationBackgroundDrawable() == null) { + options.myLocationBackgroundDrawable(ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_bg_shape)); } - /** - * Called when the fragment view hierarchy is created. - * - * @param view The content view of the fragment - * @param savedInstanceState THe saved instance state of the framgnt - */ - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - map.onCreate(savedInstanceState); - } - - /** - * Called when the fragment is visible for the users. - */ - @Override - public void onStart() { - super.onStart(); - map.onStart(); - map.getMapAsync(onMapReadyCallback); - } - - /** - * Called when the fragment is ready to be interacted with. - */ - @Override - public void onResume() { - super.onResume(); - map.onResume(); - } - - /** - * Called when the fragment is pausing. - */ - @Override - public void onPause() { - super.onPause(); - map.onPause(); - } - - /** - * Called when the fragment state needs to be saved. - * - * @param outState The saved state - */ - @Override - public void onSaveInstanceState(@NonNull Bundle outState) { - super.onSaveInstanceState(outState); - map.onSaveInstanceState(outState); - } - - /** - * Called when the fragment is no longer visible for the user. - */ - @Override - public void onStop() { - super.onStop(); - map.onStop(); - } - - /** - * Called when the fragment receives onLowMemory call from the hosting Activity. - */ - @Override - public void onLowMemory() { - super.onLowMemory(); - map.onLowMemory(); - } - - /** - * Called when the fragment is view hiearchy is being destroyed. - */ - @Override - public void onDestroyView() { - super.onDestroyView(); - map.onDestroy(); - } - - /** - * Sets a callback object which will be triggered when the MapboxMap instance is ready to be used. - * - * @param onMapReadyCallback The callback to be invoked. - */ - public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) { - this.onMapReadyCallback = onMapReadyCallback; + return map = new MapView(inflater.getContext(), options); + } + + /** + * <p> + * Returns the Mapbox access token set in the app resources. + * </p> + * It will first search the application manifest for a {@link MapboxConstants#KEY_META_DATA_MANIFEST} + * meta-data value. If not found it will then attempt to load the access token from the + * {@code res/raw/token.txt} development file. + * + * @param context The {@link Context} of the {@link android.app.Activity} or {@link android.app.Fragment}. + * @return The Mapbox access token or null if not found. + * @see MapboxConstants#KEY_META_DATA_MANIFEST + * @deprecated As of release 4.1.0, replaced by + * {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)} + */ + @Deprecated + private String getToken(@NonNull Context context) { + try { + // read out AndroidManifest + PackageManager packageManager = context.getPackageManager(); + ApplicationInfo appInfo = packageManager.getApplicationInfo(context.getPackageName(), + PackageManager.GET_META_DATA); + String token = appInfo.metaData.getString(MapboxConstants.KEY_META_DATA_MANIFEST); + if (token == null || token.isEmpty()) { + throw new IllegalArgumentException(); + } + return token; + } catch (Exception exception) { + // use fallback on string resource, used for development + int tokenResId = context.getResources().getIdentifier("mapbox_access_token", "string", context.getPackageName()); + return tokenResId != 0 ? context.getString(tokenResId) : null; } + } + + /** + * Called when the fragment view hierarchy is created. + * + * @param view The content view of the fragment + * @param savedInstanceState THe saved instance state of the framgnt + */ + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + map.onCreate(savedInstanceState); + } + + /** + * Called when the fragment is visible for the users. + */ + @Override + public void onStart() { + super.onStart(); + map.onStart(); + map.getMapAsync(onMapReadyCallback); + } + + /** + * Called when the fragment is ready to be interacted with. + */ + @Override + public void onResume() { + super.onResume(); + map.onResume(); + } + + /** + * Called when the fragment is pausing. + */ + @Override + public void onPause() { + super.onPause(); + map.onPause(); + } + + /** + * Called when the fragment state needs to be saved. + * + * @param outState The saved state + */ + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + map.onSaveInstanceState(outState); + } + + /** + * Called when the fragment is no longer visible for the user. + */ + @Override + public void onStop() { + super.onStop(); + map.onStop(); + } + + /** + * Called when the fragment receives onLowMemory call from the hosting Activity. + */ + @Override + public void onLowMemory() { + super.onLowMemory(); + map.onLowMemory(); + } + + /** + * Called when the fragment is view hiearchy is being destroyed. + */ + @Override + public void onDestroyView() { + super.onDestroyView(); + map.onDestroy(); + } + + /** + * Sets a callback object which will be triggered when the MapboxMap instance is ready to be used. + * + * @param onMapReadyCallback The callback to be invoked. + */ + public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) { + this.onMapReadyCallback = 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 201cbe3c6b..3faebf591a 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 @@ -23,295 +23,298 @@ import timber.log.Timber; */ public final class TrackingSettings { - private final MyLocationView myLocationView; - private final UiSettings uiSettings; - private final FocalPointChangeListener focalPointChangedListener; - private LocationListener myLocationListener; - - private boolean myLocationEnabled; - private boolean dismissLocationTrackingOnGesture = true; - private boolean dismissBearingTrackingOnGesture = true; - - private MapboxMap.OnMyLocationTrackingModeChangeListener onMyLocationTrackingModeChangeListener; - private MapboxMap.OnMyBearingTrackingModeChangeListener onMyBearingTrackingModeChangeListener; - - TrackingSettings(@NonNull MyLocationView myLocationView, UiSettings uiSettings, FocalPointChangeListener focalPointChangedListener) { - this.myLocationView = myLocationView; - this.focalPointChangedListener = focalPointChangedListener; - this.uiSettings = uiSettings; + private final MyLocationView myLocationView; + private final UiSettings uiSettings; + private final FocalPointChangeListener focalPointChangedListener; + private LocationListener myLocationListener; + + private boolean myLocationEnabled; + private boolean dismissLocationTrackingOnGesture = true; + private boolean dismissBearingTrackingOnGesture = true; + + private MapboxMap.OnMyLocationTrackingModeChangeListener onMyLocationTrackingModeChangeListener; + private MapboxMap.OnMyBearingTrackingModeChangeListener onMyBearingTrackingModeChangeListener; + + TrackingSettings(@NonNull MyLocationView myLocationView, UiSettings uiSettings, + FocalPointChangeListener focalPointChangedListener) { + this.myLocationView = myLocationView; + this.focalPointChangedListener = focalPointChangedListener; + this.uiSettings = uiSettings; + } + + /** + * <p> + * Set the current my location tracking mode. + * </p> + * <p> + * Will enable my location if not active. + * </p> + * See {@link MyLocationTracking} for different values. + * + * @param myLocationTrackingMode The location tracking mode to be used. + * @throws SecurityException if no suitable permission is present + * @see MyLocationTracking + */ + @UiThread + public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) { + myLocationView.setMyLocationTrackingMode(myLocationTrackingMode); + + if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { + focalPointChangedListener.onFocalPointChanged(new PointF(myLocationView.getCenterX(), + myLocationView.getCenterY())); + } else { + focalPointChangedListener.onFocalPointChanged(null); } - /** - * <p> - * Set the current my location tracking mode. - * </p> - * <p> - * Will enable my location if not active. - * </p> - * See {@link MyLocationTracking} for different values. - * - * @param myLocationTrackingMode The location tracking mode to be used. - * @throws SecurityException if no suitable permission is present - * @see MyLocationTracking - */ - @UiThread - public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) { - myLocationView.setMyLocationTrackingMode(myLocationTrackingMode); - - if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { - focalPointChangedListener.onFocalPointChanged(new PointF(myLocationView.getCenterX(), myLocationView.getCenterY())); - } else { - focalPointChangedListener.onFocalPointChanged(null); - } - - if (onMyLocationTrackingModeChangeListener != null) { - onMyLocationTrackingModeChangeListener.onMyLocationTrackingModeChange(myLocationTrackingMode); - } - } - - /** - * Returns the current user location tracking mode. - * - * @return The current user location tracking mode. - * One of the values from {@link MyLocationTracking.Mode}. - * @see MyLocationTracking.Mode - */ - @UiThread - @MyLocationTracking.Mode - public int getMyLocationTrackingMode() { - return myLocationView.getMyLocationTrackingMode(); - } - - /** - * <p> - * Set the current my bearing tracking mode. - * </p> - * Shows the direction the user is heading. - * <p> - * When location tracking is disabled the direction of {@link MyLocationView} is rotated. When - * location tracking is enabled the {@link MapView} is rotated based on the bearing value. - * </p> - * See {@link MyBearingTracking} for different values. - * - * @param myBearingTrackingMode The bearing tracking mode to be used. - * @throws SecurityException if no suitable permission is present - * @see MyBearingTracking - */ - @UiThread - public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) { - myLocationView.setMyBearingTrackingMode(myBearingTrackingMode); - if (onMyBearingTrackingModeChangeListener != null) { - onMyBearingTrackingModeChangeListener.onMyBearingTrackingModeChange(myBearingTrackingMode); - } - } - - /** - * Returns the current user bearing tracking mode. - * See {@link MyBearingTracking} for possible return values. - * - * @return the current user bearing tracking mode. - * @see MyBearingTracking - */ - @UiThread - @MyBearingTracking.Mode - public int getMyBearingTrackingMode() { - return myLocationView.getMyBearingTrackingMode(); + if (onMyLocationTrackingModeChangeListener != null) { + onMyLocationTrackingModeChangeListener.onMyLocationTrackingModeChange(myLocationTrackingMode); } - - /** - * Returns if the tracking modes will be dismissed when a gesture occurs. - * - * @return True to indicate the tracking modes will be dismissed. - * @deprecated use @link #isAllDismissTrackingOnGestureinstead - */ - @Deprecated - public boolean isDismissTrackingOnGesture() { - return dismissLocationTrackingOnGesture && dismissBearingTrackingOnGesture; + } + + /** + * Returns the current user location tracking mode. + * + * @return The current user location tracking mode. + * One of the values from {@link MyLocationTracking.Mode}. + * @see MyLocationTracking.Mode + */ + @UiThread + @MyLocationTracking.Mode + public int getMyLocationTrackingMode() { + return myLocationView.getMyLocationTrackingMode(); + } + + /** + * <p> + * Set the current my bearing tracking mode. + * </p> + * Shows the direction the user is heading. + * <p> + * When location tracking is disabled the direction of {@link MyLocationView} is rotated. When + * location tracking is enabled the {@link MapView} is rotated based on the bearing value. + * </p> + * See {@link MyBearingTracking} for different values. + * + * @param myBearingTrackingMode The bearing tracking mode to be used. + * @throws SecurityException if no suitable permission is present + * @see MyBearingTracking + */ + @UiThread + public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) { + myLocationView.setMyBearingTrackingMode(myBearingTrackingMode); + if (onMyBearingTrackingModeChangeListener != null) { + onMyBearingTrackingModeChangeListener.onMyBearingTrackingModeChange(myBearingTrackingMode); } - - /** - * Returns if all tracking modes will be dismissed when a gesture occurs. - * - * @return True to indicate that location and bearing tracking will be dismissed. - */ - public boolean isAllDismissTrackingOnGesture() { - return dismissLocationTrackingOnGesture && dismissBearingTrackingOnGesture; - } - - /** - * Set the dismissal of the tracking modes if a gesture occurs. - * - * @param dismissTrackingOnGesture True to dismiss the tracking modes. - * @deprecated use @link #setDismissAllTrackingOnGesture instead - */ - @Deprecated - public void setDismissTrackingOnGesture(boolean dismissTrackingOnGesture) { - setDismissAllTrackingOnGesture(dismissTrackingOnGesture); + } + + /** + * Returns the current user bearing tracking mode. + * See {@link MyBearingTracking} for possible return values. + * + * @return the current user bearing tracking mode. + * @see MyBearingTracking + */ + @UiThread + @MyBearingTracking.Mode + public int getMyBearingTrackingMode() { + return myLocationView.getMyBearingTrackingMode(); + } + + /** + * Returns if the tracking modes will be dismissed when a gesture occurs. + * + * @return True to indicate the tracking modes will be dismissed. + * @deprecated use @link #isAllDismissTrackingOnGestureinstead + */ + @Deprecated + public boolean isDismissTrackingOnGesture() { + return dismissLocationTrackingOnGesture && dismissBearingTrackingOnGesture; + } + + /** + * Returns if all tracking modes will be dismissed when a gesture occurs. + * + * @return True to indicate that location and bearing tracking will be dismissed. + */ + public boolean isAllDismissTrackingOnGesture() { + return dismissLocationTrackingOnGesture && dismissBearingTrackingOnGesture; + } + + /** + * Set the dismissal of the tracking modes if a gesture occurs. + * + * @param dismissTrackingOnGesture True to dismiss the tracking modes. + * @deprecated use @link #setDismissAllTrackingOnGesture instead + */ + @Deprecated + public void setDismissTrackingOnGesture(boolean dismissTrackingOnGesture) { + setDismissAllTrackingOnGesture(dismissTrackingOnGesture); + } + + /** + * Set the dismissal of the tracking modes if a gesture occurs. + * + * @param dismissTrackingOnGesture True to dismiss all the tracking modes. + */ + public void setDismissAllTrackingOnGesture(boolean dismissTrackingOnGesture) { + dismissLocationTrackingOnGesture = dismissTrackingOnGesture; + dismissBearingTrackingOnGesture = dismissTrackingOnGesture; + } + + /** + * Set the dismissal of the tracking modes if a gesture occurs. + * + * @param dismissLocationTrackingOnGesture True to dismiss the location tracking mode. + */ + public void setDismissLocationTrackingOnGesture(boolean dismissLocationTrackingOnGesture) { + this.dismissLocationTrackingOnGesture = dismissLocationTrackingOnGesture; + } + + /** + * Returns if the location tracking will be disabled when a gesture occurs + * + * @return True if location tracking will be disabled. + */ + public boolean isDismissLocationTrackingOnGesture() { + return dismissLocationTrackingOnGesture; + } + + /** + * Set the dismissal of the bearing tracking modes if a gesture occurs. + * + * @param dismissBearingTrackingOnGesture True to dimsiss the bearinf tracking mode + */ + public void setDismissBearingTrackingOnGesture(boolean dismissBearingTrackingOnGesture) { + this.dismissBearingTrackingOnGesture = dismissBearingTrackingOnGesture; + } + + /** + * Returns if bearing will disabled when a gesture occurs + * + * @return True if bearing tracking will be disabled + */ + public boolean isDismissBearingTrackingOnGesture() { + return dismissBearingTrackingOnGesture; + } + + /** + * Returns if location tracking is disabled + * + * @return True if location tracking is disabled. + */ + public boolean isLocationTrackingDisabled() { + return myLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE; + } + + /** + * Returns if bearing tracking disabled + * + * @return True if bearing tracking is disabled. + */ + public boolean isBearingTrackingDisabled() { + return myLocationView.getMyBearingTrackingMode() == MyBearingTracking.NONE; + } + + /** + * Returns if rotate gesture are currently enabled. + * + * @return True if rotate gestures are currently enabled. + */ + public boolean isRotateGestureCurrentlyEnabled() { + // rotate gestures are recognised if: + // The user settings are enabled AND; + // EITHER bearing tracking is dismissed on gesture OR there is no bearing tracking + return uiSettings.isRotateGesturesEnabled() + && (dismissBearingTrackingOnGesture + || myLocationView.getMyBearingTrackingMode() == MyBearingTracking.NONE + || myLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE); + } + + /** + * Returns if scroll gesture are currently enabled. + * + * @return True if scroll gestures are currently enabled. + */ + public boolean isScrollGestureCurrentlyEnabled() { + return uiSettings.isScrollGesturesEnabled() + && (dismissLocationTrackingOnGesture + || myLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE); + } + + /** + * Reset the tracking modes as necessary. Location tracking is reset if the map center is changed, + * bearing tracking if there is a rotation. + */ + void resetTrackingModesIfRequired(boolean translate, boolean rotate) { + // if tracking is on, and we should dismiss tracking with gestures, and this is a scroll action, turn tracking off + if (translate && !isLocationTrackingDisabled() && isDismissLocationTrackingOnGesture()) { + setMyLocationTrackingMode(MyLocationTracking.TRACKING_NONE); } - /** - * Set the dismissal of the tracking modes if a gesture occurs. - * - * @param dismissTrackingOnGesture True to dismiss all the tracking modes. - */ - public void setDismissAllTrackingOnGesture(boolean dismissTrackingOnGesture) { - dismissLocationTrackingOnGesture = dismissTrackingOnGesture; - dismissBearingTrackingOnGesture = dismissTrackingOnGesture; + // reset bearing tracking only on rotate + if (rotate && !isBearingTrackingDisabled() && isDismissBearingTrackingOnGesture()) { + setMyBearingTrackingMode(MyBearingTracking.NONE); } - - /** - * Set the dismissal of the tracking modes if a gesture occurs. - * - * @param dismissLocationTrackingOnGesture True to dismiss the location tracking mode. - */ - public void setDismissLocationTrackingOnGesture(boolean dismissLocationTrackingOnGesture) { - this.dismissLocationTrackingOnGesture = dismissLocationTrackingOnGesture; - } - - /** - * Returns if the location tracking will be disabled when a gesture occurs - * - * @return True if location tracking will be disabled. - */ - public boolean isDismissLocationTrackingOnGesture() { - return dismissLocationTrackingOnGesture; - } - - /** - * Set the dismissal of the bearing tracking modes if a gesture occurs. - * - * @param dismissBearingTrackingOnGesture True to dimsiss the bearinf tracking mode - */ - public void setDismissBearingTrackingOnGesture(boolean dismissBearingTrackingOnGesture) { - this.dismissBearingTrackingOnGesture = dismissBearingTrackingOnGesture; - } - - /** - * Returns if bearing will disabled when a gesture occurs - * - * @return True if bearing tracking will be disabled - */ - public boolean isDismissBearingTrackingOnGesture() { - return dismissBearingTrackingOnGesture; - } - - /** - * Returns if location tracking is disabled - * - * @return True if location tracking is disabled. - */ - public boolean isLocationTrackingDisabled() { - return myLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE; - } - - /** - * Returns if bearing tracking disabled - * - * @return True if bearing tracking is disabled. - */ - public boolean isBearingTrackingDisabled() { - return myLocationView.getMyBearingTrackingMode() == MyBearingTracking.NONE; - } - - /** - * Returns if rotate gesture are currently enabled. - * - * @return True if rotate gestures are currently enabled. - */ - public boolean isRotateGestureCurrentlyEnabled() { - // rotate gestures are recognised if: - // The user settings are enabled AND; - // EITHER bearing tracking is dismissed on gesture OR there is no bearing tracking - return uiSettings.isRotateGesturesEnabled() && - (dismissBearingTrackingOnGesture - || myLocationView.getMyBearingTrackingMode() == MyBearingTracking.NONE - || myLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE); - } - - /** - * Returns if scroll gesture are currently enabled. - * - * @return True if scroll gestures are currently enabled. - */ - public boolean isScrollGestureCurrentlyEnabled() { - return uiSettings.isScrollGesturesEnabled() && - (dismissLocationTrackingOnGesture - || myLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE); - } - - /** - * Reset the tracking modes as necessary. Location tracking is reset if the map center is changed, - * bearing tracking if there is a rotation. - * - * @param translate - * @param rotate - */ - void resetTrackingModesIfRequired(boolean translate, boolean rotate) { - // if tracking is on, and we should dismiss tracking with gestures, and this is a scroll action, turn tracking off - if (translate && !isLocationTrackingDisabled() && isDismissLocationTrackingOnGesture()) { - setMyLocationTrackingMode(MyLocationTracking.TRACKING_NONE); - } - - // reset bearing tracking only on rotate - if (rotate && !isBearingTrackingDisabled() && isDismissBearingTrackingOnGesture()) { - setMyBearingTrackingMode(MyBearingTracking.NONE); - } - } - - void resetTrackingModesIfRequired(CameraPosition cameraPosition) { - resetTrackingModesIfRequired(cameraPosition.target != null, cameraPosition.bearing != -1); - } - - Location getMyLocation() { - return myLocationView.getLocation(); - } - - void setOnMyLocationChangeListener(@Nullable final MapboxMap.OnMyLocationChangeListener listener) { - if (listener != null) { - myLocationListener = new LocationListener() { - @Override - public void onLocationChanged(Location location) { - if (listener != null) { - listener.onMyLocationChange(location); - } - } - }; - LocationServices.getLocationServices(myLocationView.getContext()).addLocationListener(myLocationListener); - } else { - LocationServices.getLocationServices(myLocationView.getContext()).removeLocationListener(myLocationListener); - myLocationListener = null; + } + + void resetTrackingModesIfRequired(CameraPosition cameraPosition) { + resetTrackingModesIfRequired(cameraPosition.target != null, cameraPosition.bearing != -1); + } + + Location getMyLocation() { + return myLocationView.getLocation(); + } + + void setOnMyLocationChangeListener(@Nullable final MapboxMap.OnMyLocationChangeListener listener) { + if (listener != null) { + myLocationListener = new LocationListener() { + @Override + public void onLocationChanged(Location location) { + if (listener != null) { + listener.onMyLocationChange(location); + } } + }; + LocationServices.getLocationServices(myLocationView.getContext()).addLocationListener(myLocationListener); + } else { + LocationServices.getLocationServices(myLocationView.getContext()).removeLocationListener(myLocationListener); + myLocationListener = null; } - - boolean isPermissionsAccepted() { - return (ContextCompat.checkSelfPermission(myLocationView.getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) || - ContextCompat.checkSelfPermission(myLocationView.getContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; - } - - void setOnMyLocationTrackingModeChangeListener(MapboxMap.OnMyLocationTrackingModeChangeListener onMyLocationTrackingModeChangeListener) { - this.onMyLocationTrackingModeChangeListener = onMyLocationTrackingModeChangeListener; - } - - void setOnMyBearingTrackingModeChangeListener(MapboxMap.OnMyBearingTrackingModeChangeListener onMyBearingTrackingModeChangeListener) { - this.onMyBearingTrackingModeChangeListener = onMyBearingTrackingModeChangeListener; - } - - MyLocationView getMyLocationView() { - return myLocationView; - } - - - boolean isMyLocationEnabled() { - return myLocationEnabled; - } - - void setMyLocationEnabled(boolean locationEnabled) { - if (!isPermissionsAccepted()) { - Timber.e("Could not activate user location tracking: " - + "user did not accept the permission or permissions were not requested."); - return; - } - myLocationEnabled = locationEnabled; - myLocationView.setEnabled(locationEnabled); + } + + boolean isPermissionsAccepted() { + return (ContextCompat.checkSelfPermission(myLocationView.getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) + == PackageManager.PERMISSION_GRANTED) + || ContextCompat.checkSelfPermission(myLocationView.getContext(), Manifest.permission.ACCESS_FINE_LOCATION) + == PackageManager.PERMISSION_GRANTED; + } + + void setOnMyLocationTrackingModeChangeListener( + MapboxMap.OnMyLocationTrackingModeChangeListener onMyLocationTrackingModeChangeListener) { + this.onMyLocationTrackingModeChangeListener = onMyLocationTrackingModeChangeListener; + } + + void setOnMyBearingTrackingModeChangeListener( + MapboxMap.OnMyBearingTrackingModeChangeListener onMyBearingTrackingModeChangeListener) { + this.onMyBearingTrackingModeChangeListener = onMyBearingTrackingModeChangeListener; + } + + MyLocationView getMyLocationView() { + return myLocationView; + } + + + boolean isMyLocationEnabled() { + return myLocationEnabled; + } + + void setMyLocationEnabled(boolean locationEnabled) { + if (!isPermissionsAccepted()) { + Timber.e("Could not activate user location tracking: " + + "user did not accept the permission or permissions were not requested."); + return; } + myLocationEnabled = locationEnabled; + myLocationView.setEnabled(locationEnabled); + } } 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 2babc59f1b..88acc13356 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 @@ -21,275 +21,280 @@ import static com.mapbox.mapboxsdk.maps.MapView.REGION_DID_CHANGE_ANIMATED; /** * Resembles the current Map transformation. * <p> - * Responsible for synchronising {@link CameraPosition} state and notifying {@link com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraChangeListener}. + * Responsible for synchronising {@link CameraPosition} state and notifying + * {@link com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraChangeListener}. * </p> */ final class Transform implements MapView.OnMapChangedListener { - private final NativeMapView mapView; - private final MarkerViewManager markerViewManager; - private final TrackingSettings trackingSettings; - private final MyLocationView myLocationView; - - private CameraPosition cameraPosition; - private MapboxMap.CancelableCallback cameraCancelableCallback; - private MapboxMap.OnCameraChangeListener onCameraChangeListener; - - Transform(NativeMapView mapView, MarkerViewManager markerViewManager, TrackingSettings trackingSettings) { - this.mapView = mapView; - this.markerViewManager = markerViewManager; - this.trackingSettings = trackingSettings; - this.myLocationView = trackingSettings.getMyLocationView(); + private final NativeMapView mapView; + private final MarkerViewManager markerViewManager; + private final TrackingSettings trackingSettings; + private final MyLocationView myLocationView; + + private CameraPosition cameraPosition; + private MapboxMap.CancelableCallback cameraCancelableCallback; + private MapboxMap.OnCameraChangeListener onCameraChangeListener; + + Transform(NativeMapView mapView, MarkerViewManager markerViewManager, TrackingSettings trackingSettings) { + this.mapView = mapView; + this.markerViewManager = markerViewManager; + this.trackingSettings = trackingSettings; + this.myLocationView = trackingSettings.getMyLocationView(); + } + + void initialise(@NonNull MapboxMap mapboxMap, @NonNull MapboxMapOptions options) { + CameraPosition position = options.getCamera(); + if (position != null && !position.equals(CameraPosition.DEFAULT)) { + moveCamera(mapboxMap, CameraUpdateFactory.newCameraPosition(position), null); + } + } + + // + // Camera API + // + + @UiThread + public final CameraPosition getCameraPosition() { + if (cameraPosition == null) { + cameraPosition = invalidateCameraPosition(); + } + return cameraPosition; + } + + @UiThread + void updateCameraPosition(@NonNull CameraPosition position) { + if (myLocationView != null) { + myLocationView.setCameraPosition(position); + } + markerViewManager.setTilt((float) position.tilt); + } + + @Override + public void onMapChanged(@MapView.MapChange int change) { + if (change == REGION_DID_CHANGE_ANIMATED && cameraCancelableCallback != null) { + invalidateCameraPosition(); + if (cameraCancelableCallback != null) { + cameraCancelableCallback.onFinish(); + cameraCancelableCallback = null; + } + mapView.removeOnMapChangedListener(this); + } + } + + @UiThread + final void moveCamera(MapboxMap mapboxMap, CameraUpdate update, MapboxMap.CancelableCallback callback) { + cameraPosition = update.getCameraPosition(mapboxMap); + trackingSettings.resetTrackingModesIfRequired(cameraPosition); + cancelTransitions(); + mapView.jumpTo(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt, cameraPosition.zoom); + if (callback != null) { + callback.onFinish(); + } + } + + @UiThread + final void easeCamera(MapboxMap mapboxMap, CameraUpdate update, int durationMs, boolean easingInterpolator, + boolean resetTrackingMode, final MapboxMap.CancelableCallback callback) { + cameraPosition = update.getCameraPosition(mapboxMap); + if (resetTrackingMode) { + trackingSettings.resetTrackingModesIfRequired(cameraPosition); } - void initialise(@NonNull MapboxMap mapboxMap, @NonNull MapboxMapOptions options) { - CameraPosition position = options.getCamera(); - if (position != null && !position.equals(CameraPosition.DEFAULT)) { - moveCamera(mapboxMap, CameraUpdateFactory.newCameraPosition(position), null); - } - } + cancelTransitions(); + if (callback != null) { + cameraCancelableCallback = callback; + mapView.addOnMapChangedListener(this); + } + + mapView.easeTo(cameraPosition.bearing, cameraPosition.target, getDurationNano(durationMs), cameraPosition.tilt, + cameraPosition.zoom, easingInterpolator); + } - // - // Camera API - // + @UiThread + final void animateCamera(MapboxMap mapboxMap, CameraUpdate update, int durationMs, + final MapboxMap.CancelableCallback callback) { + cameraPosition = update.getCameraPosition(mapboxMap); + trackingSettings.resetTrackingModesIfRequired(cameraPosition); - @UiThread - public final CameraPosition getCameraPosition() { - if (cameraPosition == null) { - cameraPosition = invalidateCameraPosition(); - } - return cameraPosition; + cancelTransitions(); + if (callback != null) { + cameraCancelableCallback = callback; + mapView.addOnMapChangedListener(this); } - @UiThread - void updateCameraPosition(@NonNull CameraPosition position) { - if (myLocationView != null) { - myLocationView.setCameraPosition(position); - } - markerViewManager.setTilt((float) position.tilt); - } + mapView.flyTo(cameraPosition.bearing, cameraPosition.target, getDurationNano(durationMs), cameraPosition.tilt, + cameraPosition.zoom); + } - @Override - public void onMapChanged(@MapView.MapChange int change) { - if (change == REGION_DID_CHANGE_ANIMATED && cameraCancelableCallback != null) { - invalidateCameraPosition(); - if (cameraCancelableCallback != null) { - cameraCancelableCallback.onFinish(); - cameraCancelableCallback = null; - } - mapView.removeOnMapChangedListener(this); - } + @UiThread + @Nullable + CameraPosition invalidateCameraPosition() { + if (mapView != null) { + cameraPosition = new CameraPosition.Builder(mapView.getCameraValues()).build(); + if (onCameraChangeListener != null) { + onCameraChangeListener.onCameraChange(this.cameraPosition); + } } + return cameraPosition; + } - @UiThread - final void moveCamera(MapboxMap mapboxMap, CameraUpdate update, MapboxMap.CancelableCallback callback) { - cameraPosition = update.getCameraPosition(mapboxMap); - trackingSettings.resetTrackingModesIfRequired(cameraPosition); - cancelTransitions(); - mapView.jumpTo(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt, cameraPosition.zoom); - if (callback != null) { - callback.onFinish(); - } + void cancelTransitions() { + if (cameraCancelableCallback != null) { + cameraCancelableCallback.onCancel(); + cameraCancelableCallback = null; } + mapView.cancelTransitions(); + } - @UiThread - final void easeCamera(MapboxMap mapboxMap, CameraUpdate update, int durationMs, boolean easingInterpolator, boolean resetTrackingMode, final MapboxMap.CancelableCallback callback) { - cameraPosition = update.getCameraPosition(mapboxMap); - if (resetTrackingMode) { - trackingSettings.resetTrackingModesIfRequired(cameraPosition); - } + @UiThread + void resetNorth() { + cancelTransitions(); + mapView.resetNorth(); + } - cancelTransitions(); - if (callback != null) { - cameraCancelableCallback = callback; - mapView.addOnMapChangedListener(this); - } + void setOnCameraChangeListener(@Nullable MapboxMap.OnCameraChangeListener listener) { + this.onCameraChangeListener = listener; + } - mapView.easeTo(cameraPosition.bearing, cameraPosition.target, getDurationNano(durationMs), cameraPosition.tilt, cameraPosition.zoom, easingInterpolator); - } + private long getDurationNano(long durationMs) { + return durationMs > 0 ? TimeUnit.NANOSECONDS.convert(durationMs, TimeUnit.MILLISECONDS) : 0; + } - @UiThread - final void animateCamera(MapboxMap mapboxMap, CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) { - cameraPosition = update.getCameraPosition(mapboxMap); - trackingSettings.resetTrackingModesIfRequired(cameraPosition); + // + // non Camera API + // - cancelTransitions(); - if (callback != null) { - cameraCancelableCallback = callback; - mapView.addOnMapChangedListener(this); - } + // Zoom in or out - mapView.flyTo(cameraPosition.bearing, cameraPosition.target, getDurationNano(durationMs), cameraPosition.tilt, cameraPosition.zoom); - } + double getZoom() { + return cameraPosition.zoom; + } - @UiThread - @Nullable - CameraPosition invalidateCameraPosition() { - if (mapView != null) { - cameraPosition = new CameraPosition.Builder(mapView.getCameraValues()).build(); - if (onCameraChangeListener != null) { - onCameraChangeListener.onCameraChange(this.cameraPosition); - } - } - return cameraPosition; - } + void zoom(boolean zoomIn) { + zoom(zoomIn, -1.0f, -1.0f); + } - void cancelTransitions() { - if (cameraCancelableCallback != null) { - cameraCancelableCallback.onCancel(); - cameraCancelableCallback = null; - } - mapView.cancelTransitions(); - } + void zoom(boolean zoomIn, float x, float y) { + // Cancel any animation + cancelTransitions(); - @UiThread - void resetNorth() { - cancelTransitions(); - mapView.resetNorth(); + if (zoomIn) { + mapView.scaleBy(2.0, x, y, MapboxConstants.ANIMATION_DURATION); + } else { + mapView.scaleBy(0.5, x, y, MapboxConstants.ANIMATION_DURATION); } + } - void setOnCameraChangeListener(@Nullable MapboxMap.OnCameraChangeListener listener) { - this.onCameraChangeListener = listener; - } + // Direction + double getBearing() { + double direction = -mapView.getBearing(); - private long getDurationNano(long durationMs) { - return durationMs > 0 ? TimeUnit.NANOSECONDS.convert(durationMs, TimeUnit.MILLISECONDS) : 0; + while (direction > 360) { + direction -= 360; } - - // - // non Camera API - // - - // Zoom in or out - - double getZoom() { - return cameraPosition.zoom; + while (direction < 0) { + direction += 360; } - void zoom(boolean zoomIn) { - zoom(zoomIn, -1.0f, -1.0f); - } + return direction; + } - void zoom(boolean zoomIn, float x, float y) { - // Cancel any animation - cancelTransitions(); + double getRawBearing() { + return mapView.getBearing(); + } - if (zoomIn) { - mapView.scaleBy(2.0, x, y, MapboxConstants.ANIMATION_DURATION); - } else { - mapView.scaleBy(0.5, x, y, MapboxConstants.ANIMATION_DURATION); - } + void setBearing(double bearing) { + if (myLocationView != null) { + myLocationView.setBearing(bearing); } + mapView.setBearing(bearing); + } - // Direction - double getBearing() { - double direction = -mapView.getBearing(); - - while (direction > 360) { - direction -= 360; - } - while (direction < 0) { - direction += 360; - } - - return direction; + void setBearing(double bearing, float focalX, float focalY) { + if (myLocationView != null) { + myLocationView.setBearing(bearing); } + mapView.setBearing(bearing, focalX, focalY); + } - double getRawBearing(){ - return mapView.getBearing(); - } - - void setBearing(double bearing) { - if (myLocationView != null) { - myLocationView.setBearing(bearing); - } - mapView.setBearing(bearing); - } - void setBearing(double bearing, float focalX, float focalY) { - if (myLocationView != null) { - myLocationView.setBearing(bearing); - } - mapView.setBearing(bearing, focalX, focalY); - } + // + // LatLng / CenterCoordinate + // + LatLng getLatLng() { + return mapView.getLatLng(); + } - // - // LatLng / CenterCoordinate - // - - LatLng getLatLng() { - return mapView.getLatLng(); - } - - // - // Pitch / Tilt - // + // + // Pitch / Tilt + // - double getTilt() { - return mapView.getPitch(); - } + double getTilt() { + return mapView.getPitch(); + } - void setTilt(Double pitch) { - if (myLocationView != null) { - myLocationView.setTilt(pitch); - } - markerViewManager.setTilt(pitch.floatValue()); - mapView.setPitch(pitch, 0); + void setTilt(Double pitch) { + if (myLocationView != null) { + myLocationView.setTilt(pitch); } + markerViewManager.setTilt(pitch.floatValue()); + mapView.setPitch(pitch, 0); + } - // - // Center coordinate - // + // + // Center coordinate + // - LatLng getCenterCoordinate() { - return mapView.getLatLng(); - } + LatLng getCenterCoordinate() { + return mapView.getLatLng(); + } - void setCenterCoordinate(LatLng centerCoordinate) { - mapView.setLatLng(centerCoordinate); - } + void setCenterCoordinate(LatLng centerCoordinate) { + mapView.setLatLng(centerCoordinate); + } - void setGestureInProgress(boolean gestureInProgress) { - mapView.setGestureInProgress(gestureInProgress); - if (!gestureInProgress) { - invalidateCameraPosition(); - } + void setGestureInProgress(boolean gestureInProgress) { + mapView.setGestureInProgress(gestureInProgress); + if (!gestureInProgress) { + invalidateCameraPosition(); } + } - void zoomBy(double pow, float x, float y) { - mapView.scaleBy(pow, x, y); - } + void zoomBy(double pow, float x, float y) { + mapView.scaleBy(pow, x, y); + } - void moveBy(double offsetX, double offsetY, long duration) { - mapView.moveBy(offsetX, offsetY, duration); - } + void moveBy(double offsetX, double offsetY, long duration) { + mapView.moveBy(offsetX, offsetY, duration); + } - // - // Min & Max ZoomLevel - // + // + // Min & Max ZoomLevel + // - void setMinZoom(double minZoom) { - if ((minZoom < MapboxConstants.MINIMUM_ZOOM) || (minZoom > MapboxConstants.MAXIMUM_ZOOM)) { - Timber.e("Not setting minZoomPreference, value is in unsupported range: " + minZoom); - return; - } - mapView.setMinZoom(minZoom); + void setMinZoom(double minZoom) { + if ((minZoom < MapboxConstants.MINIMUM_ZOOM) || (minZoom > MapboxConstants.MAXIMUM_ZOOM)) { + Timber.e("Not setting minZoomPreference, value is in unsupported range: " + minZoom); + return; } + mapView.setMinZoom(minZoom); + } - double getMinZoom() { - return mapView.getMinZoom(); - } + double getMinZoom() { + return mapView.getMinZoom(); + } - void setMaxZoom(double maxZoom) { - if ((maxZoom < MapboxConstants.MINIMUM_ZOOM) || (maxZoom > MapboxConstants.MAXIMUM_ZOOM)) { - Timber.e("Not setting maxZoomPreference, value is in unsupported range: " + maxZoom); - return; - } - mapView.setMaxZoom(maxZoom); + void setMaxZoom(double maxZoom) { + if ((maxZoom < MapboxConstants.MINIMUM_ZOOM) || (maxZoom > MapboxConstants.MAXIMUM_ZOOM)) { + Timber.e("Not setting maxZoomPreference, value is in unsupported range: " + maxZoom); + return; } + mapView.setMaxZoom(maxZoom); + } - double getMaxZoom() { - return mapView.getMaxZoom(); - } + double getMaxZoom() { + return mapView.getMaxZoom(); + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java index 91d1c243f9..b58d5bfc0e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java @@ -23,697 +23,700 @@ import com.mapbox.mapboxsdk.utils.ColorUtils; */ public final class UiSettings { - private final FocalPointChangeListener focalPointChangeListener; - private final Projection projection; - private final CompassView compassView; - private final ImageView attributionsView; - private final View logoView; - private float pixelRatio; - - private boolean rotateGesturesEnabled = true; - private boolean rotateGestureChangeAllowed = true; - - private boolean tiltGesturesEnabled = true; - private boolean tiltGestureChangeAllowed = true; - - private boolean zoomGesturesEnabled = true; - private boolean zoomGestureChangeAllowed = true; - - private boolean scrollGesturesEnabled = true; - private boolean scrollGestureChangeAllowed = true; - - private boolean zoomControlsEnabled; - - private boolean deselectMarkersOnTap = true; - - private PointF userProvidedFocalPoint; - - UiSettings(@NonNull Projection projection, @NonNull FocalPointChangeListener listener, @NonNull CompassView compassView, @NonNull ImageView attributionsView, @NonNull View logoView) { - this.projection = projection; - this.focalPointChangeListener = listener; - this.compassView = compassView; - this.attributionsView = attributionsView; - this.logoView = logoView; - if (logoView.getResources() != null) { - this.pixelRatio = logoView.getResources().getDisplayMetrics().density; - } - } - - void initialise(@NonNull Context context, @NonNull MapboxMapOptions options) { - Resources resources = context.getResources(); - initialiseGestures(options); - initialiseCompass(options, resources); - initialiseLogo(options, resources); - initialiseAttribution(context, options); - } - - private void initialiseGestures(MapboxMapOptions options) { - setZoomGesturesEnabled(options.getZoomGesturesEnabled()); - setZoomGestureChangeAllowed(options.getZoomGesturesEnabled()); - setScrollGesturesEnabled(options.getScrollGesturesEnabled()); - setScrollGestureChangeAllowed(options.getScrollGesturesEnabled()); - setRotateGesturesEnabled(options.getRotateGesturesEnabled()); - setRotateGestureChangeAllowed(options.getRotateGesturesEnabled()); - setTiltGesturesEnabled(options.getTiltGesturesEnabled()); - setTiltGestureChangeAllowed(options.getTiltGesturesEnabled()); - setZoomControlsEnabled(options.getZoomControlsEnabled()); - } - - private void initialiseCompass(MapboxMapOptions options, Resources resources) { - setCompassEnabled(options.getCompassEnabled()); - setCompassGravity(options.getCompassGravity()); - int[] compassMargins = options.getCompassMargins(); - if (compassMargins != null) { - setCompassMargins(compassMargins[0], compassMargins[1], compassMargins[2], compassMargins[3]); - } else { - int tenDp = (int) resources.getDimension(R.dimen.mapbox_ten_dp); - setCompassMargins(tenDp, tenDp, tenDp, tenDp); - } - setCompassFadeFacingNorth(options.getCompassFadeFacingNorth()); - } - - private void initialiseLogo(MapboxMapOptions options, Resources resources) { - setLogoEnabled(options.getLogoEnabled()); - setLogoGravity(options.getLogoGravity()); - int[] logoMargins = options.getLogoMargins(); - if (logoMargins != null) { - setLogoMargins(logoMargins[0], logoMargins[1], logoMargins[2], logoMargins[3]); - } else { - int sixteenDp = (int) resources.getDimension(R.dimen.mapbox_sixteen_dp); - setLogoMargins(sixteenDp, sixteenDp, sixteenDp, sixteenDp); - } - } - - private void initialiseAttribution(Context context, MapboxMapOptions options) { - Resources resources = context.getResources(); - setAttributionEnabled(options.getAttributionEnabled()); - setAttributionGravity(options.getAttributionGravity()); - int[] attributionMargins = options.getAttributionMargins(); - if (attributionMargins != null) { - setAttributionMargins(attributionMargins[0], attributionMargins[1], attributionMargins[2], attributionMargins[3]); - } else { - int sevenDp = (int) resources.getDimension(R.dimen.mapbox_seven_dp); - int seventySixDp = (int) resources.getDimension(R.dimen.mapbox_seventy_six_dp); - setAttributionMargins(seventySixDp, sevenDp, sevenDp, sevenDp); - } - - int attributionTintColor = options.getAttributionTintColor(); - setAttributionTintColor(attributionTintColor != -1 - ? attributionTintColor : ColorUtils.getPrimaryColor(context)); - } - - /** - * <p> - * Enables or disables the compass. The compass is an icon on the map that indicates the - * direction of north on the map. When a user clicks - * the compass, the camera orients itself to its default orientation and fades away shortly - * after. If disabled, the compass will never be displayed. - * </p> - * By default, the compass is enabled. - * - * @param compassEnabled True to enable the compass; false to disable the compass. - */ - public void setCompassEnabled(boolean compassEnabled) { - compassView.setEnabled(compassEnabled); - } - - /** - * Returns whether the compass is enabled. - * - * @return True if the compass is enabled; false if the compass is disabled. - */ - public boolean isCompassEnabled() { - return compassView.isEnabled(); - } - - /** - * <p> - * Sets the gravity of the compass view. Use this to change the corner of the map view that the - * compass is displayed in. - * </p> - * By default, the compass is in the top right corner. - * - * @param gravity One of the values from {@link Gravity}. - * @see Gravity - */ - @UiThread - public void setCompassGravity(int gravity) { - setWidgetGravity(compassView, gravity); - } - - /** - * Enables or disables fading of the compass when facing north. - * <p> - * By default this feature is enabled - * </p> - * - * @param compassFadeFacingNorth True to enable the fading animation; false to disable it - */ - public void setCompassFadeFacingNorth(boolean compassFadeFacingNorth) { - compassView.fadeCompassViewFacingNorth(compassFadeFacingNorth); - } - - /** - * Returns whether the compass performs a fading animation out when facing north. - * - * @return True if the compass will fade, false if it remains visible - */ - public boolean isCompassFadeWhenFacingNorth() { - return compassView.isFadeCompassViewFacingNorth(); - } - - /** - * Returns the gravity value of the CompassView - * - * @return The gravity - */ - public int getCompassGravity() { - return ((FrameLayout.LayoutParams) compassView.getLayoutParams()).gravity; - } - - /** - * Sets the margins of the compass view. Use this to change the distance of the compass from the - * map view edge. - * - * @param left The left margin in pixels. - * @param top The top margin in pixels. - * @param right The right margin in pixels. - * @param bottom The bottom margin in pixels. - */ - @UiThread - public void setCompassMargins(int left, int top, int right, int bottom) { - setWidgetMargins(compassView, left, top, right, bottom); - } - - /** - * Returns the left side margin of CompassView - * - * @return The left margin in pixels - */ - public int getCompassMarginLeft() { - return ((FrameLayout.LayoutParams) compassView.getLayoutParams()).leftMargin; - } - - /** - * Returns the top side margin of CompassView - * - * @return The top margin in pixels - */ - public int getCompassMarginTop() { - return ((FrameLayout.LayoutParams) compassView.getLayoutParams()).topMargin; - } - - /** - * Returns the right side margin of CompassView - * - * @return The right margin in pixels - */ - public int getCompassMarginRight() { - return ((FrameLayout.LayoutParams) compassView.getLayoutParams()).rightMargin; - } - - /** - * Returns the bottom side margin of CompassView - * - * @return The bottom margin in pixels - */ - public int getCompassMarginBottom() { - return ((FrameLayout.LayoutParams) compassView.getLayoutParams()).bottomMargin; - } - - /** - * <p> - * Enables or disables the Mapbox logo. - * </p> - * By default, the compass is enabled. - * - * @param enabled True to enable the logo; false to disable the logo. - */ - public void setLogoEnabled(boolean enabled) { - logoView.setVisibility(enabled ? View.VISIBLE : View.GONE); - } - - /** - * Returns whether the logo is enabled. - * - * @return True if the logo is enabled; false if the logo is disabled. - */ - public boolean isLogoEnabled() { - return logoView.getVisibility() == View.VISIBLE; - } - - /** - * <p> - * Sets the gravity of the logo view. Use this to change the corner of the map view that the - * Mapbox logo is displayed in. - * </p> - * By default, the logo is in the bottom left corner. - * - * @param gravity One of the values from {@link Gravity}. - * @see Gravity - */ - public void setLogoGravity(int gravity) { - setWidgetGravity(logoView, gravity); - } - - /** - * Returns the gravity value of the logo - * - * @return The gravity - */ - public int getLogoGravity() { - return ((FrameLayout.LayoutParams) logoView.getLayoutParams()).gravity; - } - - /** - * Sets the margins of the logo view. Use this to change the distance of the Mapbox logo from the - * map view edge. - * - * @param left The left margin in pixels. - * @param top The top margin in pixels. - * @param right The right margin in pixels. - * @param bottom The bottom margin in pixels. - */ - public void setLogoMargins(int left, int top, int right, int bottom) { - setWidgetMargins(logoView, left, top, right, bottom); - } - - /** - * Returns the left side margin of the logo - * - * @return The left margin in pixels - */ - public int getLogoMarginLeft() { - return ((FrameLayout.LayoutParams) logoView.getLayoutParams()).leftMargin; - } - - /** - * Returns the top side margin of the logo - * - * @return The top margin in pixels - */ - public int getLogoMarginTop() { - return ((FrameLayout.LayoutParams) logoView.getLayoutParams()).topMargin; - } - - /** - * Returns the right side margin of the logo - * - * @return The right margin in pixels - */ - public int getLogoMarginRight() { - return ((FrameLayout.LayoutParams) logoView.getLayoutParams()).rightMargin; - } - - /** - * Returns the bottom side margin of the logo - * - * @return The bottom margin in pixels - */ - public int getLogoMarginBottom() { - return ((FrameLayout.LayoutParams) logoView.getLayoutParams()).bottomMargin; - } - - /** - * <p> - * Enables or disables the attribution. - * </p> - * By default, the attribution is enabled. - * - * @param enabled True to enable the attribution; false to disable the attribution. - */ - public void setAttributionEnabled(boolean enabled) { - attributionsView.setVisibility(enabled ? View.VISIBLE : View.GONE); - } - - /** - * Returns whether the attribution is enabled. - * - * @return True if the attribution is enabled; false if the attribution is disabled. - */ - public boolean isAttributionEnabled() { - return attributionsView.getVisibility() == View.VISIBLE; - } - - /** - * <p> - * Sets the gravity of the attribution. - * </p> - * By default, the attribution is in the bottom left corner next to the Mapbox logo. - * - * @param gravity One of the values from {@link Gravity}. - * @see Gravity - */ - public void setAttributionGravity(int gravity) { - setWidgetGravity(attributionsView, gravity); - } - - /** - * Returns the gravity value of the logo - * - * @return The gravity - */ - public int getAttributionGravity() { - return ((FrameLayout.LayoutParams) attributionsView.getLayoutParams()).gravity; - } - - /** - * Sets the margins of the attribution view. - * - * @param left The left margin in pixels. - * @param top The top margin in pixels. - * @param right The right margin in pixels. - * @param bottom The bottom margin in pixels. - */ - public void setAttributionMargins(int left, int top, int right, int bottom) { - setWidgetMargins(attributionsView, left, top, right, bottom); - } - - /** - * <p> - * Sets the tint of the attribution view. Use this to change the color of the attribution. - * </p> - * By default, the logo is tinted with the primary color of your theme. - * - * @param tintColor Color to tint the attribution. - */ - public void setAttributionTintColor(@ColorInt int tintColor) { - // Check that the tint color being passed in isn't transparent. - if (Color.alpha(tintColor) == 0) { - ColorUtils.setTintList(attributionsView, ContextCompat.getColor(attributionsView.getContext(), R.color.mapbox_blue)); - } else { - ColorUtils.setTintList(attributionsView, tintColor); - } - } - - /** - * Returns the left side margin of the attribution view. - * - * @return The left margin in pixels - */ - public int getAttributionMarginLeft() { - return ((FrameLayout.LayoutParams) attributionsView.getLayoutParams()).leftMargin; - } - - /** - * Returns the top side margin of the attribution view. - * - * @return The top margin in pixels - */ - public int getAttributionMarginTop() { - return ((FrameLayout.LayoutParams) attributionsView.getLayoutParams()).topMargin; - } - - /** - * Returns the right side margin of the attribution view. - * - * @return The right margin in pixels - */ - public int getAttributionMarginRight() { - return ((FrameLayout.LayoutParams) attributionsView.getLayoutParams()).rightMargin; - } - - /** - * Returns the bottom side margin of the logo - * - * @return The bottom margin in pixels - */ - public int getAttributionMarginBottom() { - return ((FrameLayout.LayoutParams) attributionsView.getLayoutParams()).bottomMargin; - } - - /** - * <p> - * Changes whether the user may rotate the map. - * </p> - * <p> - * This setting controls only user interactions with the map. If you set the value to false, - * you may still change the map location programmatically. - * </p> - * The default value is true. - * - * @param rotateGesturesEnabled If true, rotating is enabled. - */ - public void setRotateGesturesEnabled(boolean rotateGesturesEnabled) { - if (rotateGestureChangeAllowed) { - this.rotateGesturesEnabled = rotateGesturesEnabled; - } - } - - /** - * Returns whether the user may rotate the map. - * - * @return If true, rotating is enabled. - */ - public boolean isRotateGesturesEnabled() { - return rotateGesturesEnabled; - } - - void setRotateGestureChangeAllowed(boolean rotateGestureChangeAllowed) { - this.rotateGestureChangeAllowed = rotateGestureChangeAllowed; - } - - boolean isRotateGestureChangeAllowed() { - return rotateGestureChangeAllowed; - } - - /** - * <p> - * Changes whether the user may tilt the map. - * </p> - * <p> - * This setting controls only user interactions with the map. If you set the value to false, - * you may still change the map location programmatically. - * </p> - * The default value is true. - * - * @param tiltGesturesEnabled If true, tilting is enabled. - */ - public void setTiltGesturesEnabled(boolean tiltGesturesEnabled) { - if (tiltGestureChangeAllowed) { - this.tiltGesturesEnabled = tiltGesturesEnabled; - } - } - - /** - * Returns whether the user may tilt the map. - * - * @return If true, tilting is enabled. - */ - public boolean isTiltGesturesEnabled() { - return tiltGesturesEnabled; - } - - void setTiltGestureChangeAllowed(boolean tiltGestureChangeAllowed) { - this.tiltGestureChangeAllowed = tiltGestureChangeAllowed; - } - - boolean isTiltGestureChangeAllowed() { - return tiltGestureChangeAllowed; - } - - /** - * <p> - * Changes whether the user may zoom the map. - * </p> - * <p> - * This setting controls only user interactions with the map. If you set the value to false, - * you may still change the map location programmatically. - * </p> - * The default value is true. - * - * @param zoomGesturesEnabled If true, zooming is enabled. - */ - public void setZoomGesturesEnabled(boolean zoomGesturesEnabled) { - if (zoomGestureChangeAllowed) { - this.zoomGesturesEnabled = zoomGesturesEnabled; - } - } - - /** - * Returns whether the user may zoom the map. - * - * @return If true, zooming is enabled. - */ - public boolean isZoomGesturesEnabled() { - return zoomGesturesEnabled; - } - - void setZoomGestureChangeAllowed(boolean zoomGestureChangeAllowed) { - this.zoomGestureChangeAllowed = zoomGestureChangeAllowed; - } - - boolean isZoomGestureChangeAllowed() { - return zoomGestureChangeAllowed; - } - - /** - * <p> - * Sets whether the zoom controls are enabled. - * If enabled, the zoom controls are a pair of buttons - * (one for zooming in, one for zooming out) that appear on the screen. - * When pressed, they cause the camera to zoom in (or out) by one zoom level. - * If disabled, the zoom controls are not shown. - * </p> - * By default the zoom controls are enabled if the device is only single touch capable; - * - * @param zoomControlsEnabled If true, the zoom controls are enabled. - */ - public void setZoomControlsEnabled(boolean zoomControlsEnabled) { - this.zoomControlsEnabled = zoomControlsEnabled; - } - - /** - * Gets whether the zoom controls are enabled. - * - * @return If true, the zoom controls are enabled. - */ - public boolean isZoomControlsEnabled() { - return zoomControlsEnabled; - } - - /** - * Gets whether the markers are automatically deselected (and therefore, their infowindows - * closed) when a map tap is detected. - * - * @return If true, markers are deselected on a map tap. - */ - public boolean isDeselectMarkersOnTap() { - return deselectMarkersOnTap; - } - - /** - * Sets whether the markers are automatically deselected (and therefore, their infowindows - * closed) when a map tap is detected. - * - * @param deselectMarkersOnTap determines if markers should be deslected on tap - */ - public void setDeselectMarkersOnTap(boolean deselectMarkersOnTap) { - this.deselectMarkersOnTap = deselectMarkersOnTap; - } - - /** - * <p> - * Changes whether the user may scroll around the map. - * </p> - * <p> - * This setting controls only user interactions with the map. If you set the value to false, - * you may still change the map location programmatically. - * </p> - * The default value is true. - * - * @param scrollGesturesEnabled If true, scrolling is enabled. - */ - public void setScrollGesturesEnabled(boolean scrollGesturesEnabled) { - if (scrollGestureChangeAllowed) { - this.scrollGesturesEnabled = scrollGesturesEnabled; - } - } - - /** - * Returns whether the user may scroll around the map. - * - * @return If true, scrolling is enabled. - */ - public boolean isScrollGesturesEnabled() { - return scrollGesturesEnabled; - } - - void setScrollGestureChangeAllowed(boolean scrollGestureChangeAllowed) { - this.scrollGestureChangeAllowed = scrollGestureChangeAllowed; - } - - boolean isScrollGestureChangeAllowed() { - return scrollGestureChangeAllowed; - } - - /** - * <p> - * Sets the preference for whether all gestures should be enabled or disabled. - * </p> - * <p> - * This setting controls only user interactions with the map. If you set the value to false, - * you may still change the map location programmatically. - * </p> - * The default value is true. - * - * @param enabled If true, all gestures are available; otherwise, all gestures are disabled. - * @see #setZoomGesturesEnabled(boolean) ) - * @see #setScrollGesturesEnabled(boolean) - * @see #setRotateGesturesEnabled(boolean) - * @see #setTiltGesturesEnabled(boolean) - */ - public void setAllGesturesEnabled(boolean enabled) { - setScrollGesturesEnabled(enabled); - setRotateGesturesEnabled(enabled); - setTiltGesturesEnabled(enabled); - setZoomGesturesEnabled(enabled); - } - - /** - * Sets the focal point used as center for a gesture - * - * @param focalPoint the focal point to be used. - */ - public void setFocalPoint(@Nullable PointF focalPoint) { - this.userProvidedFocalPoint = focalPoint; - focalPointChangeListener.onFocalPointChanged(focalPoint); - } - - /** - * Returns the gesture focal point - * - * @return The focal point - */ - public PointF getFocalPoint() { - return userProvidedFocalPoint; - } - - /** - * Returns the measured height of the MapView - * - * @return height in pixels - */ - public float getHeight() { - return projection.getHeight(); - } - - /** - * Returns the measured width of the MapView - * - * @return widht in pixels - */ - public float getWidth() { - return projection.getWidth(); - } - - float getPixelRatio() { - return pixelRatio; - } - - /** - * Invalidates the ViewSettings instances shown on top of the MapView - */ - public void invalidate() { - setLogoMargins(getLogoMarginLeft(), getLogoMarginTop(), getLogoMarginRight(), getLogoMarginBottom()); - setCompassMargins(getCompassMarginLeft(), getCompassMarginTop(), getCompassMarginRight(), getCompassMarginBottom()); - setAttributionMargins(getAttributionMarginLeft(), getAttributionMarginTop(), getAttributionMarginRight(), getAttributionMarginBottom()); - } - - private void setWidgetGravity(@NonNull final View view, int gravity) { - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) view.getLayoutParams(); - layoutParams.gravity = gravity; - view.setLayoutParams(layoutParams); - } - - private void setWidgetMargins(@NonNull final View view, int left, int top, int right, int bottom) { - int contentPadding[] = projection.getContentPadding(); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) view.getLayoutParams(); - left += contentPadding[0]; - top += contentPadding[1]; - right += contentPadding[2]; - bottom += contentPadding[3]; - layoutParams.setMargins(left, top, right, bottom); - view.setLayoutParams(layoutParams); - } + private final FocalPointChangeListener focalPointChangeListener; + private final Projection projection; + private final CompassView compassView; + private final ImageView attributionsView; + private final View logoView; + private float pixelRatio; + + private boolean rotateGesturesEnabled = true; + private boolean rotateGestureChangeAllowed = true; + + private boolean tiltGesturesEnabled = true; + private boolean tiltGestureChangeAllowed = true; + + private boolean zoomGesturesEnabled = true; + private boolean zoomGestureChangeAllowed = true; + + private boolean scrollGesturesEnabled = true; + private boolean scrollGestureChangeAllowed = true; + + private boolean zoomControlsEnabled; + + private boolean deselectMarkersOnTap = true; + + private PointF userProvidedFocalPoint; + + UiSettings(@NonNull Projection projection, @NonNull FocalPointChangeListener listener, + @NonNull CompassView compassView, @NonNull ImageView attributionsView, @NonNull View logoView) { + this.projection = projection; + this.focalPointChangeListener = listener; + this.compassView = compassView; + this.attributionsView = attributionsView; + this.logoView = logoView; + if (logoView.getResources() != null) { + this.pixelRatio = logoView.getResources().getDisplayMetrics().density; + } + } + + void initialise(@NonNull Context context, @NonNull MapboxMapOptions options) { + Resources resources = context.getResources(); + initialiseGestures(options); + initialiseCompass(options, resources); + initialiseLogo(options, resources); + initialiseAttribution(context, options); + } + + private void initialiseGestures(MapboxMapOptions options) { + setZoomGesturesEnabled(options.getZoomGesturesEnabled()); + setZoomGestureChangeAllowed(options.getZoomGesturesEnabled()); + setScrollGesturesEnabled(options.getScrollGesturesEnabled()); + setScrollGestureChangeAllowed(options.getScrollGesturesEnabled()); + setRotateGesturesEnabled(options.getRotateGesturesEnabled()); + setRotateGestureChangeAllowed(options.getRotateGesturesEnabled()); + setTiltGesturesEnabled(options.getTiltGesturesEnabled()); + setTiltGestureChangeAllowed(options.getTiltGesturesEnabled()); + setZoomControlsEnabled(options.getZoomControlsEnabled()); + } + + private void initialiseCompass(MapboxMapOptions options, Resources resources) { + setCompassEnabled(options.getCompassEnabled()); + setCompassGravity(options.getCompassGravity()); + int[] compassMargins = options.getCompassMargins(); + if (compassMargins != null) { + setCompassMargins(compassMargins[0], compassMargins[1], compassMargins[2], compassMargins[3]); + } else { + int tenDp = (int) resources.getDimension(R.dimen.mapbox_ten_dp); + setCompassMargins(tenDp, tenDp, tenDp, tenDp); + } + setCompassFadeFacingNorth(options.getCompassFadeFacingNorth()); + } + + private void initialiseLogo(MapboxMapOptions options, Resources resources) { + setLogoEnabled(options.getLogoEnabled()); + setLogoGravity(options.getLogoGravity()); + int[] logoMargins = options.getLogoMargins(); + if (logoMargins != null) { + setLogoMargins(logoMargins[0], logoMargins[1], logoMargins[2], logoMargins[3]); + } else { + int sixteenDp = (int) resources.getDimension(R.dimen.mapbox_sixteen_dp); + setLogoMargins(sixteenDp, sixteenDp, sixteenDp, sixteenDp); + } + } + + private void initialiseAttribution(Context context, MapboxMapOptions options) { + Resources resources = context.getResources(); + setAttributionEnabled(options.getAttributionEnabled()); + setAttributionGravity(options.getAttributionGravity()); + int[] attributionMargins = options.getAttributionMargins(); + if (attributionMargins != null) { + setAttributionMargins(attributionMargins[0], attributionMargins[1], attributionMargins[2], attributionMargins[3]); + } else { + int sevenDp = (int) resources.getDimension(R.dimen.mapbox_seven_dp); + int seventySixDp = (int) resources.getDimension(R.dimen.mapbox_seventy_six_dp); + setAttributionMargins(seventySixDp, sevenDp, sevenDp, sevenDp); + } + + int attributionTintColor = options.getAttributionTintColor(); + setAttributionTintColor(attributionTintColor != -1 + ? attributionTintColor : ColorUtils.getPrimaryColor(context)); + } + + /** + * <p> + * Enables or disables the compass. The compass is an icon on the map that indicates the + * direction of north on the map. When a user clicks + * the compass, the camera orients itself to its default orientation and fades away shortly + * after. If disabled, the compass will never be displayed. + * </p> + * By default, the compass is enabled. + * + * @param compassEnabled True to enable the compass; false to disable the compass. + */ + public void setCompassEnabled(boolean compassEnabled) { + compassView.setEnabled(compassEnabled); + } + + /** + * Returns whether the compass is enabled. + * + * @return True if the compass is enabled; false if the compass is disabled. + */ + public boolean isCompassEnabled() { + return compassView.isEnabled(); + } + + /** + * <p> + * Sets the gravity of the compass view. Use this to change the corner of the map view that the + * compass is displayed in. + * </p> + * By default, the compass is in the top right corner. + * + * @param gravity One of the values from {@link Gravity}. + * @see Gravity + */ + @UiThread + public void setCompassGravity(int gravity) { + setWidgetGravity(compassView, gravity); + } + + /** + * Enables or disables fading of the compass when facing north. + * <p> + * By default this feature is enabled + * </p> + * + * @param compassFadeFacingNorth True to enable the fading animation; false to disable it + */ + public void setCompassFadeFacingNorth(boolean compassFadeFacingNorth) { + compassView.fadeCompassViewFacingNorth(compassFadeFacingNorth); + } + + /** + * Returns whether the compass performs a fading animation out when facing north. + * + * @return True if the compass will fade, false if it remains visible + */ + public boolean isCompassFadeWhenFacingNorth() { + return compassView.isFadeCompassViewFacingNorth(); + } + + /** + * Returns the gravity value of the CompassView + * + * @return The gravity + */ + public int getCompassGravity() { + return ((FrameLayout.LayoutParams) compassView.getLayoutParams()).gravity; + } + + /** + * Sets the margins of the compass view. Use this to change the distance of the compass from the + * map view edge. + * + * @param left The left margin in pixels. + * @param top The top margin in pixels. + * @param right The right margin in pixels. + * @param bottom The bottom margin in pixels. + */ + @UiThread + public void setCompassMargins(int left, int top, int right, int bottom) { + setWidgetMargins(compassView, left, top, right, bottom); + } + + /** + * Returns the left side margin of CompassView + * + * @return The left margin in pixels + */ + public int getCompassMarginLeft() { + return ((FrameLayout.LayoutParams) compassView.getLayoutParams()).leftMargin; + } + + /** + * Returns the top side margin of CompassView + * + * @return The top margin in pixels + */ + public int getCompassMarginTop() { + return ((FrameLayout.LayoutParams) compassView.getLayoutParams()).topMargin; + } + + /** + * Returns the right side margin of CompassView + * + * @return The right margin in pixels + */ + public int getCompassMarginRight() { + return ((FrameLayout.LayoutParams) compassView.getLayoutParams()).rightMargin; + } + + /** + * Returns the bottom side margin of CompassView + * + * @return The bottom margin in pixels + */ + public int getCompassMarginBottom() { + return ((FrameLayout.LayoutParams) compassView.getLayoutParams()).bottomMargin; + } + + /** + * <p> + * Enables or disables the Mapbox logo. + * </p> + * By default, the compass is enabled. + * + * @param enabled True to enable the logo; false to disable the logo. + */ + public void setLogoEnabled(boolean enabled) { + logoView.setVisibility(enabled ? View.VISIBLE : View.GONE); + } + + /** + * Returns whether the logo is enabled. + * + * @return True if the logo is enabled; false if the logo is disabled. + */ + public boolean isLogoEnabled() { + return logoView.getVisibility() == View.VISIBLE; + } + + /** + * <p> + * Sets the gravity of the logo view. Use this to change the corner of the map view that the + * Mapbox logo is displayed in. + * </p> + * By default, the logo is in the bottom left corner. + * + * @param gravity One of the values from {@link Gravity}. + * @see Gravity + */ + public void setLogoGravity(int gravity) { + setWidgetGravity(logoView, gravity); + } + + /** + * Returns the gravity value of the logo + * + * @return The gravity + */ + public int getLogoGravity() { + return ((FrameLayout.LayoutParams) logoView.getLayoutParams()).gravity; + } + + /** + * Sets the margins of the logo view. Use this to change the distance of the Mapbox logo from the + * map view edge. + * + * @param left The left margin in pixels. + * @param top The top margin in pixels. + * @param right The right margin in pixels. + * @param bottom The bottom margin in pixels. + */ + public void setLogoMargins(int left, int top, int right, int bottom) { + setWidgetMargins(logoView, left, top, right, bottom); + } + + /** + * Returns the left side margin of the logo + * + * @return The left margin in pixels + */ + public int getLogoMarginLeft() { + return ((FrameLayout.LayoutParams) logoView.getLayoutParams()).leftMargin; + } + + /** + * Returns the top side margin of the logo + * + * @return The top margin in pixels + */ + public int getLogoMarginTop() { + return ((FrameLayout.LayoutParams) logoView.getLayoutParams()).topMargin; + } + + /** + * Returns the right side margin of the logo + * + * @return The right margin in pixels + */ + public int getLogoMarginRight() { + return ((FrameLayout.LayoutParams) logoView.getLayoutParams()).rightMargin; + } + + /** + * Returns the bottom side margin of the logo + * + * @return The bottom margin in pixels + */ + public int getLogoMarginBottom() { + return ((FrameLayout.LayoutParams) logoView.getLayoutParams()).bottomMargin; + } + + /** + * <p> + * Enables or disables the attribution. + * </p> + * By default, the attribution is enabled. + * + * @param enabled True to enable the attribution; false to disable the attribution. + */ + public void setAttributionEnabled(boolean enabled) { + attributionsView.setVisibility(enabled ? View.VISIBLE : View.GONE); + } + + /** + * Returns whether the attribution is enabled. + * + * @return True if the attribution is enabled; false if the attribution is disabled. + */ + public boolean isAttributionEnabled() { + return attributionsView.getVisibility() == View.VISIBLE; + } + + /** + * <p> + * Sets the gravity of the attribution. + * </p> + * By default, the attribution is in the bottom left corner next to the Mapbox logo. + * + * @param gravity One of the values from {@link Gravity}. + * @see Gravity + */ + public void setAttributionGravity(int gravity) { + setWidgetGravity(attributionsView, gravity); + } + + /** + * Returns the gravity value of the logo + * + * @return The gravity + */ + public int getAttributionGravity() { + return ((FrameLayout.LayoutParams) attributionsView.getLayoutParams()).gravity; + } + + /** + * Sets the margins of the attribution view. + * + * @param left The left margin in pixels. + * @param top The top margin in pixels. + * @param right The right margin in pixels. + * @param bottom The bottom margin in pixels. + */ + public void setAttributionMargins(int left, int top, int right, int bottom) { + setWidgetMargins(attributionsView, left, top, right, bottom); + } + + /** + * <p> + * Sets the tint of the attribution view. Use this to change the color of the attribution. + * </p> + * By default, the logo is tinted with the primary color of your theme. + * + * @param tintColor Color to tint the attribution. + */ + public void setAttributionTintColor(@ColorInt int tintColor) { + // Check that the tint color being passed in isn't transparent. + if (Color.alpha(tintColor) == 0) { + ColorUtils.setTintList(attributionsView, ContextCompat.getColor(attributionsView.getContext(), + R.color.mapbox_blue)); + } else { + ColorUtils.setTintList(attributionsView, tintColor); + } + } + + /** + * Returns the left side margin of the attribution view. + * + * @return The left margin in pixels + */ + public int getAttributionMarginLeft() { + return ((FrameLayout.LayoutParams) attributionsView.getLayoutParams()).leftMargin; + } + + /** + * Returns the top side margin of the attribution view. + * + * @return The top margin in pixels + */ + public int getAttributionMarginTop() { + return ((FrameLayout.LayoutParams) attributionsView.getLayoutParams()).topMargin; + } + + /** + * Returns the right side margin of the attribution view. + * + * @return The right margin in pixels + */ + public int getAttributionMarginRight() { + return ((FrameLayout.LayoutParams) attributionsView.getLayoutParams()).rightMargin; + } + + /** + * Returns the bottom side margin of the logo + * + * @return The bottom margin in pixels + */ + public int getAttributionMarginBottom() { + return ((FrameLayout.LayoutParams) attributionsView.getLayoutParams()).bottomMargin; + } + + /** + * <p> + * Changes whether the user may rotate the map. + * </p> + * <p> + * This setting controls only user interactions with the map. If you set the value to false, + * you may still change the map location programmatically. + * </p> + * The default value is true. + * + * @param rotateGesturesEnabled If true, rotating is enabled. + */ + public void setRotateGesturesEnabled(boolean rotateGesturesEnabled) { + if (rotateGestureChangeAllowed) { + this.rotateGesturesEnabled = rotateGesturesEnabled; + } + } + + /** + * Returns whether the user may rotate the map. + * + * @return If true, rotating is enabled. + */ + public boolean isRotateGesturesEnabled() { + return rotateGesturesEnabled; + } + + void setRotateGestureChangeAllowed(boolean rotateGestureChangeAllowed) { + this.rotateGestureChangeAllowed = rotateGestureChangeAllowed; + } + + boolean isRotateGestureChangeAllowed() { + return rotateGestureChangeAllowed; + } + + /** + * <p> + * Changes whether the user may tilt the map. + * </p> + * <p> + * This setting controls only user interactions with the map. If you set the value to false, + * you may still change the map location programmatically. + * </p> + * The default value is true. + * + * @param tiltGesturesEnabled If true, tilting is enabled. + */ + public void setTiltGesturesEnabled(boolean tiltGesturesEnabled) { + if (tiltGestureChangeAllowed) { + this.tiltGesturesEnabled = tiltGesturesEnabled; + } + } + + /** + * Returns whether the user may tilt the map. + * + * @return If true, tilting is enabled. + */ + public boolean isTiltGesturesEnabled() { + return tiltGesturesEnabled; + } + + void setTiltGestureChangeAllowed(boolean tiltGestureChangeAllowed) { + this.tiltGestureChangeAllowed = tiltGestureChangeAllowed; + } + + boolean isTiltGestureChangeAllowed() { + return tiltGestureChangeAllowed; + } + + /** + * <p> + * Changes whether the user may zoom the map. + * </p> + * <p> + * This setting controls only user interactions with the map. If you set the value to false, + * you may still change the map location programmatically. + * </p> + * The default value is true. + * + * @param zoomGesturesEnabled If true, zooming is enabled. + */ + public void setZoomGesturesEnabled(boolean zoomGesturesEnabled) { + if (zoomGestureChangeAllowed) { + this.zoomGesturesEnabled = zoomGesturesEnabled; + } + } + + /** + * Returns whether the user may zoom the map. + * + * @return If true, zooming is enabled. + */ + public boolean isZoomGesturesEnabled() { + return zoomGesturesEnabled; + } + + void setZoomGestureChangeAllowed(boolean zoomGestureChangeAllowed) { + this.zoomGestureChangeAllowed = zoomGestureChangeAllowed; + } + + boolean isZoomGestureChangeAllowed() { + return zoomGestureChangeAllowed; + } + + /** + * <p> + * Sets whether the zoom controls are enabled. + * If enabled, the zoom controls are a pair of buttons + * (one for zooming in, one for zooming out) that appear on the screen. + * When pressed, they cause the camera to zoom in (or out) by one zoom level. + * If disabled, the zoom controls are not shown. + * </p> + * By default the zoom controls are enabled if the device is only single touch capable; + * + * @param zoomControlsEnabled If true, the zoom controls are enabled. + */ + public void setZoomControlsEnabled(boolean zoomControlsEnabled) { + this.zoomControlsEnabled = zoomControlsEnabled; + } + + /** + * Gets whether the zoom controls are enabled. + * + * @return If true, the zoom controls are enabled. + */ + public boolean isZoomControlsEnabled() { + return zoomControlsEnabled; + } + + /** + * Gets whether the markers are automatically deselected (and therefore, their infowindows + * closed) when a map tap is detected. + * + * @return If true, markers are deselected on a map tap. + */ + public boolean isDeselectMarkersOnTap() { + return deselectMarkersOnTap; + } + + /** + * Sets whether the markers are automatically deselected (and therefore, their infowindows + * closed) when a map tap is detected. + * + * @param deselectMarkersOnTap determines if markers should be deslected on tap + */ + public void setDeselectMarkersOnTap(boolean deselectMarkersOnTap) { + this.deselectMarkersOnTap = deselectMarkersOnTap; + } + + /** + * <p> + * Changes whether the user may scroll around the map. + * </p> + * <p> + * This setting controls only user interactions with the map. If you set the value to false, + * you may still change the map location programmatically. + * </p> + * The default value is true. + * + * @param scrollGesturesEnabled If true, scrolling is enabled. + */ + public void setScrollGesturesEnabled(boolean scrollGesturesEnabled) { + if (scrollGestureChangeAllowed) { + this.scrollGesturesEnabled = scrollGesturesEnabled; + } + } + + /** + * Returns whether the user may scroll around the map. + * + * @return If true, scrolling is enabled. + */ + public boolean isScrollGesturesEnabled() { + return scrollGesturesEnabled; + } + + void setScrollGestureChangeAllowed(boolean scrollGestureChangeAllowed) { + this.scrollGestureChangeAllowed = scrollGestureChangeAllowed; + } + + boolean isScrollGestureChangeAllowed() { + return scrollGestureChangeAllowed; + } + + /** + * <p> + * Sets the preference for whether all gestures should be enabled or disabled. + * </p> + * <p> + * This setting controls only user interactions with the map. If you set the value to false, + * you may still change the map location programmatically. + * </p> + * The default value is true. + * + * @param enabled If true, all gestures are available; otherwise, all gestures are disabled. + * @see #setZoomGesturesEnabled(boolean) ) + * @see #setScrollGesturesEnabled(boolean) + * @see #setRotateGesturesEnabled(boolean) + * @see #setTiltGesturesEnabled(boolean) + */ + public void setAllGesturesEnabled(boolean enabled) { + setScrollGesturesEnabled(enabled); + setRotateGesturesEnabled(enabled); + setTiltGesturesEnabled(enabled); + setZoomGesturesEnabled(enabled); + } + + /** + * Sets the focal point used as center for a gesture + * + * @param focalPoint the focal point to be used. + */ + public void setFocalPoint(@Nullable PointF focalPoint) { + this.userProvidedFocalPoint = focalPoint; + focalPointChangeListener.onFocalPointChanged(focalPoint); + } + + /** + * Returns the gesture focal point + * + * @return The focal point + */ + public PointF getFocalPoint() { + return userProvidedFocalPoint; + } + + /** + * Returns the measured height of the MapView + * + * @return height in pixels + */ + public float getHeight() { + return projection.getHeight(); + } + + /** + * Returns the measured width of the MapView + * + * @return widht in pixels + */ + public float getWidth() { + return projection.getWidth(); + } + + float getPixelRatio() { + return pixelRatio; + } + + /** + * Invalidates the ViewSettings instances shown on top of the MapView + */ + public void invalidate() { + setLogoMargins(getLogoMarginLeft(), getLogoMarginTop(), getLogoMarginRight(), getLogoMarginBottom()); + setCompassMargins(getCompassMarginLeft(), getCompassMarginTop(), getCompassMarginRight(), getCompassMarginBottom()); + setAttributionMargins(getAttributionMarginLeft(), getAttributionMarginTop(), getAttributionMarginRight(), + getAttributionMarginBottom()); + } + + private void setWidgetGravity(@NonNull final View view, int gravity) { + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) view.getLayoutParams(); + layoutParams.gravity = gravity; + view.setLayoutParams(layoutParams); + } + + private void setWidgetMargins(@NonNull final View view, int left, int top, int right, int bottom) { + int[] contentPadding = projection.getContentPadding(); + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) view.getLayoutParams(); + left += contentPadding[0]; + top += contentPadding[1]; + right += contentPadding[2]; + bottom += contentPadding[3]; + layoutParams.setMargins(left, top, right, bottom); + view.setLayoutParams(layoutParams); + } } 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 4d9a60ed66..dc4a21f2fe 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 @@ -27,140 +27,140 @@ import java.lang.ref.WeakReference; */ public final class CompassView extends ImageView implements Runnable { - private static final long TIME_WAIT_IDLE = 500; - private static final long TIME_FADE_ANIMATION = TIME_WAIT_IDLE; - private static final long TIME_MAP_NORTH_ANIMATION = 150; - - private double direction = 0.0; - private boolean fadeCompassViewFacingNorth = true; - private ViewPropertyAnimatorCompat fadeAnimator; - - public CompassView(Context context) { - super(context); - initialize(context); + private static final long TIME_WAIT_IDLE = 500; + private static final long TIME_FADE_ANIMATION = TIME_WAIT_IDLE; + private static final long TIME_MAP_NORTH_ANIMATION = 150; + + private double direction = 0.0; + private boolean fadeCompassViewFacingNorth = true; + private ViewPropertyAnimatorCompat fadeAnimator; + + public CompassView(Context context) { + super(context); + initialize(context); + } + + public CompassView(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(context); + } + + public CompassView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initialize(context); + } + + private void initialize(Context context) { + setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.mapbox_compass_icon)); + setContentDescription(getResources().getString(R.string.mapbox_compassContentDescription)); + setEnabled(false); + + // Layout params + float screenDensity = context.getResources().getDisplayMetrics().density; + ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams((int) (48 * screenDensity), (int) (48 * screenDensity)); + setLayoutParams(lp); + } + + // TODO refactor MapboxMap and replace with interface + public void setMapboxMap(@NonNull MapboxMap mapboxMap) { + setOnClickListener(new CompassClickListener(mapboxMap, this)); + } + + private void resetAnimation() { + if (fadeAnimator != null) { + fadeAnimator.cancel(); } - - public CompassView(Context context, AttributeSet attrs) { - super(context, attrs); - initialize(context); + fadeAnimator = null; + } + + public boolean isHidden() { + return fadeCompassViewFacingNorth && isFacingNorth(); + } + + public boolean isFacingNorth() { + // increase range more than just 0.0 + return direction >= 359.0 || direction <= 1.0; + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + if (enabled && !isHidden()) { + resetAnimation(); + setAlpha(1.0f); + setVisibility(View.VISIBLE); + } else { + resetAnimation(); + setAlpha(0.0f); + setVisibility(View.INVISIBLE); } + } - public CompassView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - initialize(context); - } + public void update(final double direction) { + this.direction = direction; - private void initialize(Context context) { - setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.mapbox_compass_icon)); - setContentDescription(getResources().getString(R.string.mapbox_compassContentDescription)); - setEnabled(false); - - // Layout params - float screenDensity = context.getResources().getDisplayMetrics().density; - ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams((int) (48 * screenDensity), (int) (48 * screenDensity)); - setLayoutParams(lp); + if (!isEnabled()) { + return; } - // TODO refactor MapboxMap and replace with interface - public void setMapboxMap(@NonNull MapboxMap mapboxMap) { - setOnClickListener(new CompassClickListener(mapboxMap, this)); + if (isHidden()) { + if (getVisibility() == View.INVISIBLE || fadeAnimator != null) { + return; + } + postDelayed(this, TIME_WAIT_IDLE); + return; + } else { + resetAnimation(); + setAlpha(1.0f); + setVisibility(View.VISIBLE); } - private void resetAnimation() { - if (fadeAnimator != null) { - fadeAnimator.cancel(); - } - fadeAnimator = null; - } + setRotation((float) direction); + } - public boolean isHidden() { - return fadeCompassViewFacingNorth && isFacingNorth(); - } + public void fadeCompassViewFacingNorth(boolean compassFadeFacingNorth) { + fadeCompassViewFacingNorth = compassFadeFacingNorth; + } - public boolean isFacingNorth() { - // increase range more than just 0.0 - return direction >= 359.0 || direction <= 1.0; - } + public boolean isFadeCompassViewFacingNorth() { + return fadeCompassViewFacingNorth; + } - @Override - public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - if (enabled && !isHidden()) { - resetAnimation(); - setAlpha(1.0f); - setVisibility(View.VISIBLE); - } else { - resetAnimation(); - setAlpha(0.0f); - setVisibility(View.INVISIBLE); + @Override + public void run() { + if (isFacingNorth() && fadeCompassViewFacingNorth) { + resetAnimation(); + setLayerType(View.LAYER_TYPE_HARDWARE, null); + fadeAnimator = ViewCompat.animate(CompassView.this).alpha(0.0f).setDuration(TIME_FADE_ANIMATION); + fadeAnimator.setListener(new ViewPropertyAnimatorListenerAdapter() { + @Override + public void onAnimationEnd(View view) { + setLayerType(LAYER_TYPE_NONE, null); + setVisibility(View.INVISIBLE); + resetAnimation(); } + }); } + } - public void update(final double direction) { - this.direction = direction; + static class CompassClickListener implements View.OnClickListener { - if (!isEnabled()) { - return; - } - - if (isHidden()) { - if (getVisibility() == View.INVISIBLE || fadeAnimator != null) { - return; - } - postDelayed(this, TIME_WAIT_IDLE); - return; - } else { - resetAnimation(); - setAlpha(1.0f); - setVisibility(View.VISIBLE); - } + private WeakReference<MapboxMap> mapboxMap; + private WeakReference<CompassView> compassView; - setRotation((float) direction); - } - - public void fadeCompassViewFacingNorth(boolean compassFadeFacingNorth) { - fadeCompassViewFacingNorth = compassFadeFacingNorth; - } - - public boolean isFadeCompassViewFacingNorth(){ - return fadeCompassViewFacingNorth; + CompassClickListener(final MapboxMap mapboxMap, CompassView compassView) { + this.mapboxMap = new WeakReference<>(mapboxMap); + this.compassView = new WeakReference<>(compassView); } @Override - public void run() { - if (isFacingNorth() && fadeCompassViewFacingNorth) { - resetAnimation(); - setLayerType(View.LAYER_TYPE_HARDWARE, null); - fadeAnimator = ViewCompat.animate(CompassView.this).alpha(0.0f).setDuration(TIME_FADE_ANIMATION); - fadeAnimator.setListener(new ViewPropertyAnimatorListenerAdapter() { - @Override - public void onAnimationEnd(View view) { - setLayerType(LAYER_TYPE_NONE, null); - setVisibility(View.INVISIBLE); - resetAnimation(); - } - }); - } - } - - 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) { - mapboxMap.resetNorth(); - compassView.postDelayed(compassView, TIME_WAIT_IDLE + TIME_MAP_NORTH_ANIMATION); - } - } + public void onClick(View view) { + final MapboxMap mapboxMap = this.mapboxMap.get(); + final CompassView compassView = this.compassView.get(); + if (mapboxMap != null && compassView != null) { + mapboxMap.resetNorth(); + compassView.postDelayed(compassView, TIME_WAIT_IDLE + TIME_MAP_NORTH_ANIMATION); + } } + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java index 677b59bea8..d34b0f693d 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 @@ -4,7 +4,6 @@ import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Camera; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PointF; @@ -44,745 +43,753 @@ import java.lang.ref.WeakReference; */ public class MyLocationView extends View { - private MyLocationBehavior myLocationBehavior; - private MapboxMap mapboxMap; + private MyLocationBehavior myLocationBehavior; + private MapboxMap mapboxMap; - private Projection projection; - private float[] projectedCoordinate = new float[2]; - private float projectedX; - private float projectedY; + private Projection projection; + private float[] projectedCoordinate = new float[2]; + private float projectedX; + private float projectedY; - private float contentPaddingX; - private float contentPaddingY; + private float contentPaddingX; + private float contentPaddingY; - private LatLng latLng; - private Location location; - private long locationUpdateTimestamp; - private float previousDirection; + private LatLng latLng; + private Location location; + private long locationUpdateTimestamp; + private float previousDirection; - private float accuracy; - private Paint accuracyPaint; + private float accuracy; + private Paint accuracyPaint; - private ValueAnimator locationChangeAnimator; - private ValueAnimator accuracyAnimator; - private ValueAnimator directionAnimator; + private ValueAnimator locationChangeAnimator; + private ValueAnimator accuracyAnimator; + private ValueAnimator directionAnimator; - private ValueAnimator.AnimatorUpdateListener invalidateSelfOnUpdateListener = - new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - invalidate(); - } - }; - - private Drawable foregroundDrawable; - private Drawable foregroundBearingDrawable; - private Drawable backgroundDrawable; - - private Rect foregroundBounds; - private Rect backgroundBounds; - - private int backgroundOffsetLeft; - private int backgroundOffsetTop; - private int backgroundOffsetRight; - private int backgroundOffsetBottom; - - private Matrix matrix; - private Camera camera; - private PointF screenLocation; + private ValueAnimator.AnimatorUpdateListener invalidateSelfOnUpdateListener = + new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + invalidate(); + } + }; - // camera vars - private double tilt; - private double bearing; - private float magneticHeading; + private Drawable foregroundDrawable; + private Drawable foregroundBearingDrawable; + private Drawable backgroundDrawable; - // Controls the compass update rate in milliseconds - private static final int COMPASS_UPDATE_RATE_MS = 500; + private Rect foregroundBounds; + private Rect backgroundBounds; - @MyLocationTracking.Mode - private int myLocationTrackingMode; + private int backgroundOffsetLeft; + private int backgroundOffsetTop; + private int backgroundOffsetRight; + private int backgroundOffsetBottom; - @MyBearingTracking.Mode - private int myBearingTrackingMode; + private Matrix matrix; + private Camera camera; + private PointF screenLocation; - private GpsLocationListener userLocationListener; - private CompassListener compassListener; + // camera vars + private double tilt; + private double bearing; + private float magneticHeading; - public MyLocationView(Context context) { - super(context); - init(context); - } + // Controls the compass update rate in milliseconds + private static final int COMPASS_UPDATE_RATE_MS = 500; - public MyLocationView(Context context, AttributeSet attrs) { - super(context, attrs); - init(context); - } + @MyLocationTracking.Mode + private int myLocationTrackingMode; - public MyLocationView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(context); - } + @MyBearingTracking.Mode + private int myBearingTrackingMode; - private void init(Context context) { - if (isInEditMode()) { - return; - } + private GpsLocationListener userLocationListener; + private CompassListener compassListener; - setEnabled(false); + public MyLocationView(Context context) { + super(context); + init(context); + } - // setup LayoutParams - ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); - setLayoutParams(lp); + public MyLocationView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } - matrix = new Matrix(); - camera = new Camera(); - camera.setLocation(0, 0, -1000); - accuracyPaint = new Paint(); + public MyLocationView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context); + } - myLocationBehavior = new MyLocationBehaviorFactory().getBehavioralModel(MyLocationTracking.TRACKING_NONE); - compassListener = new CompassListener(context); + private void init(Context context) { + if (isInEditMode()) { + return; } - public final void setForegroundDrawables(Drawable defaultDrawable, Drawable bearingDrawable) { - if (defaultDrawable == null) { - return; - } + setEnabled(false); - if (bearingDrawable == null) { - // if user only provided one resource - // use same for bearing mode - bearingDrawable = defaultDrawable.getConstantState().newDrawable(); - } + // setup LayoutParams + ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + setLayoutParams(lp); - if (backgroundDrawable == null) { - // if the user didn't provide a background resource we will use the foreground resource instead, - // we need to create a new drawable to handle tinting correctly - backgroundDrawable = defaultDrawable.getConstantState().newDrawable(); - } + matrix = new Matrix(); + camera = new Camera(); + camera.setLocation(0, 0, -1000); + accuracyPaint = new Paint(); - if (defaultDrawable.getIntrinsicWidth() != bearingDrawable.getIntrinsicWidth() || defaultDrawable.getIntrinsicHeight() != bearingDrawable.getIntrinsicHeight()) { - throw new RuntimeException("The dimensions from location and bearing drawables should be match"); - } + myLocationBehavior = new MyLocationBehaviorFactory().getBehavioralModel(MyLocationTracking.TRACKING_NONE); + compassListener = new CompassListener(context); + } - foregroundDrawable = defaultDrawable; - foregroundBearingDrawable = bearingDrawable; + public final void setForegroundDrawables(Drawable defaultDrawable, Drawable bearingDrawable) { + if (defaultDrawable == null) { + return; + } - invalidateBounds(); + if (bearingDrawable == null) { + // if user only provided one resource + // use same for bearing mode + bearingDrawable = defaultDrawable.getConstantState().newDrawable(); } - public final void setForegroundDrawableTint(@ColorInt int color) { - if (foregroundDrawable != null) { - foregroundDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN); - } - if (foregroundBearingDrawable != null) { - foregroundBearingDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN); - } - invalidate(); + if (backgroundDrawable == null) { + // if the user didn't provide a background resource we will use the foreground resource instead, + // we need to create a new drawable to handle tinting correctly + backgroundDrawable = defaultDrawable.getConstantState().newDrawable(); } - public final void setShadowDrawable(Drawable drawable) { - setShadowDrawable(drawable, 0, 0, 0, 0); + if (defaultDrawable.getIntrinsicWidth() != bearingDrawable.getIntrinsicWidth() + || defaultDrawable.getIntrinsicHeight() != bearingDrawable.getIntrinsicHeight()) { + throw new RuntimeException("The dimensions from location and bearing drawables should be match"); } - public final void setShadowDrawable(Drawable drawable, int left, int top, int right, int bottom) { - if (drawable != null) { - backgroundDrawable = drawable; - } + foregroundDrawable = defaultDrawable; + foregroundBearingDrawable = bearingDrawable; - backgroundOffsetLeft = left; - backgroundOffsetTop = top; - backgroundOffsetRight = right; - backgroundOffsetBottom = bottom; + invalidateBounds(); + } - invalidateBounds(); + public final void setForegroundDrawableTint(@ColorInt int color) { + if (foregroundDrawable != null) { + foregroundDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN); } - - public final void setShadowDrawableTint(@ColorInt int color) { - if (backgroundDrawable == null) { - return; - } - backgroundDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN); - invalidate(); + if (foregroundBearingDrawable != null) { + foregroundBearingDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN); } + invalidate(); + } - public final void setAccuracyTint(@ColorInt int color) { - int alpha = accuracyPaint.getAlpha(); - accuracyPaint.setColor(color); - accuracyPaint.setAlpha(alpha); - invalidate(); - } + public final void setShadowDrawable(Drawable drawable) { + setShadowDrawable(drawable, 0, 0, 0, 0); + } - public final void setAccuracyAlpha(@IntRange(from = 0, to = 255) int alpha) { - accuracyPaint.setAlpha(alpha); - invalidate(); + public final void setShadowDrawable(Drawable drawable, int left, int top, int right, int bottom) { + if (drawable != null) { + backgroundDrawable = drawable; } - private void invalidateBounds() { - if (backgroundDrawable == null || foregroundDrawable == null || foregroundBearingDrawable == null) { - return; - } - - int backgroundWidth = backgroundDrawable.getIntrinsicWidth(); - int backgroundHeight = backgroundDrawable.getIntrinsicHeight(); - int horizontalOffset = backgroundOffsetLeft - backgroundOffsetRight; - int verticalOffset = backgroundOffsetTop - backgroundOffsetBottom; - backgroundBounds = new Rect(-backgroundWidth / 2 + horizontalOffset, -backgroundHeight / 2 + verticalOffset, backgroundWidth / 2 + horizontalOffset, backgroundHeight / 2 + verticalOffset); - backgroundDrawable.setBounds(backgroundBounds); + backgroundOffsetLeft = left; + backgroundOffsetTop = top; + backgroundOffsetRight = right; + backgroundOffsetBottom = bottom; - int foregroundWidth = foregroundDrawable.getIntrinsicWidth(); - int foregroundHeight = foregroundDrawable.getIntrinsicHeight(); - foregroundBounds = new Rect(-foregroundWidth / 2, -foregroundHeight / 2, foregroundWidth / 2, foregroundHeight / 2); - foregroundDrawable.setBounds(foregroundBounds); - foregroundBearingDrawable.setBounds(foregroundBounds); + invalidateBounds(); + } - // invoke a new draw - invalidate(); + public final void setShadowDrawableTint(@ColorInt int color) { + if (backgroundDrawable == null) { + return; } + backgroundDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN); + invalidate(); + } - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - if (location == null || foregroundBounds == null || backgroundBounds == null || accuracyAnimator == null || screenLocation == null) { - // Not ready yet - return; - } - - final PointF pointF = screenLocation; - float metersPerPixel = (float) projection.getMetersPerPixelAtLatitude(location.getLatitude()); - float accuracyPixels = (Float) accuracyAnimator.getAnimatedValue() / metersPerPixel / 2; - float maxRadius = getWidth() / 2; - accuracyPixels = accuracyPixels <= maxRadius ? accuracyPixels : maxRadius; - - // reset - matrix.reset(); - projectedCoordinate[0] = 0; - projectedCoordinate[1] = 0; + public final void setAccuracyTint(@ColorInt int color) { + int alpha = accuracyPaint.getAlpha(); + accuracyPaint.setColor(color); + accuracyPaint.setAlpha(alpha); + invalidate(); + } - // put camera in position - camera.save(); - camera.rotate((float) tilt, 0, 0); - camera.getMatrix(matrix); + public final void setAccuracyAlpha(@IntRange(from = 0, to = 255) int alpha) { + accuracyPaint.setAlpha(alpha); + invalidate(); + } - if (myBearingTrackingMode != MyBearingTracking.NONE && directionAnimator != null) { - matrix.preRotate((Float) directionAnimator.getAnimatedValue()); - } - - matrix.preTranslate(0, contentPaddingY); - matrix.postTranslate(pointF.x, pointF.y - contentPaddingY); - - // concat our matrix on canvas - canvas.concat(matrix); + private void invalidateBounds() { + if (backgroundDrawable == null || foregroundDrawable == null || foregroundBearingDrawable == null) { + return; + } - // calculate focal point - matrix.mapPoints(projectedCoordinate); - projectedX = pointF.x - projectedCoordinate[0]; - projectedY = pointF.y - projectedCoordinate[1]; + int backgroundWidth = backgroundDrawable.getIntrinsicWidth(); + int backgroundHeight = backgroundDrawable.getIntrinsicHeight(); + int horizontalOffset = backgroundOffsetLeft - backgroundOffsetRight; + int verticalOffset = backgroundOffsetTop - backgroundOffsetBottom; + backgroundBounds = new Rect(-backgroundWidth / 2 + horizontalOffset, + -backgroundHeight / 2 + verticalOffset, backgroundWidth / 2 + horizontalOffset, backgroundHeight / 2 + + verticalOffset); + backgroundDrawable.setBounds(backgroundBounds); - // restore orientation from camera - camera.restore(); + int foregroundWidth = foregroundDrawable.getIntrinsicWidth(); + int foregroundHeight = foregroundDrawable.getIntrinsicHeight(); + foregroundBounds = new Rect(-foregroundWidth / 2, -foregroundHeight / 2, foregroundWidth / 2, foregroundHeight / 2); + foregroundDrawable.setBounds(foregroundBounds); + foregroundBearingDrawable.setBounds(foregroundBounds); - // draw circle - canvas.drawCircle(0, 0, accuracyPixels, accuracyPaint); + // invoke a new draw + invalidate(); + } - // draw shadow - if (backgroundDrawable != null) { - backgroundDrawable.draw(canvas); - } + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); - // draw foreground - if (myBearingTrackingMode == MyBearingTracking.NONE) { - if (foregroundDrawable != null) { - foregroundDrawable.draw(canvas); - } - } else if (foregroundBearingDrawable != null && foregroundBounds != null) { - foregroundBearingDrawable.draw(canvas); - } + if (location == null || foregroundBounds == null || backgroundBounds == null || accuracyAnimator == null + || screenLocation == null) { + // Not ready yet + return; } - public void setTilt(@FloatRange(from = 0, to = 60.0f) double tilt) { - this.tilt = tilt; - if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { - mapboxMap.getUiSettings().setFocalPoint(new PointF(getCenterX(), getCenterY())); - } - } + final PointF pointF = screenLocation; + float metersPerPixel = (float) projection.getMetersPerPixelAtLatitude(location.getLatitude()); + float accuracyPixels = (Float) accuracyAnimator.getAnimatedValue() / metersPerPixel / 2; + float maxRadius = getWidth() / 2; + accuracyPixels = accuracyPixels <= maxRadius ? accuracyPixels : maxRadius; - public void setBearing(double bearing) { - this.bearing = bearing; - if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { - if (myBearingTrackingMode == MyBearingTracking.GPS) { - setCompass(location.getBearing() - bearing); - } else if (myBearingTrackingMode == MyBearingTracking.COMPASS) { - setCompass(magneticHeading - bearing); - } - } - } + // reset + matrix.reset(); + projectedCoordinate[0] = 0; + projectedCoordinate[1] = 0; - public void setCameraPosition(CameraPosition position) { - if (position != null) { - setTilt(position.tilt); - setBearing(position.bearing); - } - } + // put camera in position + camera.save(); + camera.rotate((float) tilt, 0, 0); + camera.getMatrix(matrix); - public void onStart() { - if (myBearingTrackingMode == MyBearingTracking.COMPASS) { - compassListener.onResume(); - } - if (isEnabled()) { - toggleGps(true); - } + if (myBearingTrackingMode != MyBearingTracking.NONE && directionAnimator != null) { + matrix.preRotate((Float) directionAnimator.getAnimatedValue()); } - public void onStop() { - compassListener.onPause(); - toggleGps(false); - } + matrix.preTranslate(0, contentPaddingY); + matrix.postTranslate(pointF.x, pointF.y - contentPaddingY); - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - // cleanup to prevent memory leaks - if (locationChangeAnimator != null) { - locationChangeAnimator.cancel(); - locationChangeAnimator = null; - } + // concat our matrix on canvas + canvas.concat(matrix); - if (accuracyAnimator != null) { - accuracyAnimator.cancel(); - accuracyAnimator = null; - } + // calculate focal point + matrix.mapPoints(projectedCoordinate); + projectedX = pointF.x - projectedCoordinate[0]; + projectedY = pointF.y - projectedCoordinate[1]; - if (directionAnimator != null) { - directionAnimator.cancel(); - directionAnimator = null; - } + // restore orientation from camera + camera.restore(); - if (userLocationListener != null) { - LocationServices services = LocationServices.getLocationServices(getContext()); - services.removeLocationListener(userLocationListener); - userLocationListener = null; - } - } + // draw circle + canvas.drawCircle(0, 0, accuracyPixels, accuracyPaint); - public void update() { - if (isEnabled()) { - myLocationBehavior.invalidate(); - } else { - setVisibility(View.INVISIBLE); - } + // draw shadow + if (backgroundDrawable != null) { + backgroundDrawable.draw(canvas); } - // TODO refactor MapboxMap out - public void setMapboxMap(MapboxMap mapboxMap) { - this.mapboxMap = mapboxMap; - this.projection = mapboxMap.getProjection(); - } + // draw foreground + if (myBearingTrackingMode == MyBearingTracking.NONE) { + if (foregroundDrawable != null) { + foregroundDrawable.draw(canvas); + } + } else if (foregroundBearingDrawable != null && foregroundBounds != null) { + foregroundBearingDrawable.draw(canvas); + } + } + + public void setTilt(@FloatRange(from = 0, to = 60.0f) double tilt) { + this.tilt = tilt; + if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { + mapboxMap.getUiSettings().setFocalPoint(new PointF(getCenterX(), getCenterY())); + } + } + + public void setBearing(double bearing) { + this.bearing = bearing; + if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { + if (myBearingTrackingMode == MyBearingTracking.GPS) { + setCompass(location.getBearing() - bearing); + } else if (myBearingTrackingMode == MyBearingTracking.COMPASS) { + setCompass(magneticHeading - bearing); + } + } + } + + public void setCameraPosition(CameraPosition position) { + if (position != null) { + setTilt(position.tilt); + setBearing(position.bearing); + } + } + + public void onStart() { + if (myBearingTrackingMode == MyBearingTracking.COMPASS) { + compassListener.onResume(); + } + if (isEnabled()) { + toggleGps(true); + } + } - @Override - public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - setVisibility(enabled ? View.VISIBLE : View.INVISIBLE); - toggleGps(enabled); + public void onStop() { + compassListener.onPause(); + toggleGps(false); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + // cleanup to prevent memory leaks + if (locationChangeAnimator != null) { + locationChangeAnimator.cancel(); + locationChangeAnimator = null; } - @Override - protected Parcelable onSaveInstanceState() { - Bundle bundle = new Bundle(); - bundle.putParcelable("superState", super.onSaveInstanceState()); - bundle.putDouble("tilt", tilt); - return bundle; + if (accuracyAnimator != null) { + accuracyAnimator.cancel(); + accuracyAnimator = null; + } + + if (directionAnimator != null) { + directionAnimator.cancel(); + directionAnimator = null; + } + + if (userLocationListener != null) { + LocationServices services = LocationServices.getLocationServices(getContext()); + services.removeLocationListener(userLocationListener); + userLocationListener = null; + } + } + + public void update() { + if (isEnabled()) { + myLocationBehavior.invalidate(); + } else { + setVisibility(View.INVISIBLE); + } + } + + // TODO refactor MapboxMap out + public void setMapboxMap(MapboxMap mapboxMap) { + this.mapboxMap = mapboxMap; + this.projection = mapboxMap.getProjection(); + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + setVisibility(enabled ? View.VISIBLE : View.INVISIBLE); + toggleGps(enabled); + } + + @Override + protected Parcelable onSaveInstanceState() { + Bundle bundle = new Bundle(); + bundle.putParcelable("superState", super.onSaveInstanceState()); + bundle.putDouble("tilt", tilt); + return bundle; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + if (state instanceof Bundle) { + Bundle bundle = (Bundle) state; + tilt = bundle.getDouble("tilt"); + state = bundle.getParcelable("superState"); + } + super.onRestoreInstanceState(state); + } + + /** + * Enabled / Disable GPS location updates along with updating the UI + * + * @param enableGps true if GPS is to be enabled, false if GPS is to be disabled + */ + private void toggleGps(boolean enableGps) { + LocationServices locationServices = LocationServices.getLocationServices(getContext()); + if (enableGps) { + // Set an initial location if one available + Location lastLocation = locationServices.getLastLocation(); + + if (lastLocation != null) { + setLocation(lastLocation); + } + + if (userLocationListener == null) { + userLocationListener = new GpsLocationListener(this); + } + + locationServices.addLocationListener(userLocationListener); + } else { + // Disable location and user dot + location = null; + locationServices.removeLocationListener(userLocationListener); + } + + locationServices.toggleGPS(enableGps); + } + + public Location getLocation() { + return location; + } + + public void setLocation(Location location) { + if (location == null) { + this.location = null; + return; + } + + this.location = location; + myLocationBehavior.updateLatLng(location); + } + + public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) { + this.myBearingTrackingMode = myBearingTrackingMode; + if (myBearingTrackingMode == MyBearingTracking.COMPASS) { + compassListener.onResume(); + } else { + compassListener.onPause(); + if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { + // always face north + setCompass(0); + } else { + myLocationBehavior.invalidate(); + } + } + invalidate(); + } + + public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) { + MyLocationBehaviorFactory factory = new MyLocationBehaviorFactory(); + myLocationBehavior = factory.getBehavioralModel(myLocationTrackingMode); + + if (location != null) { + if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { + // center map directly + mapboxMap.easeCamera(CameraUpdateFactory.newLatLng(new LatLng(location)), 0, false /*linear interpolator*/, + false /*do not disable tracking*/, null); + } else { + // do not use interpolated location from tracking mode + latLng = null; + } + myLocationBehavior.updateLatLng(location); + } + + this.myLocationTrackingMode = myLocationTrackingMode; + invalidate(); + } + + @MyLocationTracking.Mode + public int getMyLocationTrackingMode() { + return myLocationTrackingMode; + } + + + @MyBearingTracking.Mode + public int getMyBearingTrackingMode() { + return myBearingTrackingMode; + } + + private void setCompass(double bearing) { + setCompass(bearing, 0 /* no animation */); + } + + private void setCompass(double bearing, long duration) { + float oldDir = previousDirection; + if (directionAnimator != null) { + oldDir = (Float) directionAnimator.getAnimatedValue(); + directionAnimator.end(); + directionAnimator = null; + } + + float newDir = (float) bearing; + float diff = oldDir - newDir; + if (diff > 180.0f) { + newDir += 360.0f; + } else if (diff < -180.0f) { + newDir -= 360.f; } + previousDirection = newDir; - @Override - public void onRestoreInstanceState(Parcelable state) { - if (state instanceof Bundle) { - Bundle bundle = (Bundle) state; - tilt = bundle.getDouble("tilt"); - state = bundle.getParcelable("superState"); - } - super.onRestoreInstanceState(state); - } + directionAnimator = ValueAnimator.ofFloat(oldDir, newDir); + directionAnimator.setDuration(duration); + directionAnimator.addUpdateListener(invalidateSelfOnUpdateListener); + directionAnimator.start(); + } - /** - * Enabled / Disable GPS location updates along with updating the UI - * - * @param enableGps true if GPS is to be enabled, false if GPS is to be disabled - */ - private void toggleGps(boolean enableGps) { - LocationServices locationServices = LocationServices.getLocationServices(getContext()); - if (enableGps) { - // Set an initial location if one available - Location lastLocation = locationServices.getLastLocation(); - - if (lastLocation != null) { - setLocation(lastLocation); - } + public float getCenterX() { + return (getX() + getMeasuredWidth()) / 2 + contentPaddingX - projectedX; + } - if (userLocationListener == null) { - userLocationListener = new GpsLocationListener(this); - } + public float getCenterY() { + return (getY() + getMeasuredHeight()) / 2 + contentPaddingY - projectedY; + } - locationServices.addLocationListener(userLocationListener); - } else { - // Disable location and user dot - location = null; - locationServices.removeLocationListener(userLocationListener); - } + public void setContentPadding(int[] padding) { + contentPaddingX = (padding[0] - padding[2]) / 2; + contentPaddingY = (padding[1] - padding[3]) / 2; + } + + private static class GpsLocationListener implements LocationListener { - locationServices.toggleGPS(enableGps); + private WeakReference<MyLocationView> userLocationView; + + GpsLocationListener(MyLocationView myLocationView) { + userLocationView = new WeakReference<>(myLocationView); } - public Location getLocation() { - return location; + /** + * Callback method for receiving location updates from LocationServices. + * + * @param location The new Location data + */ + @Override + public void onLocationChanged(Location location) { + MyLocationView locationView = userLocationView.get(); + if (locationView != null) { + locationView.setLocation(location); + } } + } - public void setLocation(Location location) { - if (location == null) { - this.location = null; - return; - } + private class CompassListener implements SensorEventListener { - this.location = location; - myLocationBehavior.updateLatLng(location); - } + private final SensorManager sensorManager; - public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) { - this.myBearingTrackingMode = myBearingTrackingMode; - if (myBearingTrackingMode == MyBearingTracking.COMPASS) { - compassListener.onResume(); - } else { - compassListener.onPause(); - if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { - // always face north - setCompass(0); - } else { - myLocationBehavior.invalidate(); - } - } - invalidate(); - } + private Sensor rotationVectorSensor; + float[] matrix = new float[9]; + float[] orientation = new float[3]; - public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) { - MyLocationBehaviorFactory factory = new MyLocationBehaviorFactory(); - myLocationBehavior = factory.getBehavioralModel(myLocationTrackingMode); + // Compass data + private long compassUpdateNextTimestamp = 0; - if (location != null) { - if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { - // center map directly - mapboxMap.easeCamera(CameraUpdateFactory.newLatLng(new LatLng(location)), 0, false /*linear interpolator*/, false /*do not disable tracking*/, null); - } else { - // do not use interpolated location from tracking mode - latLng = null; - } - myLocationBehavior.updateLatLng(location); - } + CompassListener(Context context) { + sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + rotationVectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR); + } - this.myLocationTrackingMode = myLocationTrackingMode; - invalidate(); + public void onResume() { + sensorManager.registerListener(this, rotationVectorSensor, SensorManager.SENSOR_DELAY_GAME); } - @MyLocationTracking.Mode - public int getMyLocationTrackingMode() { - return myLocationTrackingMode; + public void onPause() { + sensorManager.unregisterListener(this, rotationVectorSensor); } + @Override + public void onSensorChanged(SensorEvent event) { - @MyBearingTracking.Mode - public int getMyBearingTrackingMode() { - return myBearingTrackingMode; - } + // check when the last time the compass was updated, return if too soon. + long currentTime = SystemClock.elapsedRealtime(); + if (currentTime < compassUpdateNextTimestamp) { + return; + } - private void setCompass(double bearing) { - setCompass(bearing, 0 /* no animation */); - } + if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) { - private void setCompass(double bearing, long duration) { - float oldDir = previousDirection; - if (directionAnimator != null) { - oldDir = (Float) directionAnimator.getAnimatedValue(); - directionAnimator.end(); - directionAnimator = null; - } + // calculate the rotation matrix + SensorManager.getRotationMatrixFromVector(matrix, event.values); + SensorManager.getOrientation(matrix, orientation); - float newDir = (float) bearing; - float diff = oldDir - newDir; - if (diff > 180.0f) { - newDir += 360.0f; - } else if (diff < -180.0f) { - newDir -= 360.f; + magneticHeading = (float) Math.toDegrees(SensorManager.getOrientation(matrix, orientation)[0]); + if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { + // Change the user location view orientation to reflect the device orientation + rotateCamera(magneticHeading); + setCompass(0, COMPASS_UPDATE_RATE_MS); + } else { + // Change compass direction + setCompass(magneticHeading - bearing, COMPASS_UPDATE_RATE_MS); } - previousDirection = newDir; - - directionAnimator = ValueAnimator.ofFloat(oldDir, newDir); - directionAnimator.setDuration(duration); - directionAnimator.addUpdateListener(invalidateSelfOnUpdateListener); - directionAnimator.start(); - } - public float getCenterX() { - return (getX() + getMeasuredWidth()) / 2 + contentPaddingX - projectedX; + compassUpdateNextTimestamp = currentTime + COMPASS_UPDATE_RATE_MS; + } } - public float getCenterY() { - return (getY() + getMeasuredHeight()) / 2 + contentPaddingY - projectedY; + private void rotateCamera(float rotation) { + CameraPosition.Builder builder = new CameraPosition.Builder(); + builder.bearing(rotation); + mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), COMPASS_UPDATE_RATE_MS, + false /*linear interpolator*/, false /*do not disable tracking*/, null); } - public void setContentPadding(int[] padding) { - contentPaddingX = (padding[0] - padding[2]) / 2; - contentPaddingY = (padding[1] - padding[3]) / 2; + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { } - private static class GpsLocationListener implements LocationListener { + } - private WeakReference<MyLocationView> userLocationView; + private class MarkerCoordinateAnimatorListener implements ValueAnimator.AnimatorUpdateListener { - GpsLocationListener(MyLocationView myLocationView) { - userLocationView = new WeakReference<>(myLocationView); - } + private MyLocationBehavior behavior; + private double fromLat; + private double fromLng; + private double toLat; + private double toLng; - /** - * Callback method for receiving location updates from LocationServices. - * - * @param location The new Location data - */ - @Override - public void onLocationChanged(Location location) { - MyLocationView locationView = userLocationView.get(); - if (locationView != null) { - locationView.setLocation(location); - } - } + private MarkerCoordinateAnimatorListener(MyLocationBehavior myLocationBehavior, LatLng from, LatLng to) { + behavior = myLocationBehavior; + fromLat = from.getLatitude(); + fromLng = from.getLongitude(); + toLat = to.getLatitude(); + toLng = to.getLongitude(); } - private class CompassListener implements SensorEventListener { - - private final SensorManager sensorManager; - - private Sensor rotationVectorSensor; - float[] matrix = new float[9]; - float[] orientation = new float[3]; - - // Compass data - private long compassUpdateNextTimestamp = 0; - - CompassListener(Context context) { - sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - rotationVectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR); - } - - public void onResume() { - sensorManager.registerListener(this, rotationVectorSensor, SensorManager.SENSOR_DELAY_GAME); - } - - public void onPause() { - sensorManager.unregisterListener(this, rotationVectorSensor); - } - - @Override - public void onSensorChanged(SensorEvent event) { - - // check when the last time the compass was updated, return if too soon. - long currentTime = SystemClock.elapsedRealtime(); - if (currentTime < compassUpdateNextTimestamp) { - return; - } - - if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) { - - // calculate the rotation matrix - SensorManager.getRotationMatrixFromVector(matrix, event.values); - SensorManager.getOrientation(matrix, orientation); - - magneticHeading = (float) Math.toDegrees(SensorManager.getOrientation(matrix, orientation)[0]); - if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { - // Change the user location view orientation to reflect the device orientation - rotateCamera(magneticHeading); - setCompass(0, COMPASS_UPDATE_RATE_MS); - } else { - // Change compass direction - setCompass(magneticHeading - bearing, COMPASS_UPDATE_RATE_MS); - } - - compassUpdateNextTimestamp = currentTime + COMPASS_UPDATE_RATE_MS; - } - } - - private void rotateCamera(float rotation) { - CameraPosition.Builder builder = new CameraPosition.Builder(); - builder.bearing(rotation); - mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), COMPASS_UPDATE_RATE_MS, false /*linear interpolator*/, false /*do not disable tracking*/, null); - } + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float frac = animation.getAnimatedFraction(); + double latitude = fromLat + (toLat - fromLat) * frac; + double longitude = fromLng + (toLng - fromLng) * frac; + behavior.updateLatLng(latitude, longitude); + update(); + } + } - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - } + private class MyLocationBehaviorFactory { + MyLocationBehavior getBehavioralModel(@MyLocationTracking.Mode int mode) { + if (mode == MyLocationTracking.TRACKING_NONE) { + return new MyLocationShowBehavior(); + } else { + return new MyLocationTrackingBehavior(); + } } + } - private class MarkerCoordinateAnimatorListener implements ValueAnimator.AnimatorUpdateListener { + private abstract class MyLocationBehavior { - private MyLocationBehavior behavior; - private double fromLat; - private double fromLng; - private double toLat; - private double toLng; - - private MarkerCoordinateAnimatorListener(MyLocationBehavior myLocationBehavior, LatLng from, LatLng to) { - behavior = myLocationBehavior; - fromLat = from.getLatitude(); - fromLng = from.getLongitude(); - toLat = to.getLatitude(); - toLng = to.getLongitude(); - } + void updateLatLng(@NonNull Location newLocation) { + location = newLocation; + } - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float frac = animation.getAnimatedFraction(); - double latitude = fromLat + (toLat - fromLat) * frac; - double longitude = fromLng + (toLng - fromLng) * frac; - behavior.updateLatLng(latitude, longitude); - update(); - } + void updateLatLng(double lat, double lon) { + if (latLng != null) { + latLng.setLatitude(lat); + latLng.setLongitude(lon); + } } - private class MyLocationBehaviorFactory { + void updateAccuracy(@NonNull Location location) { + if (accuracyAnimator != null && accuracyAnimator.isRunning()) { + // use current accuracy as a starting point + accuracy = (Float) accuracyAnimator.getAnimatedValue(); + accuracyAnimator.end(); + } - MyLocationBehavior getBehavioralModel(@MyLocationTracking.Mode int mode) { - if (mode == MyLocationTracking.TRACKING_NONE) { - return new MyLocationShowBehavior(); - } else { - return new MyLocationTrackingBehavior(); - } - } + accuracyAnimator = ValueAnimator.ofFloat(accuracy * 10, location.getAccuracy() * 10); + accuracyAnimator.setDuration(750); + accuracyAnimator.start(); + accuracy = location.getAccuracy(); } - private abstract class MyLocationBehavior { + abstract void invalidate(); + } - void updateLatLng(@NonNull Location newLocation) { - location = newLocation; - } + private class MyLocationTrackingBehavior extends MyLocationBehavior { - void updateLatLng(double lat, double lon) { - if (latLng != null) { - latLng.setLatitude(lat); - latLng.setLongitude(lon); - } - } + @Override + void updateLatLng(@NonNull Location location) { + super.updateLatLng(location); + if (latLng == null) { + // first location fix + latLng = new LatLng(location); + locationUpdateTimestamp = SystemClock.elapsedRealtime(); + } + + // updateLatLng timestamp + float previousUpdateTimeStamp = locationUpdateTimestamp; + locationUpdateTimestamp = SystemClock.elapsedRealtime(); + + // calculate animation duration + float animationDuration; + if (previousUpdateTimeStamp == 0) { + animationDuration = 0; + } else { + animationDuration = (locationUpdateTimestamp - previousUpdateTimeStamp) * 1.1f + /*make animation slightly longer*/; + } + + // calculate interpolated location + latLng = new LatLng(location); + CameraPosition.Builder builder = new CameraPosition.Builder().target(latLng); + + // add direction + if (myBearingTrackingMode == MyBearingTracking.GPS) { + if (location.hasBearing()) { + builder.bearing(location.getBearing()); + } + setCompass(0, COMPASS_UPDATE_RATE_MS); + } + + // accuracy + updateAccuracy(location); + + // ease to new camera position with a linear interpolator + mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), (int) animationDuration, + false /*linear interpolator*/, false /*do not disable tracking*/, null); + } - void updateAccuracy(@NonNull Location location) { - if (accuracyAnimator != null && accuracyAnimator.isRunning()) { - // use current accuracy as a starting point - accuracy = (Float) accuracyAnimator.getAnimatedValue(); - accuracyAnimator.end(); - } - - accuracyAnimator = ValueAnimator.ofFloat(accuracy * 10, location.getAccuracy() * 10); - accuracyAnimator.setDuration(750); - accuracyAnimator.start(); - accuracy = location.getAccuracy(); - } + @Override + void invalidate() { + int[] mapPadding = mapboxMap.getPadding(); + float x = (getWidth() + mapPadding[0] - mapPadding[2]) / 2 + contentPaddingX; + float y = (getHeight() - mapPadding[3] + mapPadding[1]) / 2 + contentPaddingY; + screenLocation = new PointF(x, y); + MyLocationView.this.invalidate(); + } + } - abstract void invalidate(); - } - - private class MyLocationTrackingBehavior extends MyLocationBehavior { - - @Override - void updateLatLng(@NonNull Location location) { - super.updateLatLng(location); - if (latLng == null) { - // first location fix - latLng = new LatLng(location); - locationUpdateTimestamp = SystemClock.elapsedRealtime(); - } - - // updateLatLng timestamp - float previousUpdateTimeStamp = locationUpdateTimestamp; - locationUpdateTimestamp = SystemClock.elapsedRealtime(); - - // calculate animation duration - float animationDuration; - if (previousUpdateTimeStamp == 0) { - animationDuration = 0; - } else { - animationDuration = (locationUpdateTimestamp - previousUpdateTimeStamp) * 1.1f /*make animation slightly longer*/; - } - - // calculate interpolated location - latLng = new LatLng(location); - CameraPosition.Builder builder = new CameraPosition.Builder().target(latLng); - - // add direction - if (myBearingTrackingMode == MyBearingTracking.GPS) { - if (location.hasBearing()) { - builder.bearing(location.getBearing()); - } - setCompass(0, COMPASS_UPDATE_RATE_MS); - } - - // accuracy - updateAccuracy(location); - - // ease to new camera position with a linear interpolator - mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), (int) animationDuration, false /*linear interpolator*/, false /*do not disable tracking*/, null); - } + private class MyLocationShowBehavior extends MyLocationBehavior { - @Override - void invalidate() { - int[] mapPadding = mapboxMap.getPadding(); - float x = (getWidth() + mapPadding[0] - mapPadding[2]) / 2 + contentPaddingX; - float y = (getHeight() - mapPadding[3] + mapPadding[1]) / 2 + contentPaddingY; - screenLocation = new PointF(x, y); - MyLocationView.this.invalidate(); - } + @Override + void updateLatLng(@NonNull final Location location) { + super.updateLatLng(location); + if (latLng == null) { + // first location update + latLng = new LatLng(location); + locationUpdateTimestamp = SystemClock.elapsedRealtime(); + } + + // update LatLng location + LatLng newLocation = new LatLng(location); + + // update LatLng accuracy + updateAccuracy(location); + + // calculate updateLatLng time + add some extra offset to improve animation + long previousUpdateTimeStamp = locationUpdateTimestamp; + locationUpdateTimestamp = SystemClock.elapsedRealtime(); + long locationUpdateDuration = (long) ((locationUpdateTimestamp - previousUpdateTimeStamp) * 1.2f); + + // animate changes + if (locationChangeAnimator != null) { + locationChangeAnimator.end(); + locationChangeAnimator = null; + } + + locationChangeAnimator = ValueAnimator.ofFloat(0.0f, 1.0f); + locationChangeAnimator.setDuration(locationUpdateDuration); + locationChangeAnimator.addUpdateListener(new MarkerCoordinateAnimatorListener(this, + latLng, newLocation + )); + locationChangeAnimator.start(); + latLng = newLocation; } - private class MyLocationShowBehavior extends MyLocationBehavior { - - @Override - void updateLatLng(@NonNull final Location location) { - super.updateLatLng(location); - if (latLng == null) { - // first location update - latLng = new LatLng(location); - locationUpdateTimestamp = SystemClock.elapsedRealtime(); - } - - // update LatLng location - LatLng newLocation = new LatLng(location); - - // update LatLng accuracy - updateAccuracy(location); - - // calculate updateLatLng time + add some extra offset to improve animation - long previousUpdateTimeStamp = locationUpdateTimestamp; - locationUpdateTimestamp = SystemClock.elapsedRealtime(); - long locationUpdateDuration = (long) ((locationUpdateTimestamp - previousUpdateTimeStamp) * 1.2f); - - // animate changes - if (locationChangeAnimator != null) { - locationChangeAnimator.end(); - locationChangeAnimator = null; - } - - locationChangeAnimator = ValueAnimator.ofFloat(0.0f, 1.0f); - locationChangeAnimator.setDuration(locationUpdateDuration); - locationChangeAnimator.addUpdateListener(new MarkerCoordinateAnimatorListener(this, - latLng, newLocation - )); - locationChangeAnimator.start(); - latLng = newLocation; - } - - @Override - void invalidate() { - if (latLng != null) { - screenLocation = projection.toScreenLocation(latLng); - } - MyLocationView.this.invalidate(); - } + @Override + void invalidate() { + if (latLng != null) { + screenLocation = projection.toScreenLocation(latLng); + } + MyLocationView.this.invalidate(); } + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java index e85ed96927..6cfbfed733 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 @@ -17,280 +17,282 @@ import com.mapbox.mapboxsdk.maps.Projection; */ public class MyLocationViewSettings { - private Projection projection; - private MyLocationView myLocationView; - private FocalPointChangeListener focalPointChangeListener; - - // - // State - // - - private boolean enabled; - - // - // Foreground - // - - private Drawable foregroundDrawable; - private Drawable foregroundBearingDrawable; - - @ColorInt - private int foregroundTintColor; - - // - // Background - // - - private Drawable backgroundDrawable; - private int[] backgroundOffset = new int[4]; - - @ColorInt - private int backgroundTintColor; - - // - // Accuracy - // - - private int accuracyAlpha; - - @ColorInt - private int accuracyTintColor; - - // - // Padding - // - - private int[] padding = new int[4]; - - /** - * Creates an instance of MyLocationViewSettings - * <p> - * - * @param myLocationView the MyLocationView to apply the settings to - * @param projection the MapView projection - * @param focalPointChangedListener the interface to be invoked when focal points changes - * @see MyLocationView - */ - public MyLocationViewSettings(MyLocationView myLocationView, Projection projection, FocalPointChangeListener focalPointChangedListener) { - this.myLocationView = myLocationView; - this.projection = projection; - this.focalPointChangeListener = focalPointChangedListener; - } - - public void initialise(@NonNull MapboxMapOptions options) { - CameraPosition position = options.getCamera(); - if (position != null && !position.equals(CameraPosition.DEFAULT)) { - setTilt(position.tilt); - } - setForegroundDrawable(options.getMyLocationForegroundDrawable(), options.getMyLocationForegroundBearingDrawable()); - setForegroundTintColor(options.getMyLocationForegroundTintColor()); - setBackgroundDrawable(options.getMyLocationBackgroundDrawable(), options.getMyLocationBackgroundPadding()); - setBackgroundTintColor(options.getMyLocationBackgroundTintColor()); - setAccuracyAlpha(options.getMyLocationAccuracyAlpha()); - setAccuracyTintColor(options.getMyLocationAccuracyTintColor()); - } - - /** - * Returns if the MyLocationView is enabled - * - * @return true if MyLocationView is enabled, - */ - public boolean isEnabled() { - return enabled; - } - - /** - * Set the enabled state of MyLocationView - * - * @param enabled true shows the MyLocationView on the map - */ - public void setEnabled(boolean enabled) { - this.enabled = enabled; - myLocationView.setEnabled(enabled); - } - - /** - * Set the foreground drawable of the MyLocationView - * <p> - * The foreground drawable is the image visible on screen - * </p> - * - * @param foregroundDrawable the drawable to show as foreground without bearing - * @param foregroundBearingDrawable the drawable to show as foreground when bearing is enabled - */ - public void setForegroundDrawable(Drawable foregroundDrawable, Drawable foregroundBearingDrawable) { - this.foregroundDrawable = foregroundDrawable; - this.foregroundBearingDrawable = foregroundBearingDrawable; - myLocationView.setForegroundDrawables(foregroundDrawable, foregroundBearingDrawable); - } - - /** - * Get the foreground drawable when bearing is disabled. - * - * @return the drawable used as foreground - */ - public Drawable getForegroundDrawable() { - return foregroundDrawable; - } - - /** - * Get the foreground drawable when bearing is enabled. - * - * @return the bearing drawable used as foreground - */ - public Drawable getForegroundBearingDrawable() { - return foregroundBearingDrawable; - } - - /** - * Set the foreground tint color. - * <p> - * The color will tint both the foreground and the bearing foreground drawable. - * </p> - * - * @param foregroundTintColor the color to tint the foreground drawable - */ - public void setForegroundTintColor(@ColorInt int foregroundTintColor) { - this.foregroundTintColor = foregroundTintColor; - myLocationView.setForegroundDrawableTint(foregroundTintColor); - } - - /** - * Get the foreground tint color. - * - * @return the foreground tint color - */ - public int getForegroundTintColor() { - return foregroundTintColor; - } - - /** - * Set the background drawable of MyLocationView - * <p> - * Padding can be added to provide an offset to the background - * </p> - * - * @param backgroundDrawable the drawable to show as background - * @param padding the padding added to the background - */ - public void setBackgroundDrawable(Drawable backgroundDrawable, int[] padding) { - this.backgroundDrawable = backgroundDrawable; - this.backgroundOffset = padding; - if (padding != null && padding.length == 4) { - myLocationView.setShadowDrawable(backgroundDrawable, padding[0], padding[1], padding[2], padding[3]); - } else { - myLocationView.setShadowDrawable(backgroundDrawable); - } - } - - /** - * Get the background drawable of MyLocationView. - * - * @return the drawable used as background - */ - public Drawable getBackgroundDrawable() { - return backgroundDrawable; + private Projection projection; + private MyLocationView myLocationView; + private FocalPointChangeListener focalPointChangeListener; + + // + // State + // + + private boolean enabled; + + // + // Foreground + // + + private Drawable foregroundDrawable; + private Drawable foregroundBearingDrawable; + + @ColorInt + private int foregroundTintColor; + + // + // Background + // + + private Drawable backgroundDrawable; + private int[] backgroundOffset = new int[4]; + + @ColorInt + private int backgroundTintColor; + + // + // Accuracy + // + + private int accuracyAlpha; + + @ColorInt + private int accuracyTintColor; + + // + // Padding + // + + private int[] padding = new int[4]; + + /** + * Creates an instance of MyLocationViewSettings + * <p> + * + * @param myLocationView the MyLocationView to apply the settings to + * @param projection the MapView projection + * @param focalPointChangedListener the interface to be invoked when focal points changes + * @see MyLocationView + */ + public MyLocationViewSettings(MyLocationView myLocationView, Projection projection, FocalPointChangeListener + focalPointChangedListener) { + this.myLocationView = myLocationView; + this.projection = projection; + this.focalPointChangeListener = focalPointChangedListener; + } + + public void initialise(@NonNull MapboxMapOptions options) { + CameraPosition position = options.getCamera(); + if (position != null && !position.equals(CameraPosition.DEFAULT)) { + setTilt(position.tilt); } - - /** - * Set the background tint color. - * - * @param backgroundTintColor the color to tint the background - */ - public void setBackgroundTintColor(@ColorInt int backgroundTintColor) { - this.backgroundTintColor = backgroundTintColor; - myLocationView.setShadowDrawableTint(backgroundTintColor); - } - - /** - * Get the background tint color. - * - * @return the background tint color - */ - public int getBackgroundTintColor() { - return backgroundTintColor; - } - - /** - * Get the background offset. - * - * @return the background offset - */ - public int[] getBackgroundOffset() { - return backgroundOffset; - } - - /** - * Set the MyLocationView padding. - * - * @param left the padding left of MyLocationView - * @param top the padding top of MyLocationView - * @param right the padding right of MyLocationView - * @param bottom the padding bottom of MyLocaionView - */ - public void setPadding(int left, int top, int right, int bottom) { - padding = new int[]{left, top, right, bottom}; - myLocationView.setContentPadding(padding); - projection.invalidateContentPadding(padding); - invalidateFocalPointForTracking(myLocationView); + setForegroundDrawable(options.getMyLocationForegroundDrawable(), options.getMyLocationForegroundBearingDrawable()); + setForegroundTintColor(options.getMyLocationForegroundTintColor()); + setBackgroundDrawable(options.getMyLocationBackgroundDrawable(), options.getMyLocationBackgroundPadding()); + setBackgroundTintColor(options.getMyLocationBackgroundTintColor()); + setAccuracyAlpha(options.getMyLocationAccuracyAlpha()); + setAccuracyTintColor(options.getMyLocationAccuracyTintColor()); + } + + /** + * Returns if the MyLocationView is enabled + * + * @return true if MyLocationView is enabled, + */ + public boolean isEnabled() { + return enabled; + } + + /** + * Set the enabled state of MyLocationView + * + * @param enabled true shows the MyLocationView on the map + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + myLocationView.setEnabled(enabled); + } + + /** + * Set the foreground drawable of the MyLocationView + * <p> + * The foreground drawable is the image visible on screen + * </p> + * + * @param foregroundDrawable the drawable to show as foreground without bearing + * @param foregroundBearingDrawable the drawable to show as foreground when bearing is enabled + */ + public void setForegroundDrawable(Drawable foregroundDrawable, Drawable foregroundBearingDrawable) { + this.foregroundDrawable = foregroundDrawable; + this.foregroundBearingDrawable = foregroundBearingDrawable; + myLocationView.setForegroundDrawables(foregroundDrawable, foregroundBearingDrawable); + } + + /** + * Get the foreground drawable when bearing is disabled. + * + * @return the drawable used as foreground + */ + public Drawable getForegroundDrawable() { + return foregroundDrawable; + } + + /** + * Get the foreground drawable when bearing is enabled. + * + * @return the bearing drawable used as foreground + */ + public Drawable getForegroundBearingDrawable() { + return foregroundBearingDrawable; + } + + /** + * Set the foreground tint color. + * <p> + * The color will tint both the foreground and the bearing foreground drawable. + * </p> + * + * @param foregroundTintColor the color to tint the foreground drawable + */ + public void setForegroundTintColor(@ColorInt int foregroundTintColor) { + this.foregroundTintColor = foregroundTintColor; + myLocationView.setForegroundDrawableTint(foregroundTintColor); + } + + /** + * Get the foreground tint color. + * + * @return the foreground tint color + */ + public int getForegroundTintColor() { + return foregroundTintColor; + } + + /** + * Set the background drawable of MyLocationView + * <p> + * Padding can be added to provide an offset to the background + * </p> + * + * @param backgroundDrawable the drawable to show as background + * @param padding the padding added to the background + */ + public void setBackgroundDrawable(Drawable backgroundDrawable, int[] padding) { + this.backgroundDrawable = backgroundDrawable; + this.backgroundOffset = padding; + if (padding != null && padding.length == 4) { + myLocationView.setShadowDrawable(backgroundDrawable, padding[0], padding[1], padding[2], padding[3]); + } else { + myLocationView.setShadowDrawable(backgroundDrawable); } - - /** - * Get the MyLocationView padding. - * - * @return an array describing the padding in a LTRB manner - */ - public int[] getPadding() { - return padding; - } - - /** - * Get the alpha value of the accuracy circle of MyLocationView - * - * @return the alpha value - */ - public int getAccuracyAlpha() { - return accuracyAlpha; - } - - /** - * Set the alpha value of the accuracy circle of MyLocationView - * - * @param accuracyAlpha the alpha value to set - */ - public void setAccuracyAlpha(@IntRange(from = 0, to = 255) int accuracyAlpha) { - this.accuracyAlpha = accuracyAlpha; - myLocationView.setAccuracyAlpha(accuracyAlpha); - } - - /** - * Get the accuracy tint color of MyLocationView. - * - * @return the tint color used for accuracy - */ - public int getAccuracyTintColor() { - return accuracyTintColor; - } - - /** - * Set the accuracy tint color of MyLocationView. - * - * @param accuracyTintColor the accuracy tint color - */ - public void setAccuracyTintColor(@ColorInt int accuracyTintColor) { - this.accuracyTintColor = accuracyTintColor; - myLocationView.setAccuracyTint(accuracyTintColor); - } - - public void setTilt(double tilt) { - myLocationView.setTilt(tilt); - } - - private void invalidateFocalPointForTracking(MyLocationView myLocationView) { - if (!(myLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE)) { - focalPointChangeListener.onFocalPointChanged(new PointF(myLocationView.getCenterX(), myLocationView.getCenterY())); - } else { - focalPointChangeListener.onFocalPointChanged(null); - } + } + + /** + * Get the background drawable of MyLocationView. + * + * @return the drawable used as background + */ + public Drawable getBackgroundDrawable() { + return backgroundDrawable; + } + + /** + * Set the background tint color. + * + * @param backgroundTintColor the color to tint the background + */ + public void setBackgroundTintColor(@ColorInt int backgroundTintColor) { + this.backgroundTintColor = backgroundTintColor; + myLocationView.setShadowDrawableTint(backgroundTintColor); + } + + /** + * Get the background tint color. + * + * @return the background tint color + */ + public int getBackgroundTintColor() { + return backgroundTintColor; + } + + /** + * Get the background offset. + * + * @return the background offset + */ + public int[] getBackgroundOffset() { + return backgroundOffset; + } + + /** + * Set the MyLocationView padding. + * + * @param left the padding left of MyLocationView + * @param top the padding top of MyLocationView + * @param right the padding right of MyLocationView + * @param bottom the padding bottom of MyLocaionView + */ + public void setPadding(int left, int top, int right, int bottom) { + padding = new int[] {left, top, right, bottom}; + myLocationView.setContentPadding(padding); + projection.invalidateContentPadding(padding); + invalidateFocalPointForTracking(myLocationView); + } + + /** + * Get the MyLocationView padding. + * + * @return an array describing the padding in a LTRB manner + */ + public int[] getPadding() { + return padding; + } + + /** + * Get the alpha value of the accuracy circle of MyLocationView + * + * @return the alpha value + */ + public int getAccuracyAlpha() { + return accuracyAlpha; + } + + /** + * Set the alpha value of the accuracy circle of MyLocationView + * + * @param accuracyAlpha the alpha value to set + */ + public void setAccuracyAlpha(@IntRange(from = 0, to = 255) int accuracyAlpha) { + this.accuracyAlpha = accuracyAlpha; + myLocationView.setAccuracyAlpha(accuracyAlpha); + } + + /** + * Get the accuracy tint color of MyLocationView. + * + * @return the tint color used for accuracy + */ + public int getAccuracyTintColor() { + return accuracyTintColor; + } + + /** + * Set the accuracy tint color of MyLocationView. + * + * @param accuracyTintColor the accuracy tint color + */ + public void setAccuracyTintColor(@ColorInt int accuracyTintColor) { + this.accuracyTintColor = accuracyTintColor; + myLocationView.setAccuracyTint(accuracyTintColor); + } + + public void setTilt(double tilt) { + myLocationView.setTilt(tilt); + } + + private void invalidateFocalPointForTracking(MyLocationView myLocationView) { + if (!(myLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE)) { + focalPointChangeListener.onFocalPointChanged(new PointF(myLocationView.getCenterX(), + myLocationView.getCenterY())); + } else { + focalPointChangeListener.onFocalPointChanged(null); } + } } |