diff options
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps')
14 files changed, 1322 insertions, 1227 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraUpdate.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraUpdate.java deleted file mode 100644 index 0f3e710134..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraUpdate.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.support.annotation.NonNull; - -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.maps.MapboxMap; - -public interface CameraUpdate { - - CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap); - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraUpdateFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraUpdateFactory.java deleted file mode 100644 index cad5152316..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraUpdateFactory.java +++ /dev/null @@ -1,387 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.graphics.Point; -import android.graphics.PointF; -import android.graphics.RectF; -import android.support.annotation.IntDef; -import android.support.annotation.NonNull; - -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; -import com.mapbox.mapboxsdk.utils.MathUtils; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -public class CameraUpdateFactory { - - /** - * Returns a CameraUpdate that moves the camera to a specified CameraPosition. - * - * @param cameraPosition Camera Position to change to - * @return CameraUpdate Final Camera Position data - */ - public static CameraUpdate newCameraPosition(@NonNull CameraPosition cameraPosition) { - return new CameraPositionUpdate(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt, cameraPosition.zoom); - } - - /** - * Returns a CameraUpdate that moves the center of the screen to a latitude and longitude - * specified by a LatLng object. This centers the camera on the LatLng object. - * - * @param latLng - * @return - */ - public static CameraUpdate newLatLng(@NonNull LatLng latLng) { - return new CameraPositionUpdate(-1, latLng, -1, -1); - } - - /** - * Returns a CameraUpdate that transforms the camera such that the specified latitude/longitude - * bounds are centered on screen at the greatest possible zoom level. - * You can specify padding, in order to inset the bounding box from the map view's edges. - * The returned CameraUpdate has a bearing of 0 and a tilt of 0. - * - * @param bounds - * @param padding - * @return - */ - public static CameraUpdate newLatLngBounds(@NonNull LatLngBounds bounds, int padding) { - return newLatLngBounds(bounds, padding, padding, padding, padding); - } - - - /** - * Returns a CameraUpdate that transforms the camera such that the specified latitude/longitude - * bounds are centered on screen at the greatest possible zoom level. - * You can specify padding, in order to inset the bounding box from the map view's edges. - * The returned CameraUpdate has a bearing of 0 and a tilt of 0. - * - * @param bounds - * @param paddingLeft - * @param paddingTop - * @param paddingRight - * @param paddingBottom - * @return - */ - public static CameraUpdate newLatLngBounds(@NonNull LatLngBounds bounds, int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { - return new CameraBoundsUpdate(bounds, paddingLeft, paddingTop, paddingRight, paddingBottom); - } - - /** - * Returns a CameraUpdate that moves the center of the screen to a latitude and longitude specified by a LatLng object, and moves to the given zoom level. - * - * @param latLng - * @param zoom - * @return - */ - public static CameraUpdate newLatLngZoom(@NonNull LatLng latLng, float zoom) { - return new CameraPositionUpdate(-1, latLng, -1, zoom); - } - - /** - * Returns a CameraUpdate that scrolls the camera over the map, shifting the center of view by the specified number of pixels in the x and y directions. - * - * @param xPixel - * @param yPixel - * @return - */ - public static CameraUpdate scrollBy(float xPixel, float yPixel) { - return new CameraMoveUpdate(xPixel, yPixel); - } - - /** - * Returns a CameraUpdate that shifts the zoom level of the current camera viewpoint. - * - * @param amount - * @param focus - * @return - */ - public static CameraUpdate zoomBy(float amount, Point focus) { - return new ZoomUpdate(amount, focus.x, focus.y); - } - - /** - * Returns a CameraUpdate that shifts the zoom level of the current camera viewpoint. - * - * @param amount - * @return - */ - public static CameraUpdate zoomBy(float amount) { - return new ZoomUpdate(ZoomUpdate.ZOOM_BY, amount); - } - - /** - * Returns a CameraUpdate that zooms in on the map by moving the viewpoint's height closer to the Earth's surface. The zoom increment is 1.0. - * - * @return - */ - public static CameraUpdate zoomIn() { - return new ZoomUpdate(ZoomUpdate.ZOOM_IN); - } - - /** - * Returns a CameraUpdate that zooms out on the map by moving the viewpoint's height farther away from the Earth's surface. The zoom increment is -1.0. - * - * @return - */ - public static CameraUpdate zoomOut() { - return new ZoomUpdate(ZoomUpdate.ZOOM_OUT); - } - - /** - * Returns a CameraUpdate that moves the camera viewpoint to a particular zoom level. - * - * @param zoom - * @return - */ - public static CameraUpdate zoomTo(float zoom) { - return new ZoomUpdate(ZoomUpdate.ZOOM_TO, zoom); - } - - // - // CameraUpdate types - // - - public static class CameraPositionUpdate implements CameraUpdate { - - private final float bearing; - private final LatLng target; - private final float tilt; - private final float zoom; - - CameraPositionUpdate(float bearing, LatLng target, float tilt, float zoom) { - this.bearing = bearing; - this.target = target; - this.tilt = tilt; - this.zoom = zoom; - } - - public LatLng getTarget() { - return target; - } - - public float getBearing() { - return bearing; - } - - public float getTilt() { - return tilt; - } - - public float getZoom() { - return zoom; - } - - @Override - public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) { - CameraPosition previousPosition = mapboxMap.getCameraPosition(); - if (target == null) { - return new CameraPosition.Builder(true) - .tilt(tilt) - .zoom(zoom) - .bearing(bearing) - .target(previousPosition.target) - .build(); - } - return new CameraPosition.Builder(this).build(); - } - } - - public static class CameraBoundsUpdate implements CameraUpdate { - - private LatLngBounds bounds; - private RectF padding; - - public CameraBoundsUpdate(LatLngBounds bounds, RectF padding) { - this.bounds = bounds; - this.padding = padding; - } - - public CameraBoundsUpdate(LatLngBounds bounds, int[] padding) { - this(bounds, new RectF(padding[0], padding[1], padding[2], padding[3])); - } - - public CameraBoundsUpdate(LatLngBounds bounds, int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { - this(bounds, new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}); - } - - public LatLngBounds getBounds() { - return bounds; - } - - public RectF getPadding() { - return padding; - } - - @Override - public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) { - MapView mapView = mapboxMap.getMapView(); - RectF padding = getPadding(); - - // Calculate the bounds of the possibly rotated shape with respect to the viewport. - PointF nePixel = new PointF(-10000, -10000); - PointF swPixel = new PointF(1000, 10000); - float viewportHeight = mapView.getHeight(); - for (LatLng latLng : getBounds().toLatLngs()) { - PointF pixel = mapView.toScreenLocation(latLng); - swPixel.x = Math.min(swPixel.x, pixel.x); - nePixel.x = Math.max(nePixel.x, pixel.x); - swPixel.y = Math.min(swPixel.y, viewportHeight - pixel.y); - nePixel.y = Math.max(nePixel.y, viewportHeight - pixel.y); - } - - float width = nePixel.x - swPixel.x; - float height = nePixel.y - swPixel.y; - - // Calculate the zoom level. - float scaleX = (mapView.getWidth() - padding.left - padding.right) / width; - float scaleY = (mapView.getHeight() - padding.top - padding.bottom) / height; - float minScale = scaleX < scaleY ? scaleX : scaleY; - double zoom = Math.log(mapView.getScale() * minScale) / Math.log(2); - zoom = MathUtils.clamp(zoom, (float) mapView.getMinZoom(), (float) mapView.getMaxZoom()); - - // Calculate the center point of a virtual bounds that is extended in all directions by padding. - PointF paddedNEPixel = new PointF(nePixel.x + padding.right / minScale, nePixel.y + padding.top / minScale); - PointF paddedSWPixel = new PointF(swPixel.x - padding.left / minScale, swPixel.y - padding.bottom / minScale); - PointF centerPixel = new PointF((paddedNEPixel.x + paddedSWPixel.x) / 2, (paddedNEPixel.y + paddedSWPixel.y) / 2); - - centerPixel.y = viewportHeight - centerPixel.y; - - LatLng center = mapboxMap.getProjection().fromScreenLocation(centerPixel); - - return new CameraPosition.Builder() - .target(center) - .zoom((float) zoom) - .tilt(0) - .bearing(0) - .build(); - } - } - - public static class CameraMoveUpdate implements CameraUpdate { - - private float x; - private float y; - - public CameraMoveUpdate(float x, float y) { - this.x = x; - this.y = y; - } - - @Override - public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) { - MapView mapView = mapboxMap.getMapView(); - - // Calculate the new center point - float viewPortWidth = mapView.getWidth(); - float viewPortHeight = mapView.getHeight(); - PointF targetPoint = new PointF(viewPortWidth / 2 + x, viewPortHeight / 2 + y); - - // Convert point to LatLng - LatLng latLng = mapView.fromScreenLocation(targetPoint); - - CameraPosition cameraPosition = mapboxMap.getCameraPosition(); - return new CameraPosition.Builder() - .target(latLng) - .zoom(cameraPosition.zoom) - .tilt(cameraPosition.tilt) - .bearing(cameraPosition.bearing) - .build(); - } - } - - public static class ZoomUpdate implements CameraUpdate { - - @IntDef({ZOOM_IN, ZOOM_OUT, ZOOM_BY, ZOOM_TO, ZOOM_TO_POINT}) - @Retention(RetentionPolicy.SOURCE) - public @interface Type { - } - - public static final int ZOOM_IN = 0; - public static final int ZOOM_OUT = 1; - public static final int ZOOM_BY = 2; - public static final int ZOOM_TO = 3; - public static final int ZOOM_TO_POINT = 4; - - @Type - private final int type; - private final float zoom; - private float x; - private float y; - - ZoomUpdate(@Type int type) { - this.type = type; - this.zoom = 0; - } - - ZoomUpdate(@Type int type, float zoom) { - this.type = type; - this.zoom = zoom; - } - - ZoomUpdate(float zoom, float x, float y) { - this.type = ZOOM_TO_POINT; - this.zoom = zoom; - this.x = x; - this.y = y; - } - - public float getZoom() { - return zoom; - } - - @Type - public int getType() { - return type; - } - - public float getX() { - return x; - } - - public float getY() { - return y; - } - - public float transformZoom(float currentZoom) { - switch (getType()) { - case CameraUpdateFactory.ZoomUpdate.ZOOM_IN: - currentZoom++; - break; - case CameraUpdateFactory.ZoomUpdate.ZOOM_OUT: - currentZoom--; - if (currentZoom < 0) { - currentZoom = 0; - } - break; - case CameraUpdateFactory.ZoomUpdate.ZOOM_TO: - currentZoom = getZoom(); - break; - case CameraUpdateFactory.ZoomUpdate.ZOOM_BY: - currentZoom = currentZoom + getZoom(); - break; - case CameraUpdateFactory.ZoomUpdate.ZOOM_TO_POINT: - currentZoom = currentZoom + getZoom(); - break; - } - return currentZoom; - } - - @Override - public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) { - CameraPosition cameraPosition = mapboxMap.getCameraPosition(); - if (getType() != CameraUpdateFactory.ZoomUpdate.ZOOM_TO_POINT) { - return new CameraPosition.Builder(cameraPosition) - .zoom(transformZoom(cameraPosition.zoom)) - .build(); - } else { - return new CameraPosition.Builder(cameraPosition) - .zoom(transformZoom(cameraPosition.zoom)) - .target(mapboxMap.getProjection().fromScreenLocation(new PointF(getX(), getY()))) - .build(); - } - } - } -} 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 2789e85ed8..4b5aa0cbaf 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 @@ -2,6 +2,7 @@ package com.mapbox.mapboxsdk.maps; import android.app.Fragment; import android.os.Bundle; +import android.os.Handler; import android.support.annotation.NonNull; import android.view.LayoutInflater; import android.view.View; @@ -29,18 +30,10 @@ public class MapFragment extends Fragment { private MapView mMap; - public static MapFragment newInstance(){ + public static MapFragment newInstance() { return new MapFragment(); } - public static MapFragment newInstance(MapboxMapOptions mapboxMapOptions) { - final MapFragment mapFragment = new MapFragment(); - Bundle bundle = new Bundle(); - bundle.putParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS, mapboxMapOptions); - mapFragment.setArguments(bundle); - return mapFragment; - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); @@ -94,7 +87,12 @@ public class MapFragment extends Fragment { } @NonNull - public void getMapAsync(@NonNull OnMapReadyCallback onMapReadyCallback){ - mMap.getMapAsync(onMapReadyCallback); + public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) { + new Handler().post(new Runnable() { + @Override + public void run() { + mMap.getMapAsync(onMapReadyCallback); + } + }); } } 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 bd0a010900..cb9459d5f2 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 @@ -10,7 +10,9 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -27,8 +29,8 @@ import android.support.annotation.FloatRange; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.annotation.RequiresPermission; import android.support.annotation.UiThread; +import android.support.v4.content.ContextCompat; import android.support.v4.view.GestureDetectorCompat; import android.support.v4.view.ScaleGestureDetectorCompat; import android.support.v7.app.AlertDialog; @@ -47,44 +49,50 @@ import android.view.Surface; import android.view.TextureView; import android.view.View; import android.view.ViewConfiguration; +import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.FrameLayout; import android.widget.ImageView; +import android.widget.ListView; import android.widget.ZoomButtonsController; - import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector; import com.almeros.android.multitouch.gesturedetectors.ShoveGestureDetector; import com.almeros.android.multitouch.gesturedetectors.TwoFingerGestureDetector; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.Icon; +import com.mapbox.mapboxsdk.annotations.IconFactory; import com.mapbox.mapboxsdk.annotations.InfoWindow; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.MarkerOptions; import com.mapbox.mapboxsdk.annotations.Polygon; -import com.mapbox.mapboxsdk.annotations.PolygonOptions; import com.mapbox.mapboxsdk.annotations.Polyline; -import com.mapbox.mapboxsdk.annotations.PolylineOptions; -import com.mapbox.mapboxsdk.annotations.IconFactory; 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; import com.mapbox.mapboxsdk.exceptions.IconBitmapChangedException; import com.mapbox.mapboxsdk.exceptions.InvalidAccessTokenException; +import com.mapbox.mapboxsdk.exceptions.TelemetryServiceNotConfiguredException; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.layers.CustomLayer; +import com.mapbox.mapboxsdk.maps.widgets.CompassView; +import com.mapbox.mapboxsdk.maps.widgets.UserLocationView; +import com.mapbox.mapboxsdk.telemetry.MapboxEvent; +import com.mapbox.mapboxsdk.telemetry.MapboxEventManager; import com.mapbox.mapboxsdk.utils.ApiAccess; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; +import java.util.Hashtable; +import java.util.Iterator; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; /** * <p> @@ -111,7 +119,6 @@ public class MapView extends FrameLayout { private static final float DIMENSION_SEVENTYSIX_DP = 76f; private MapboxMap mMapboxMap; - private List<Annotation> mAnnotations; private List<Icon> mIcons; private NativeMapView mNativeMapView; @@ -120,7 +127,7 @@ public class MapView extends FrameLayout { private ImageView mAttributionsView; private UserLocationView mUserLocationView; - private List<OnMapChangedListener> mOnMapChangedListener; + private CopyOnWriteArrayList<OnMapChangedListener> mOnMapChangedListener; private ZoomButtonsController mZoomButtonsController; private ConnectivityReceiver mConnectivityReceiver; private float mScreenDensity = 1.0f; @@ -133,13 +140,12 @@ public class MapView extends FrameLayout { private boolean mTwoTap = false; private boolean mZoomStarted = false; private boolean mQuickZoom = false; + private boolean mScrollInProgress = false; - /* private int mContentPaddingLeft; private int mContentPaddingTop; private int mContentPaddingRight; private int mContentPaddingBottom; -*/ @UiThread public MapView(@NonNull Context context) { @@ -160,11 +166,9 @@ public class MapView extends FrameLayout { } private void initialize(@NonNull Context context, @Nullable AttributeSet attrs) { - mOnMapChangedListener = new ArrayList<>(); + mOnMapChangedListener = new CopyOnWriteArrayList<>(); mMapboxMap = new MapboxMap(this); - mAnnotations = new ArrayList<>(); mIcons = new ArrayList<>(); - View view = LayoutInflater.from(context).inflate(R.layout.mapview_internal, this); if (!isInEditMode()) { @@ -200,7 +204,7 @@ public class MapView extends FrameLayout { // Shows the zoom controls if (!context.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)) { - mMapboxMap.setZoomControlsEnabled(true); + mMapboxMap.getUiSettings().setZoomControlsEnabled(true); } mZoomButtonsController = new ZoomButtonsController(this); mZoomButtonsController.setZoomSpeed(MapboxConstants.ANIMATION_DURATION); @@ -210,9 +214,9 @@ public class MapView extends FrameLayout { onConnectivityChanged(isConnected()); mUserLocationView = (UserLocationView) view.findViewById(R.id.userLocationView); - mUserLocationView.setMapView(this); + mUserLocationView.setMapboxMap(mMapboxMap); mCompassView = (CompassView) view.findViewById(R.id.compassView); - mCompassView.setOnClickListener(new CompassView.CompassClickListener(this)); + mCompassView.setOnClickListener(new CompassView.CompassClickListener(mMapboxMap)); mLogoView = (ImageView) view.findViewById(R.id.logoView); // Setup Attributions control @@ -232,13 +236,15 @@ public class MapView extends FrameLayout { mMapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); // Access token - if (typedArray.getString(R.styleable.MapView_access_token) != null) { + String accessToken = typedArray.getString(R.styleable.MapView_access_token); + if (accessToken != null) { setAccessToken(typedArray.getString(R.styleable.MapView_access_token)); } // Style url - if (typedArray.getString(R.styleable.MapView_style_url) != null) { - mMapboxMap.setStyleUrl(typedArray.getString(R.styleable.MapView_style_url)); + String styleUrl = typedArray.getString(R.styleable.MapView_style_url); + if (styleUrl != null) { + mMapboxMap.setStyleUrl(styleUrl); } // Enable gestures @@ -249,6 +255,10 @@ public class MapView extends FrameLayout { uiSettings.setTiltGesturesEnabled(typedArray.getBoolean(R.styleable.MapView_tilt_enabled, true)); uiSettings.setZoomControlsEnabled(typedArray.getBoolean(R.styleable.MapView_zoom_controls_enabled, false)); + // Zoom + uiSettings.setMaxZoom(typedArray.getFloat(R.styleable.MapView_zoom_max, (float) MapboxConstants.MAXIMUM_ZOOM)); + uiSettings.setMinZoom(typedArray.getFloat(R.styleable.MapView_zoom_min, (float) MapboxConstants.MINIMUM_ZOOM)); + // Compass uiSettings.setCompassEnabled(typedArray.getBoolean(R.styleable.MapView_compass_enabled, true)); uiSettings.setCompassGravity(typedArray.getInt(R.styleable.MapView_compass_gravity, Gravity.TOP | Gravity.END)); @@ -274,13 +284,7 @@ public class MapView extends FrameLayout { , (int) (typedArray.getDimension(R.styleable.MapView_attribution_margin_bottom, DIMENSION_SEVEN_DP) * mScreenDensity)); // User location - try { - //noinspection ResourceType - mMapboxMap.setMyLocationEnabled(typedArray.getBoolean(R.styleable.MapView_my_location_enabled, false)); - } catch (SecurityException ignore) { - // User did not accept location permissions - } - + mMapboxMap.setMyLocationEnabled(typedArray.getBoolean(R.styleable.MapView_my_location_enabled, false)); } finally { typedArray.recycle(); } @@ -303,7 +307,7 @@ public class MapView extends FrameLayout { */ @UiThread public void onCreate(@Nullable Bundle savedInstanceState) { - if (savedInstanceState != null) { + if (savedInstanceState != null && savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) { // Get previous camera position CameraPosition cameraPosition = savedInstanceState.getParcelable(MapboxConstants.STATE_CAMERA_POSITION); @@ -357,10 +361,17 @@ public class MapView extends FrameLayout { // User did not accept location permissions } + TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings(); //noinspection ResourceType - mMapboxMap.setMyLocationTrackingMode(savedInstanceState.getInt(MapboxConstants.STATE_MY_LOCATION_TRACKING_MODE, MyLocationTracking.TRACKING_NONE)); + trackingSettings.setMyLocationTrackingMode(savedInstanceState.getInt(MapboxConstants.STATE_MY_LOCATION_TRACKING_MODE, MyLocationTracking.TRACKING_NONE)); //noinspection ResourceType - mMapboxMap.setMyBearingTrackingMode(savedInstanceState.getInt(MapboxConstants.STATE_MY_BEARING_TRACKING_MODE, MyBearingTracking.NONE)); + trackingSettings.setMyBearingTrackingMode(savedInstanceState.getInt(MapboxConstants.STATE_MY_BEARING_TRACKING_MODE, MyBearingTracking.NONE)); + } else { + // Force a check for Telemetry + validateTelemetryServiceConfigured(); + + // Start Telemetry (authorization determined in initial MapboxEventManager constructor) + MapboxEventManager.getMapboxEventManager(getContext()).isTelemetryEnabled(); } // Force a check for an access token @@ -381,6 +392,16 @@ public class MapView extends FrameLayout { } } }); + + // Fire MapLoad + if (savedInstanceState == null) { + Hashtable<String, Object> evt = new Hashtable<>(); + evt.put(MapboxEvent.ATTRIBUTE_EVENT, MapboxEvent.TYPE_MAP_LOAD); + evt.put(MapboxEvent.KEY_LATITUDE, mMapboxMap.getCameraPosition().target.getLatitude()); + evt.put(MapboxEvent.KEY_LONGITUDE, mMapboxMap.getCameraPosition().target.getLongitude()); + evt.put(MapboxEvent.KEY_ZOOM, mMapboxMap.getCameraPosition().zoom); + MapboxEventManager.getMapboxEventManager(getContext()).pushEvent(evt); + } } /** @@ -392,14 +413,19 @@ public class MapView extends FrameLayout { @UiThread public void onSaveInstanceState(@NonNull Bundle outState) { + outState.putBoolean(MapboxConstants.STATE_HAS_SAVED_STATE, true); outState.putParcelable(MapboxConstants.STATE_CAMERA_POSITION, mMapboxMap.getCameraPosition()); outState.putBoolean(MapboxConstants.STATE_DEBUG_ACTIVE, mMapboxMap.isDebugActive()); outState.putString(MapboxConstants.STATE_STYLE_URL, mMapboxMap.getStyleUrl()); outState.putString(MapboxConstants.STATE_ACCESS_TOKEN, mMapboxMap.getAccessToken()); outState.putLong(MapboxConstants.STATE_DEFAULT_TRANSITION_DURATION, mNativeMapView.getDefaultTransitionDuration()); outState.putBoolean(MapboxConstants.STATE_MY_LOCATION_ENABLED, mMapboxMap.isMyLocationEnabled()); - outState.putInt(MapboxConstants.STATE_MY_LOCATION_TRACKING_MODE, mMapboxMap.getMyLocationTrackingMode()); - outState.putInt(MapboxConstants.STATE_MY_BEARING_TRACKING_MODE, mMapboxMap.getMyBearingTrackingMode()); + + + // TrackingSettings + TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings(); + outState.putInt(MapboxConstants.STATE_MY_LOCATION_TRACKING_MODE, trackingSettings.getMyLocationTrackingMode()); + outState.putInt(MapboxConstants.STATE_MY_BEARING_TRACKING_MODE, trackingSettings.getMyBearingTrackingMode()); // UiSettings UiSettings uiSettings = mMapboxMap.getUiSettings(); @@ -554,13 +580,13 @@ public class MapView extends FrameLayout { } // - // Rotation + // Direction // /** - * Returns the current heading of the map relative to true north. + * Returns the current direction of the map relative to true north. * - * @return The current heading measured in degrees. + * @return The current direction measured in degrees. */ @UiThread @FloatRange(from = MapboxConstants.MINIMUM_DIRECTION, to = MapboxConstants.MAXIMUM_DIRECTION) @@ -633,6 +659,46 @@ public class MapView extends FrameLayout { } // + // Content padding + // + + /** + * Return The current content padding left of the map view viewport. + * + * @return The current content padding left + */ + int getContentPaddingLeft() { + return mContentPaddingLeft; + } + + /** + * Return The current content padding left of the map view viewport. + * + * @return The current content padding left + */ + int getContentPaddingTop() { + return mContentPaddingTop; + } + + /** + * Return The current content padding left of the map view viewport. + * + * @return The current content padding right + */ + int getContentPaddingRight() { + return mContentPaddingRight; + } + + /** + * Return The current content padding left of the map view viewport. + * + * @return The current content padding bottom + */ + int getContentPaddingBottom() { + return mContentPaddingBottom; + } + + // // Zoom // @@ -647,51 +713,6 @@ public class MapView extends FrameLayout { return mNativeMapView.getZoom(); } -// /** -// * Return The current content padding left of the map view viewport. -// * -// * @return The current content padding left -// */ -///* -// public int getContentPaddingLeft() { -// return mContentPaddingLeft; -// } -//*/ -// -// /** -// * Return The current content padding left of the map view viewport. -// * -// * @return The current content padding left -// */ -///* -// public int getContentPaddingTop() { -// return mContentPaddingTop; -// } -//*/ -// -// /** -// * Return The current content padding left of the map view viewport. -// * -// * @return The current content padding left -// */ -///* -// public int getContentPaddingRight() { -// return mContentPaddingRight; -// } -//*/ -// -// /** -// * Return The current content padding left of the map view viewport. -// * -// * @param zoomLevel The new zoom level. -// * @param animated If true, animates the change. If false, immediately changes the map. -// * @see MapboxMap#MAXIMUM_ZOOM -// */ -///* -// public int getContentPaddingBottom() { -// return mContentPaddingBottom; -//*/ - /** * <p> * Sets the minimum zoom level the map can be displayed at. @@ -712,7 +733,7 @@ public class MapView extends FrameLayout { * @return The minimum zoom level. */ @UiThread - public double getMinZoom() { + double getMinZoom() { return mNativeMapView.getMinZoom(); } @@ -724,7 +745,7 @@ public class MapView extends FrameLayout { * @param maxZoom The new maximum zoom level. */ @UiThread - public void setMaxZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double maxZoom) { + void setMaxZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double maxZoom) { mNativeMapView.setMaxZoom(maxZoom); } @@ -736,7 +757,7 @@ public class MapView extends FrameLayout { * @return The maximum zoom level. */ @UiThread - public double getMaxZoom() { + double getMaxZoom() { return mNativeMapView.getMaxZoom(); } @@ -866,6 +887,28 @@ public class MapView extends FrameLayout { } } + // Checks that TelemetryService has been configured by developer + private void validateTelemetryServiceConfigured() { + + try { + // Check Implementing app's AndroidManifest.xml + PackageInfo packageInfo = getContext().getPackageManager().getPackageInfo(getContext().getPackageName(), PackageManager.GET_SERVICES); + + if (packageInfo.services != null) { + + for (ServiceInfo service : packageInfo.services) { + if (TextUtils.equals("com.mapbox.mapboxsdk.telemetry.TelemetryService", service.name)) { + return; + } + } + } + + } catch (Exception e) { + Log.w(TAG, "Error checking for Telemetry Service Config: " + e); + } + throw new TelemetryServiceNotConfiguredException(); + } + /** * <p> * Sets the current Mapbox access token used to load map styles and tiles. @@ -942,7 +985,25 @@ public class MapView extends FrameLayout { // Annotations // - private void loadIcon(Icon icon) { + Icon loadIconForMarker(Marker marker) { + Icon icon = marker.getIcon(); + if (icon == null) { + icon = IconFactory.getInstance(getContext()).defaultMarker(); + marker.setIcon(icon); + } + if (!mIcons.contains(icon)) { + mIcons.add(icon); + loadIcon(icon); + } else { + Icon oldIcon = mIcons.get(mIcons.indexOf(icon)); + if (!oldIcon.getBitmap().sameAs(icon.getBitmap())) { + throw new IconBitmapChangedException(); + } + } + return icon; + } + + void loadIcon(Icon icon) { Bitmap bitmap = icon.getBitmap(); String id = icon.getId(); if (bitmap.getConfig() != Bitmap.Config.ARGB_8888) { @@ -964,7 +1025,7 @@ public class MapView extends FrameLayout { scale, buffer.array()); } - private void reloadIcons() { + void reloadIcons() { int count = mIcons.size(); for (int i = 0; i < count; i++) { Icon icon = mIcons.get(i); @@ -972,8 +1033,35 @@ public class MapView extends FrameLayout { } } + /** + * <p> + * Updates a marker on this map. Does nothing if the marker is already added. + * </p> + * + * @param updatedMarker An updated marker object. + */ + @UiThread + void updateMarker(@NonNull Marker updatedMarker) { + if (updatedMarker == null) { + Log.w(TAG, "marker was null, doing nothing"); + return; + } + + if (updatedMarker.getId() == -1) { + Log.w(TAG, "marker has an id of -1, possibly was not added yet, doing nothing"); + } + + ensureIconLoaded(updatedMarker); + mNativeMapView.updateMarker(updatedMarker); + } + private Marker prepareMarker(MarkerOptions markerOptions) { Marker marker = markerOptions.getMarker(); + ensureIconLoaded(marker); + return marker; + } + + private void ensureIconLoaded(Marker marker) { Icon icon = marker.getIcon(); if (icon == null) { icon = IconFactory.getInstance(getContext()).defaultMarker(); @@ -988,270 +1076,48 @@ public class MapView extends FrameLayout { throw new IconBitmapChangedException(); } } - marker.setTopOffsetPixels(getTopOffsetPixelsForIcon(icon)); - return marker; - } - /** - * <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 - Marker addMarker(@NonNull MarkerOptions markerOptions) { - if (markerOptions == null) { - Log.w(TAG, "markerOptions was null, so just returning null"); - return null; + // 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) mMapboxMap.getAnnotation(marker.getId()) : null; + if (previousMarker == null || previousMarker.getIcon() == null || previousMarker.getIcon() != marker.getIcon()) { + marker.setTopOffsetPixels(getTopOffsetPixelsForIcon(icon)); } - - Marker marker = prepareMarker(markerOptions); - long id = mNativeMapView.addMarker(marker); - marker.setId(id); // the annotation needs to know its id - marker.setMapboxMap(mMapboxMap); // the annotation needs to know which map view it is in - mAnnotations.add(marker); - return marker; } - /** - * <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 - List<Marker> addMarkers(@NonNull List<MarkerOptions> markerOptionsList) { - if (markerOptionsList == null) { - Log.w(TAG, "markerOptionsList was null, so just returning null"); - return null; - } - - int count = markerOptionsList.size(); - List<Marker> markers = new ArrayList<>(count); - for (int i = 0; i < count; i++) { - MarkerOptions markerOptions = markerOptionsList.get(i); - Marker marker = prepareMarker(markerOptions); - markers.add(marker); - } - - long[] ids = mNativeMapView.addMarkers(markers); - Marker m; - for (int i = 0; i < count; i++) { - m = markers.get(i); - m.setId(ids[i]); - m.setMapboxMap(mMapboxMap); - mAnnotations.add(m); + long addMarker(@NonNull Marker marker) { + if (mNativeMapView == null) { + return 0l; } - - return new ArrayList<>(markers); + return mNativeMapView.addMarker(marker); } - /** - * 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 - Polyline addPolyline(@NonNull PolylineOptions polylineOptions) { - if (polylineOptions == null) { - Log.w(TAG, "polylineOptions was null, so just returning null"); - return null; - } - - Polyline polyline = polylineOptions.getPolyline(); - long id = mNativeMapView.addPolyline(polyline); - polyline.setId(id); - polyline.setMapboxMap(mMapboxMap); - mAnnotations.add(polyline); - return polyline; + long[] addMarkers(@NonNull List<Marker> markerList) { + return mNativeMapView.addMarkers(markerList); } - /** - * 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 - List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList) { - if (polylineOptionsList == null) { - Log.w(TAG, "polylineOptionsList was null, so just returning null"); - return null; - } - - int count = polylineOptionsList.size(); - List<Polyline> polylines = new ArrayList<>(count); - for (PolylineOptions options : polylineOptionsList) { - polylines.add(options.getPolyline()); - } - - long[] ids = mNativeMapView.addPolylines(polylines); - - Polyline p; - for (int i = 0; i < count; i++) { - p = polylines.get(i); - p.setId(ids[i]); - p.setMapboxMap(mMapboxMap); - mAnnotations.add(p); - } - - return new ArrayList<>(polylines); + long addPolyline(@NonNull Polyline polyline) { + return mNativeMapView.addPolyline(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 - Polygon addPolygon(@NonNull PolygonOptions polygonOptions) { - if (polygonOptions == null) { - Log.w(TAG, "polygonOptions was null, so just returning null"); - return null; - } - - Polygon polygon = polygonOptions.getPolygon(); - long id = mNativeMapView.addPolygon(polygon); - polygon.setId(id); - polygon.setMapboxMap(mMapboxMap); - mAnnotations.add(polygon); - return polygon; + long[] addPolylines(@NonNull List<Polyline> polylines) { + return mNativeMapView.addPolylines(polylines); } - - /** - * 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 - List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList) { - if (polygonOptionsList == null) { - Log.w(TAG, "polygonOptionsList was null, so just returning null"); - return null; - } - - int count = polygonOptionsList.size(); - List<Polygon> polygons = new ArrayList<>(count); - for (PolygonOptions polygonOptions : polygonOptionsList) { - polygons.add(polygonOptions.getPolygon()); - } - - long[] ids = mNativeMapView.addPolygons(polygons); - - Polygon p; - for (int i = 0; i < count; i++) { - p = polygons.get(i); - p.setId(ids[i]); - p.setMapboxMap(mMapboxMap); - mAnnotations.add(p); - } - - return new ArrayList<>(polygons); + long addPolygon(@NonNull Polygon polygon) { + return mNativeMapView.addPolygon(polygon); } - - /** - * <p> - * Convenience method for removing a Marker from the map. - * </p> - * Calls removeAnnotation() internally - * - * @param marker Marker to remove - */ - @UiThread - void removeMarker(@NonNull Marker marker) { - removeAnnotation(marker); + long[] addPolygons(@NonNull List<Polygon> polygons) { + return mNativeMapView.addPolygons(polygons); } - /** - * Removes an annotation from the map. - * - * @param annotation The annotation object to remove. - */ - @UiThread - void removeAnnotation(@NonNull Annotation annotation) { - if (annotation == null) { - Log.w(TAG, "annotation was null, so just returning"); - return; - } - - if (annotation instanceof Marker) { - ((Marker) annotation).hideInfoWindow(); - } - long id = annotation.getId(); + void removeAnnotation(long id) { mNativeMapView.removeAnnotation(id); - mAnnotations.remove(annotation); - } - - /** - * Removes multiple annotations from the map. - * - * @param annotationList A list of annotation objects to remove. - */ - @UiThread - void removeAnnotations(@NonNull List<? extends Annotation> annotationList) { - if (annotationList == null) { - Log.w(TAG, "annotationList was null, so just returning"); - return; - } - - int count = annotationList.size(); - long[] ids = new long[count]; - for (int i = 0; i < count; i++) { - ids[i] = annotationList.get(i).getId(); - } - mNativeMapView.removeAnnotations(ids); } - /** - * Removes all annotations from the map. - */ - @UiThread - void removeAllAnnotations() { - int count = mAnnotations.size(); - long[] ids = new long[mAnnotations.size()]; - - for (int i = 0; i < count; i++) { - Annotation annotation = mAnnotations.get(i); - long id = annotation.getId(); - ids[i] = id; - if (annotation instanceof Marker) { - ((Marker) annotation).hideInfoWindow(); - } - } - + void removeAnnotations(@NonNull long[] ids) { mNativeMapView.removeAnnotations(ids); - mAnnotations.clear(); - } - - /** - * 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 - List<Annotation> getAllAnnotations() { - return new ArrayList<>(mAnnotations); } private List<Marker> getMarkersInBounds(@NonNull LatLngBounds bbox) { @@ -1269,9 +1135,10 @@ public class MapView extends FrameLayout { } List<Marker> annotations = new ArrayList<>(ids.length); - int count = mAnnotations.size(); + List<Annotation> annotationList = mMapboxMap.getAnnotations(); + int count = annotationList.size(); for (int i = 0; i < count; i++) { - Annotation annotation = mAnnotations.get(i); + Annotation annotation = annotationList.get(i); if (annotation instanceof Marker && idsList.contains(annotation.getId())) { annotations.add((Marker) annotation); } @@ -1280,7 +1147,7 @@ public class MapView extends FrameLayout { return new ArrayList<>(annotations); } - private int getTopOffsetPixelsForIcon(Icon icon) { + int getTopOffsetPixelsForIcon(Icon icon) { // This method will dead lock if map paused. Causes a freeze if you add a marker in an // activity's onCreate() if (mNativeMapView.isPaused()) { @@ -1292,6 +1159,35 @@ public class MapView extends FrameLayout { } /** + * Sets the distance from the edges of the map view’s frame to the edges of the map + * view’s logical viewport. + * <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. + * + * @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 + void setContentPadding(int left, int top, int right, int bottom) { + if (left == mContentPaddingLeft && top == mContentPaddingTop && right == mContentPaddingRight && bottom == mContentPaddingBottom) { + return; + } + + mContentPaddingLeft = left; + mContentPaddingTop = top; + mContentPaddingRight = right; + mContentPaddingBottom = bottom; + + mNativeMapView.setContentPadding(top / mScreenDensity, left / mScreenDensity, bottom / mScreenDensity, right / mScreenDensity); + } + + /** * <p> * Returns the distance spanned by one pixel at the specified latitude and current zoom level. * </p> @@ -1400,9 +1296,10 @@ public class MapView extends FrameLayout { } private void adjustTopOffsetPixels() { - int count = mAnnotations.size(); + List<Annotation> annotations = mMapboxMap.getAnnotations(); + int count = annotations.size(); for (int i = 0; i < count; i++) { - Annotation annotation = mAnnotations.get(i); + Annotation annotation = annotations.get(i); if (annotation instanceof Marker) { Marker marker = (Marker) annotation; marker.setTopOffsetPixels( @@ -1419,9 +1316,10 @@ public class MapView extends FrameLayout { } private void reloadMarkers() { - int count = mAnnotations.size(); + List<Annotation> annotations = mMapboxMap.getAnnotations(); + int count = annotations.size(); for (int i = 0; i < count; i++) { - Annotation annotation = mAnnotations.get(i); + Annotation annotation = annotations.get(i); if (annotation instanceof Marker) { Marker marker = (Marker) annotation; mNativeMapView.removeAnnotation(annotation.getId()); @@ -1572,6 +1470,26 @@ public class MapView extends FrameLayout { // Touch events // + /** + * Helper method for tracking gesture events + * @param gestureId Type of Gesture See {@see MapboxEvent#GESTURE_SINGLETAP MapboxEvent#GESTURE_DOUBLETAP MapboxEvent#GESTURE_TWO_FINGER_SINGLETAP MapboxEvent#GESTURE_QUICK_ZOOM MapboxEvent#GESTURE_PAN_START MapboxEvent#GESTURE_PINCH_START MapboxEvent#GESTURE_ROTATION_START MapboxEvent#GESTURE_PITCH_START} + * @param xCoordinate Original x screen coordinate at start of gesture + * @param yCoordinate Original y screen cooridnate at start of gesture + */ + private void trackGestureEvent(@NonNull String gestureId, @NonNull float xCoordinate, float yCoordinate) { + + LatLng tapLatLng = fromScreenLocation(new PointF(xCoordinate, yCoordinate)); + + Hashtable<String, Object> evt = new Hashtable<>(); + evt.put(MapboxEvent.ATTRIBUTE_EVENT, MapboxEvent.TYPE_MAP_CLICK); + evt.put(MapboxEvent.KEY_GESTURE_ID, gestureId); + evt.put(MapboxEvent.KEY_LATITUDE, tapLatLng.getLatitude()); + evt.put(MapboxEvent.KEY_LONGITUDE, tapLatLng.getLongitude()); + evt.put(MapboxEvent.KEY_ZOOM, mMapboxMap.getCameraPosition().zoom); + + MapboxEventManager.getMapboxEventManager(getContext()).pushEvent(evt); + } + // Called when user touches the screen, all positions are absolute @Override public boolean onTouchEvent(@NonNull MotionEvent event) { @@ -1596,6 +1514,10 @@ public class MapView extends FrameLayout { case MotionEvent.ACTION_POINTER_DOWN: // Second pointer down mTwoTap = event.getPointerCount() == 2; + if (mTwoTap) { + // Confirmed 2nd Finger Down + trackGestureEvent(MapboxEvent.GESTURE_TWO_FINGER_SINGLETAP, event.getX(), event.getY()); + } break; case MotionEvent.ACTION_POINTER_UP: @@ -1617,6 +1539,12 @@ public class MapView extends FrameLayout { return true; } + // Scroll / Pan Has Stopped + if (mScrollInProgress) { + trackGestureEvent(MapboxEvent.TYPE_MAP_DRAGEND, event.getX(), event.getY()); + mScrollInProgress = false; + } + mTwoTap = false; mNativeMapView.setGestureInProgress(false); break; @@ -1632,8 +1560,7 @@ public class MapView extends FrameLayout { } // This class handles one finger gestures - private class GestureListener extends - GestureDetector.SimpleOnGestureListener { + private class GestureListener extends GestureDetector.SimpleOnGestureListener { // Must always return true otherwise all events are ignored @Override @@ -1665,16 +1592,20 @@ public class MapView extends FrameLayout { } // Single finger double tap - if (mUserLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE) { + if (mMapboxMap.getTrackingSettings().isLocationTrackingDisabled()) { // Zoom in on gesture zoom(true, e.getX(), e.getY()); + trackGestureEvent(MapboxEvent.GESTURE_QUICK_ZOOM, e.getX(), e.getY()); } else { - // Zoom in on center map - zoom(true, getWidth() / 2, getHeight() / 2); + // Zoom in on user location view + PointF centerPoint = mUserLocationView.getMarkerScreenPoint(); + zoom(true, centerPoint.x, centerPoint.y); } break; } + trackGestureEvent(MapboxEvent.GESTURE_DOUBLETAP, e.getX(), e.getY()); + return true; } @@ -1725,9 +1656,10 @@ public class MapView extends FrameLayout { } if (newSelectedMarkerId >= 0) { - int count = mAnnotations.size(); + List<Annotation> annotations = mMapboxMap.getAnnotations(); + int count = annotations.size(); for (int i = 0; i < count; i++) { - Annotation annotation = mAnnotations.get(i); + Annotation annotation = annotations.get(i); if (annotation instanceof Marker) { if (annotation.getId() == newSelectedMarkerId) { if (selectedMarkers.isEmpty() || !selectedMarkers.contains(annotation)) { @@ -1749,6 +1681,8 @@ public class MapView extends FrameLayout { } } + trackGestureEvent(MapboxEvent.GESTURE_SINGLETAP, e.getX(), e.getY()); + return true; } @@ -1771,7 +1705,9 @@ public class MapView extends FrameLayout { } // reset tracking modes if gesture occurs - resetTrackingModes(); + if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) { + resetTrackingModes(); + } // Fling the map float ease = 0.25f; @@ -1793,18 +1729,25 @@ public class MapView extends FrameLayout { listener.onFling(); } + trackGestureEvent(MapboxEvent.GESTURE_PAN_START, e1.getX(), e1.getY()); return true; } // Called for drags @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + Log.i(TAG, "onScroll() started"); + if (!mScrollInProgress) { + mScrollInProgress = true; + } if (!mMapboxMap.getUiSettings().isScrollGesturesEnabled()) { return false; } - // reset tracking modes if gesture occurs - resetTrackingModes(); + if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) { + // reset tracking modes if gesture occurs + resetTrackingModes(); + } // Cancel any animation mNativeMapView.cancelTransitions(); @@ -1817,6 +1760,7 @@ public class MapView extends FrameLayout { listener.onScroll(); } + Log.i(TAG, "onScroll() done"); return true; } } @@ -1834,10 +1778,13 @@ public class MapView extends FrameLayout { return false; } - // reset tracking modes if gesture occurs - resetTrackingModes(); + if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) { + // reset tracking modes if gesture occurs + resetTrackingModes(); + } mBeginTime = detector.getEventTime(); + trackGestureEvent(MapboxEvent.GESTURE_PINCH_START, detector.getFocusX(), detector.getFocusY()); return true; } @@ -1853,7 +1800,8 @@ public class MapView extends FrameLayout { // Called for pinch zooms and quickzooms/quickscales @Override public boolean onScale(ScaleGestureDetector detector) { - if (!mMapboxMap.getUiSettings().isZoomGesturesEnabled()) { + UiSettings uiSettings = mMapboxMap.getUiSettings(); + if (!uiSettings.isZoomGesturesEnabled()) { return false; } @@ -1881,13 +1829,21 @@ public class MapView extends FrameLayout { // Gesture is a quickzoom if there aren't two fingers mQuickZoom = !mTwoTap; + TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings(); + // Scale the map - if (mMapboxMap.getUiSettings().isScrollGesturesEnabled() && !mQuickZoom && mUserLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE) { + if (uiSettings.isScrollGesturesEnabled() && !mQuickZoom && trackingSettings.isLocationTrackingDisabled()) { // around gesture mNativeMapView.scaleBy(detector.getScaleFactor(), detector.getFocusX() / mScreenDensity, detector.getFocusY() / mScreenDensity); } else { - // around center map - mNativeMapView.scaleBy(detector.getScaleFactor(), (getWidth() / 2) / mScreenDensity, (getHeight() / 2) / mScreenDensity); + if(trackingSettings.isLocationTrackingDisabled()) { + // around center map + mNativeMapView.scaleBy(detector.getScaleFactor(), (getWidth() / 2) / mScreenDensity, (getHeight() / 2) / mScreenDensity); + }else { + // around user location view + PointF centerPoint = mUserLocationView.getMarkerScreenPoint(); + mNativeMapView.scaleBy(detector.getScaleFactor(), centerPoint.x / mScreenDensity, centerPoint.y / mScreenDensity); + } } return true; } @@ -1907,10 +1863,13 @@ public class MapView extends FrameLayout { return false; } - // reset tracking modes if gesture occurs - resetTrackingModes(); + if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) { + // reset tracking modes if gesture occurs + resetTrackingModes(); + } mBeginTime = detector.getEventTime(); + trackGestureEvent(MapboxEvent.GESTURE_ROTATION_START, detector.getFocusX(), detector.getFocusY()); return true; } @@ -1957,16 +1916,15 @@ public class MapView extends FrameLayout { bearing += detector.getRotationDegreesDelta(); // Rotate the map - if (mUserLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE) { + if (mMapboxMap.getTrackingSettings().isLocationTrackingDisabled()) { // around gesture mNativeMapView.setBearing(bearing, detector.getFocusX() / mScreenDensity, detector.getFocusY() / mScreenDensity); } else { - // around center map - mNativeMapView.setBearing(bearing, - (getWidth() / 2) / mScreenDensity, - (getHeight() / 2) / mScreenDensity); + // around center userlocation + PointF centerPoint = mUserLocationView.getMarkerScreenPoint(); + mNativeMapView.setBearing(bearing, centerPoint.x / mScreenDensity, centerPoint.y / mScreenDensity); } return true; } @@ -1986,10 +1944,13 @@ public class MapView extends FrameLayout { return false; } - // reset tracking modes if gesture occurs - resetTrackingModes(); + if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) { + // reset tracking modes if gesture occurs + resetTrackingModes(); + } mBeginTime = detector.getEventTime(); + trackGestureEvent(MapboxEvent.GESTURE_PITCH_START, detector.getFocusX(), detector.getFocusY()); return true; } @@ -2056,8 +2017,6 @@ public class MapView extends FrameLayout { if (!mMapboxMap.getUiSettings().isZoomGesturesEnabled()) { return; } - - // Zoom in or out zoom(zoomIn); } } @@ -2408,9 +2367,11 @@ public class MapView extends FrameLayout { // Forward to any listeners protected void onMapChanged(int mapChange) { if (mOnMapChangedListener != null) { - int count = mOnMapChangedListener.size(); - for (int i = 0; i < count; i++) { - mOnMapChangedListener.get(i).onMapChanged(mapChange); + OnMapChangedListener listener; + final Iterator<OnMapChangedListener> iterator = mOnMapChangedListener.iterator(); + while (iterator.hasNext()) { + listener = iterator.next(); + listener.onMapChanged(mapChange); } } } @@ -2419,6 +2380,11 @@ public class MapView extends FrameLayout { // User location // + boolean isPermissionsAccepted() { + return (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) || + ContextCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; + } + /** * <p> * Enables or disables the my-location layer. @@ -2433,9 +2399,6 @@ public class MapView extends FrameLayout { * @throws SecurityException if no suitable permission is present */ @UiThread - @RequiresPermission(anyOf = { - Manifest.permission.ACCESS_COARSE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION}) void setMyLocationEnabled(boolean enabled) { mUserLocationView.setEnabled(enabled); } @@ -2473,19 +2436,13 @@ public class MapView extends FrameLayout { * 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 - @RequiresPermission(anyOf = { - Manifest.permission.ACCESS_COARSE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION}) void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) { if (myLocationTrackingMode != MyLocationTracking.TRACKING_NONE && !mMapboxMap.isMyLocationEnabled()) { - //noinspection ResourceType mMapboxMap.setMyLocationEnabled(true); } - mUserLocationView.setMyLocationTrackingMode(myLocationTrackingMode); MapboxMap.OnMyLocationTrackingModeChangeListener listener = mMapboxMap.getOnMyLocationTrackingModeChangeListener(); if (listener != null) { @@ -2494,19 +2451,6 @@ public class MapView extends FrameLayout { } /** - * 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 - int getMyLocationTrackingMode() { - return mUserLocationView.getMyLocationTrackingMode(); - } - - /** * <p> * Set the current my bearing tracking mode. * </p> @@ -2518,16 +2462,11 @@ public class MapView extends FrameLayout { * 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 - @RequiresPermission(anyOf = { - Manifest.permission.ACCESS_COARSE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION}) void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) { if (myBearingTrackingMode != MyBearingTracking.NONE && !mMapboxMap.isMyLocationEnabled()) { - //noinspection ResourceType mMapboxMap.setMyLocationEnabled(true); } mUserLocationView.setMyBearingTrackingMode(myBearingTrackingMode); @@ -2537,26 +2476,11 @@ public class MapView extends FrameLayout { } } - /** - * Returns the current user bearing tracking mode. - * See {@link MyBearingTracking} for possible return values. - * - * @return the current user bearing tracking mode. - * @see MyBearingTracking - */ - @UiThread - @MyLocationTracking.Mode - int getMyBearingTrackingMode() { - //noinspection ResourceType - return mUserLocationView.getMyBearingTrackingMode(); - } - private void resetTrackingModes() { try { - //noinspection ResourceType - setMyLocationTrackingMode(MyLocationTracking.TRACKING_NONE); - //noinspection ResourceType - setMyBearingTrackingMode(MyBearingTracking.NONE); + TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings(); + trackingSettings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_NONE); + trackingSettings.setMyBearingTrackingMode(MyBearingTracking.NONE); } catch (SecurityException ignore) { // User did not accept location permissions } @@ -2806,6 +2730,10 @@ public class MapView extends FrameLayout { private void setWidgetMargins(@NonNull final View view, int left, int top, int right, int bottom) { LayoutParams layoutParams = (LayoutParams) view.getLayoutParams(); + left += mContentPaddingLeft; + top += mContentPaddingTop; + right += mContentPaddingRight; + bottom += mContentPaddingBottom; layoutParams.setMargins(left, top, right, bottom); view.setLayoutParams(layoutParams); } @@ -2813,9 +2741,11 @@ public class MapView extends FrameLayout { 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 mMapView; public AttributionOnClickListener(MapView mapView) { + super(); mMapView = mapView; } @@ -2825,7 +2755,7 @@ public class MapView extends FrameLayout { Context context = v.getContext(); String[] items = context.getResources().getStringArray(R.array.attribution_names); AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.AttributionAlertDialogStyle); - builder.setTitle(R.string.attributionsDialogTitle); + builder.setTitle(R.string.mapbox_attributionsDialogTitle); builder.setAdapter(new ArrayAdapter<>(context, R.layout.attribution_list_item, items), this); builder.show(); } @@ -2833,7 +2763,49 @@ public class MapView extends FrameLayout { // Called when someone selects an attribution, 'Improve this map' adds location data to the url @Override public void onClick(DialogInterface dialog, int which) { - Context context = ((Dialog) dialog).getContext(); + final Context context = ((Dialog) dialog).getContext(); + if (which == ATTRIBUTION_INDEX_TELEMETRY_SETTINGS) { + + int array = R.array.attribution_telemetry_options; + if (MapboxEventManager.getMapboxEventManager(context).isTelemetryEnabled()) { + array = R.array.attribution_telemetry_options_already_participating; + } + String[] items = context.getResources().getStringArray(array); + AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.AttributionAlertDialogStyle); + builder.setTitle(R.string.mapbox_attributionTelemetryTitle); + LayoutInflater factory = LayoutInflater.from(context); + View content = factory.inflate(R.layout.attribution_telemetry_view, null); + + ListView lv = (ListView) content.findViewById(R.id.telemetryOptionsList); + lv.setAdapter(new ArrayAdapter<String>(context, R.layout.attribution_list_item, items)); + lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE); + + builder.setView(content); + final AlertDialog telemDialog = builder.show(); + lv.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + switch (position) { + case 0: + String url = context.getResources().getStringArray(R.array.attribution_links)[3]; + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(url)); + context.startActivity(intent); + telemDialog.cancel(); + return; + case 1: + MapboxEventManager.getMapboxEventManager(context).setTelemetryEnabled(false); + telemDialog.cancel(); + return; + case 2: + MapboxEventManager.getMapboxEventManager(context).setTelemetryEnabled(true); + telemDialog.cancel(); + return; + } + } + }); + return; + } String url = context.getResources().getStringArray(R.array.attribution_links)[which]; if (which == ATTRIBUTION_INDEX_IMPROVE_THIS_MAP) { LatLng latLng = mMapView.getMapboxMap().getCameraPosition().target; @@ -3044,4 +3016,5 @@ public class MapView extends FrameLayout { void onMapChanged(@MapChange int change); } + } 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 757a9bc3d9..40b75d3ce1 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 @@ -1,20 +1,21 @@ package com.mapbox.mapboxsdk.maps; -import android.Manifest; import android.content.Context; import android.location.Location; import android.os.Bundle; -import android.support.annotation.FloatRange; +import android.os.SystemClock; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.annotation.RequiresPermission; import android.support.annotation.UiThread; +import android.support.v4.util.LongSparseArray; import android.text.TextUtils; import android.util.Log; import android.view.View; import com.mapbox.mapboxsdk.annotations.Annotation; +import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; +import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.InfoWindow; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.MarkerOptions; @@ -23,6 +24,8 @@ import com.mapbox.mapboxsdk.annotations.PolygonOptions; import com.mapbox.mapboxsdk.annotations.Polyline; import com.mapbox.mapboxsdk.annotations.PolylineOptions; import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.camera.CameraUpdate; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.constants.MyBearingTracking; import com.mapbox.mapboxsdk.constants.MyLocationTracking; @@ -39,10 +42,12 @@ public class MapboxMap { private MapView mMapView; private UiSettings mUiSettings; + private TrackingSettings mTrackingSettings; private Projection mProjection; private CameraPosition mCameraPosition; private boolean mInvalidCameraPosition; private String mStyleUrl; + private LongSparseArray<Annotation> mAnnotations; private List<Marker> mSelectedMarkers; private List<InfoWindow> mInfoWindows; private MapboxMap.InfoWindowAdapter mInfoWindowAdapter; @@ -54,17 +59,22 @@ public class MapboxMap { private MapboxMap.OnMapLongClickListener mOnMapLongClickListener; private MapboxMap.OnMarkerClickListener mOnMarkerClickListener; private MapboxMap.OnInfoWindowClickListener mOnInfoWindowClickListener; + private MapboxMap.OnInfoWindowLongClickListener mOnInfoWindowLongClickListener; + private MapboxMap.OnInfoWindowCloseListener mOnInfoWindowCloseListener; private MapboxMap.OnFlingListener mOnFlingListener; private MapboxMap.OnScrollListener mOnScrollListener; private MapboxMap.OnMyLocationTrackingModeChangeListener mOnMyLocationTrackingModeChangeListener; private MapboxMap.OnMyBearingTrackingModeChangeListener mOnMyBearingTrackingModeChangeListener; private MapboxMap.OnFpsChangedListener mOnFpsChangedListener; + private MapboxMap.OnCameraChangeListener mOnCameraChangeListener; MapboxMap(@NonNull MapView mapView) { mMapView = mapView; mMapView.addOnMapChangedListener(new MapChangeCameraPositionListener()); mUiSettings = new UiSettings(mapView); + mTrackingSettings = new TrackingSettings(mMapView, mUiSettings); mProjection = new Projection(mapView); + mAnnotations = new LongSparseArray<>(); mSelectedMarkers = new ArrayList<>(); mInfoWindows = new ArrayList<>(); } @@ -83,6 +93,19 @@ public class MapboxMap { } // + // TrackingSettings + // + + /** + * Gets the tracking interface settings for the map. + * + * @return + */ + public TrackingSettings getTrackingSettings() { + return mTrackingSettings; + } + + // // Projection // @@ -105,14 +128,7 @@ public class MapboxMap { */ public final CameraPosition getCameraPosition() { if (mInvalidCameraPosition) { - // Camera position has changed, need to regenerate position - mCameraPosition = new CameraPosition.Builder(true) - .bearing((float) mMapView.getBearing()) - .target(mMapView.getLatLng()) - .tilt((float) mMapView.getTilt()) - .zoom((float) mMapView.getZoom()) - .build(); - mInvalidCameraPosition = false; + invalidateCameraPosition(); } return mCameraPosition; } @@ -137,8 +153,26 @@ public class MapboxMap { */ @UiThread public final void moveCamera(CameraUpdate update) { + moveCamera(update, 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, MapboxMap.CancelableCallback callback) { mCameraPosition = update.getCameraPosition(this); mMapView.jumpTo(mCameraPosition.bearing, mCameraPosition.target, mCameraPosition.tilt, mCameraPosition.zoom); + if (mOnCameraChangeListener != null) { + mOnCameraChangeListener.onCameraChange(mCameraPosition); + } + if (callback != null) { + callback.onFinish(); + } } /** @@ -175,7 +209,25 @@ public class MapboxMap { @UiThread public final void easeCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) { mCameraPosition = update.getCameraPosition(this); - mMapView.easeTo(mCameraPosition.bearing, mCameraPosition.target, getDurationNano(durationMs), mCameraPosition.tilt, mCameraPosition.zoom, callback); + mMapView.easeTo(mCameraPosition.bearing, mCameraPosition.target, getDurationNano(durationMs), mCameraPosition.tilt, mCameraPosition.zoom, new CancelableCallback() { + @Override + public void onCancel() { + if (callback != null) { + callback.onCancel(); + } + } + + @Override + public void onFinish() { + if (mOnCameraChangeListener != null) { + mOnCameraChangeListener.onCameraChange(mCameraPosition); + } + + if (callback != null) { + callback.onFinish(); + } + } + }); } /** @@ -227,7 +279,25 @@ public class MapboxMap { @UiThread public final void animateCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) { mCameraPosition = update.getCameraPosition(this); - mMapView.flyTo(mCameraPosition.bearing, mCameraPosition.target, getDurationNano(durationMs), mCameraPosition.tilt, mCameraPosition.zoom, callback); + mMapView.flyTo(mCameraPosition.bearing, mCameraPosition.target, getDurationNano(durationMs), mCameraPosition.tilt, mCameraPosition.zoom, new CancelableCallback() { + @Override + public void onCancel() { + if (callback != null) { + callback.onCancel(); + } + } + + @Override + public void onFinish() { + if (mOnCameraChangeListener != null) { + mOnCameraChangeListener.onCameraChange(mCameraPosition); + } + + if (callback != null) { + callback.onFinish(); + } + } + }); } // internal time layer conversion @@ -235,75 +305,34 @@ public class MapboxMap { return durationMs > 0 ? TimeUnit.NANOSECONDS.convert(durationMs, TimeUnit.MILLISECONDS) : 0; } - // - // ZOOM - // - - /** - * <p> - * Sets the minimum zoom level the map can be displayed at. - * </p> - * - * @param minZoom The new minimum zoom level. - */ - @UiThread - public void setMinZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double minZoom) { - if ((minZoom < MapboxConstants.MINIMUM_ZOOM) || (minZoom > MapboxConstants.MAXIMUM_ZOOM)) { - Log.e(MapboxConstants.TAG, "Not setting minZoom, value is in unsupported range: " + minZoom); - return; + private void invalidateCameraPosition() { + mInvalidCameraPosition = false; + mCameraPosition = new CameraPosition.Builder(true) + .bearing((float) mMapView.getBearing()) + .target(mMapView.getLatLng()) + .tilt((float) mMapView.getTilt()) + .zoom((float) mMapView.getZoom()) + .build(); + if (mOnCameraChangeListener != null) { + mOnCameraChangeListener.onCameraChange(mCameraPosition); } - mMapView.setMinZoom(minZoom); } - /** - * <p> - * Gets the maximum zoom level the map can be displayed at. - * </p> - * - * @return The minimum zoom level. - */ - @UiThread - public double getMinZoom() { - return mMapView.getMinZoom(); - } - - /** - * <p> - * Sets the maximum zoom level the map can be displayed at. - * </p> - * - * @param maxZoom The new maximum zoom level. - */ - @UiThread - public void setMaxZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double maxZoom) { - if ((maxZoom < MapboxConstants.MINIMUM_ZOOM) || (maxZoom > MapboxConstants.MAXIMUM_ZOOM)) { - Log.e(MapboxConstants.TAG, "Not setting maxZoom, value is in unsupported range: " + maxZoom); - return; - } - mMapView.setMaxZoom(maxZoom); - } + // + // Reset North + // /** - * <p> - * Gets the maximum zoom level the map can be displayed at. - * </p> * - * @return The maximum zoom level. */ - @UiThread - public double getMaxZoom() { - return mMapView.getMaxZoom(); + public void resetNorth() { + mMapView.resetNorth(); } // // Manual zoom controls // - // used by UiSettings - void setZoomControlsEnabled(boolean enabled) { - mMapView.setZoomControlsEnabled(enabled); - } - // // Debug // @@ -468,7 +497,17 @@ public class MapboxMap { @UiThread @NonNull public Marker addMarker(@NonNull MarkerOptions markerOptions) { - return mMapView.addMarker(markerOptions); + return addMarker((BaseMarkerOptions) markerOptions); + } + + @UiThread + @NonNull + public Marker addMarker(@NonNull BaseMarkerOptions markerOptions) { + Marker marker = prepareMarker(markerOptions); + long id = mMapView.addMarker(marker); + marker.setId(id); + mAnnotations.put(id, marker); + return marker; } /** @@ -484,7 +523,50 @@ public class MapboxMap { @UiThread @NonNull public List<Marker> addMarkers(@NonNull List<MarkerOptions> markerOptionsList) { - return mMapView.addMarkers(markerOptionsList); + int count = markerOptionsList.size(); + List<Marker> markers = new ArrayList<>(count); + MarkerOptions markerOptions; + Marker marker; + for (int i = 0; i < count; i++) { + markerOptions = markerOptionsList.get(i); + marker = prepareMarker(markerOptions); + markers.add(marker); + } + + long[] ids = mMapView.addMarkers(markers); + long id = 0; + Marker m; + + for (int i = 0; i < markers.size(); i++) { + m = markers.get(i); + m.setMapboxMap(this); + if (ids != null) { + id = ids[i]; + } else { + //unit test + id++; + } + m.setId(id); + mAnnotations.put(id, m); + } + return markers; + } + + /** + * <p> + * Updates a marker on this map. Does nothing if the marker is already added. + * </p> + * + * @param updatedMarker An updated marker object. + */ + @UiThread + public void updateMarker(@NonNull Marker updatedMarker) { + mMapView.updateMarker(updatedMarker); + + int index = mAnnotations.indexOfKey(updatedMarker.getId()); + if (index > -1) { + mAnnotations.setValueAt(index, updatedMarker); + } } /** @@ -496,7 +578,14 @@ public class MapboxMap { @UiThread @NonNull public Polyline addPolyline(@NonNull PolylineOptions polylineOptions) { - return mMapView.addPolyline(polylineOptions); + Polyline polyline = polylineOptions.getPolyline(); + if (!polyline.getPoints().isEmpty()) { + long id = mMapView.addPolyline(polyline); + polyline.setMapboxMap(this); + polyline.setId(id); + mAnnotations.put(id, polyline); + } + return polyline; } /** @@ -508,7 +597,33 @@ public class MapboxMap { @UiThread @NonNull public List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList) { - return mMapView.addPolylines(polylineOptionsList); + int count = polylineOptionsList.size(); + Polyline polyline; + List<Polyline> polylines = new ArrayList<>(count); + for (PolylineOptions options : polylineOptionsList) { + polyline = options.getPolyline(); + if (!polyline.getPoints().isEmpty()) { + polylines.add(polyline); + } + } + + long[] ids = mMapView.addPolylines(polylines); + long id = 0; + Polyline p; + + for (int i = 0; i < polylines.size(); i++) { + p = polylines.get(i); + p.setMapboxMap(this); + if (ids != null) { + id = ids[i]; + } else { + // unit test + id++; + } + p.setId(id); + mAnnotations.put(id, p); + } + return polylines; } /** @@ -520,7 +635,14 @@ public class MapboxMap { @UiThread @NonNull public Polygon addPolygon(@NonNull PolygonOptions polygonOptions) { - return mMapView.addPolygon(polygonOptions); + Polygon polygon = polygonOptions.getPolygon(); + if (!polygon.getPoints().isEmpty()) { + long id = mMapView.addPolygon(polygon); + polygon.setId(id); + polygon.setMapboxMap(this); + mAnnotations.put(id, polygon); + } + return polygon; } /** @@ -532,7 +654,32 @@ public class MapboxMap { @UiThread @NonNull public List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList) { - return mMapView.addPolygons(polygonOptionsList); + int count = polygonOptionsList.size(); + + Polygon polygon; + List<Polygon> polygons = new ArrayList<>(count); + for (PolygonOptions polygonOptions : polygonOptionsList) { + polygon = polygonOptions.getPolygon(); + if (!polygon.getPoints().isEmpty()) { + polygons.add(polygon); + } + } + + long[] ids = mMapView.addPolygons(polygons); + long id = 0; + for (int i = 0; i < polygons.size(); i++) { + polygon = polygons.get(i); + polygon.setMapboxMap(this); + if (ids != null) { + id = ids[i]; + } else { + // unit test + id++; + } + polygon.setId(id); + mAnnotations.put(id, polygon); + } + return polygons; } /** @@ -549,13 +696,55 @@ public class MapboxMap { } /** + * <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) { + 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) { + removeAnnotation(polygon); + } + + /** * Removes an annotation from the map. * * @param annotation The annotation object to remove. */ @UiThread public void removeAnnotation(@NonNull Annotation annotation) { - mMapView.removeAnnotation(annotation); + if (annotation instanceof Marker) { + ((Marker) annotation).hideInfoWindow(); + } + long id = annotation.getId(); + mMapView.removeAnnotation(id); + mAnnotations.remove(id); + } + + /** + * Removes an annotation from the map + * + * @param id The identifier associated to the annotation to be removed + */ + @UiThread + public void removeAnnotation(long id) { + mMapView.removeAnnotation(id); + mAnnotations.remove(id); } /** @@ -565,15 +754,49 @@ public class MapboxMap { */ @UiThread public void removeAnnotations(@NonNull List<? extends Annotation> annotationList) { - mMapView.removeAnnotations(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) annotation).hideInfoWindow(); + } + ids[i] = annotationList.get(i).getId(); + } + mMapView.removeAnnotations(ids); + for (long id : ids) { + mAnnotations.remove(id); + } } /** * Removes all annotations from the map. */ @UiThread - public void removeAllAnnotations() { - mMapView.removeAllAnnotations(); + public void removeAnnotations() { + Annotation annotation; + int count = mAnnotations.size(); + long[] ids = new long[count]; + for (int i = 0; i < count; i++) { + ids[i] = mAnnotations.keyAt(i); + annotation = mAnnotations.get(ids[i]); + if (annotation instanceof Marker) { + ((Marker) annotation).hideInfoWindow(); + } + } + mMapView.removeAnnotations(ids); + mAnnotations.clear(); + } + + /** + * Return a annotation based on its id. + * + * @return An annotation with a matched id, null is returned if no match was found. + */ + @UiThread + @Nullable + public Annotation getAnnotation(long id) { + return mAnnotations.get(id); } /** @@ -583,8 +806,69 @@ public class MapboxMap { * list will not update the map. */ @NonNull - public List<Annotation> getAllAnnotations() { - return mMapView.getAllAnnotations(); + public List<Annotation> getAnnotations() { + List<Annotation> annotations = new ArrayList<>(); + for (int i = 0; i < mAnnotations.size(); i++) { + annotations.add(mAnnotations.get(mAnnotations.keyAt(i))); + } + return annotations; + } + + /** + * 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() { + List<Marker> markers = new ArrayList<>(); + Annotation annotation; + for (int i = 0; i < mAnnotations.size(); i++) { + annotation = mAnnotations.get(mAnnotations.keyAt(i)); + if (annotation instanceof Marker) { + markers.add((Marker) annotation); + } + } + return markers; + } + + /** + * 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() { + List<Polygon> polygons = new ArrayList<>(); + Annotation annotation; + for (int i = 0; i < mAnnotations.size(); i++) { + annotation = mAnnotations.get(mAnnotations.keyAt(i)); + if (annotation instanceof Polygon) { + polygons.add((Polygon) annotation); + } + } + return polygons; + } + + /** + * 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() { + List<Polyline> polylines = new ArrayList<>(); + Annotation annotation; + for (int i = 0; i < mAnnotations.size(); i++) { + annotation = mAnnotations.get(mAnnotations.keyAt(i)); + if (annotation instanceof Polyline) { + polylines.add((Polyline) annotation); + } + } + return polylines; } /** @@ -670,11 +954,17 @@ public class MapboxMap { * @return The currently selected marker. */ @UiThread - @Nullable public List<Marker> getSelectedMarkers() { return mSelectedMarkers; } + private Marker prepareMarker(BaseMarkerOptions markerOptions) { + Marker marker = markerOptions.getMarker(); + Icon icon = mMapView.loadIconForMarker(marker); + marker.setTopOffsetPixels(mMapView.getTopOffsetPixelsForIcon(icon)); + return marker; + } + // // InfoWindow // @@ -735,10 +1025,58 @@ public class MapboxMap { } // + // Padding + // + + /** + * Sets the distance from the edges of the map view’s frame to the edges of the map + * view’s logical viewport. + * <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. + * + * @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) { + mMapView.setContentPadding(left, top, right, bottom); + mUiSettings.invalidate(); + + moveCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder(mCameraPosition).build())); + } + + /** + * + * @return + */ + public int[] getPadding() { + return new int[]{mMapView.getContentPaddingLeft(), + mMapView.getContentPaddingTop(), + mMapView.getContentPaddingRight(), + mMapView.getContentPaddingBottom()}; + } + + // // 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) { + mOnCameraChangeListener = 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. @@ -845,10 +1183,44 @@ public class MapboxMap { * * @return Current active InfoWindow Click Listener */ + @UiThread public OnInfoWindowClickListener getOnInfoWindowClickListener() { return mOnInfoWindowClickListener; } + /** + * 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) { + mOnInfoWindowLongClickListener = listener; + } + + /** + * Return the InfoWindow long click listener + * + * @return Current active InfoWindow long Click Listener + */ + public OnInfoWindowLongClickListener getOnInfoWindowLongClickListener() { + return mOnInfoWindowLongClickListener; + } + + public void setOnInfoWindowCloseListener(@Nullable OnInfoWindowCloseListener listener) { + mOnInfoWindowCloseListener = listener; + } + + /** + * Return the InfoWindow close listener + * + * @return Current active InfoWindow Close Listener + */ + @UiThread + public OnInfoWindowCloseListener getOnInfoWindowCloseListener() { + return mOnInfoWindowCloseListener; + } + // // User location // @@ -874,13 +1246,14 @@ public class MapboxMap { * or @link android.Manifest.permission#ACCESS_FINE_LOCATION. * * @param enabled True to enable; false to disable. - * @throws SecurityException if no suitable permission is present */ @UiThread - @RequiresPermission(anyOf = { - Manifest.permission.ACCESS_COARSE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION}) public void setMyLocationEnabled(boolean enabled) { + if (!mMapView.isPermissionsAccepted()) { + Log.e(MapboxConstants.TAG, "Could not activate user location tracking: " + + "user did not accept the permission or permissions were not requested."); + return; + } mMyLocationEnabled = enabled; mMapView.setMyLocationEnabled(enabled); } @@ -909,40 +1282,6 @@ public class MapboxMap { } /** - * <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 - @RequiresPermission(anyOf = { - Manifest.permission.ACCESS_COARSE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION}) - public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) { - mMapView.setMyLocationTrackingMode(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 mMapView.getMyLocationTrackingMode(); - } - - /** * 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. @@ -959,42 +1298,6 @@ public class MapboxMap { } /** - * <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 UserLocationView} is rotated - * When location tracking is enabled the {@link MapView} is rotated based on 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 - @RequiresPermission(anyOf = { - Manifest.permission.ACCESS_COARSE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION}) - public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) { - mMapView.setMyBearingTrackingMode(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 - @MyLocationTracking.Mode - public int getMyBearingTrackingMode() { - return mMapView.getMyBearingTrackingMode(); - } - - /** * 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. @@ -1033,9 +1336,17 @@ public class MapboxMap { return mMapView; } -// -// Interfaces -// + // + // Invalidate + // + + public void invalidate(){ + mMapView.update(); + } + + // + // Interfaces + // /** * Interface definition for a callback to be invoked when the map is flinged. @@ -1062,6 +1373,20 @@ public class MapboxMap { } /** + * Interface definition for a callback to be invoked for 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 on every frame rendered to the map view. * * @see MapboxMap#setOnFpsChangedListener(OnFpsChangedListener) @@ -1130,7 +1455,37 @@ public class MapboxMap { * @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 onMarkerClick(@NonNull Marker marker); + boolean onInfoWindowClick(@NonNull Marker marker); + } + + /** + * Callback interface for 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); + } + + /** + * Callback interface for close events on a marker's info window. + * + * @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); } /** @@ -1169,7 +1524,7 @@ public class MapboxMap { /** * Interface definition for a callback to be invoked when the the My Location tracking mode changes. * - * @see MapboxMap#setMyLocationTrackingMode(int) + * @see MapView#setMyLocationTrackingMode(int) */ public interface OnMyLocationTrackingModeChangeListener { @@ -1184,7 +1539,7 @@ public class MapboxMap { /** * Interface definition for a callback to be invoked when the the My Location tracking mode changes. * - * @see MapboxMap#setMyLocationTrackingMode(int) + * @see MapView#setMyLocationTrackingMode(int) */ public interface OnMyBearingTrackingModeChangeListener { @@ -1212,13 +1567,20 @@ public class MapboxMap { } private class MapChangeCameraPositionListener implements MapView.OnMapChangedListener { + + private static final long UPDATE_RATE_MS = 400; + private long mPreviousUpdateTimestamp = 0; + @Override public void onMapChanged(@MapView.MapChange int change) { - if (!mInvalidCameraPosition && (change == MapView.REGION_DID_CHANGE - || change == MapView.REGION_DID_CHANGE_ANIMATED - || change == MapView.REGION_WILL_CHANGE - || change == MapView.REGION_WILL_CHANGE_ANIMATED)) { + if (change >= MapView.REGION_WILL_CHANGE && change <= MapView.REGION_DID_CHANGE_ANIMATED) { mInvalidCameraPosition = true; + long currentTime = SystemClock.elapsedRealtime(); + if (currentTime < mPreviousUpdateTimestamp) { + return; + } + invalidateCameraPosition(); + mPreviousUpdateTimestamp = currentTime + UPDATE_RATE_MS; } } } 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 deleted file mode 100644 index cb6407986e..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.content.Context; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.AttributeSet; - -import com.mapbox.mapboxsdk.camera.CameraPosition; - -/** - * Builder for composing {@link MapboxMap} objects. These options can be used when adding a - * map to your application programmatically (as opposed to via XML). If you are using a MapFragment, - * you can pass these options in using the static factory method newInstance(MapboxMapOptions). - * If you are using a MapView, you can pass these options in using the constructor MapView(Context, MapboxMapOptions). - */ -public class MapboxMapOptions implements Parcelable { - - private MapboxMap mMapboxMap; - private UiSettings mUiSettings; - - public MapboxMapOptions(MapboxMap mapboxMap) { - mMapboxMap = mapboxMap; - mUiSettings = mapboxMap.getUiSettings(); - } - - public MapboxMapOptions(Parcel in) { - throw new UnsupportedOperationException(); - } - - public MapboxMapOptions camera(CameraPosition camera) { - mMapboxMap.setCameraPosition(camera); - return this; - } - - public CameraPosition getCamera() { - return mMapboxMap.getCameraPosition(); - } - - public MapboxMapOptions compassEnabled(boolean enabled) { - mUiSettings.setCompassEnabled(enabled); - return this; - } - - public boolean getCompassEnabled() { - return mUiSettings.isCompassEnabled(); - } - - public MapboxMapOptions rotateEnabled(boolean rotateEnabled) { - mUiSettings.setRotateGesturesEnabled(rotateEnabled); - return this; - } - - public MapboxMapOptions rotateGesturesEnabled(boolean enabled) { - mUiSettings.setRotateGesturesEnabled(enabled); - return this; - } - - public boolean getRotateGesturesEnabled() { - return mUiSettings.isRotateGesturesEnabled(); - } - - public MapboxMapOptions scrollGesturesEnabled(boolean enabled) { - mUiSettings.setScrollGesturesEnabled(enabled); - return this; - } - - public boolean getScrollGesturesEnabled() { - return mUiSettings.isScrollGesturesEnabled(); - } - - public MapboxMapOptions tiltGesturesEnabled(boolean enabled) { - mUiSettings.setTiltGesturesEnabled(enabled); - return this; - } - - public boolean getTiltGesturesEnabled() { - return mUiSettings.isTiltGesturesEnabled(); - } - - public MapboxMapOptions zoomControlsEnabled(boolean enabled) { - mUiSettings.setZoomControlsEnabled(enabled); - return this; - } - - public boolean getZoomControlsEnabled() { - return mUiSettings.isZoomControlsEnabled(); - } - - public boolean getZoomGesturesEnabled() { - return mUiSettings.isZoomGesturesEnabled(); - } - - public MapboxMapOptions createFromAttributes(Context context, AttributeSet attrs) { - throw new UnsupportedOperationException(); - } - - 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; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - throw new UnsupportedOperationException(); - } -} 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 6c388d9d8a..b7f583e943 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 @@ -395,6 +395,10 @@ final class NativeMapView { return nativeAddPolygons(mNativeMapViewPtr, polygon); } + public void updateMarker(Marker marker) { + nativeUpdateMarker(mNativeMapViewPtr, marker); + } + public void removeAnnotation(long id) { nativeRemoveAnnotation(mNativeMapViewPtr, id); } @@ -619,6 +623,8 @@ final class NativeMapView { private native long nativeAddMarker(long nativeMapViewPtr, Marker marker); + private native void nativeUpdateMarker(long nativeMapViewPtr, Marker marker); + private native long[] nativeAddMarkers(long nativeMapViewPtr, List<Marker> markers); private native long nativeAddPolyline(long nativeMapViewPtr, Polyline polyline); 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 e53d430b69..0d5745d4c9 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 @@ -1,6 +1,7 @@ package com.mapbox.mapboxsdk.maps; import android.graphics.PointF; +import android.support.annotation.FloatRange; import android.support.annotation.NonNull; import com.mapbox.mapboxsdk.geometry.LatLng; @@ -21,6 +22,20 @@ public class Projection { } /** + * <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 = -180, to = 180) double latitude) { + return mMapView.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). @@ -55,7 +70,7 @@ public class Projection { .include(bottomRight) .include(bottomLeft); - return new VisibleRegion(topLeft,topRight,bottomLeft,bottomRight,builder.build()); + return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight, builder.build()); } /** @@ -69,4 +84,14 @@ public class Projection { public PointF toScreenLocation(LatLng location) { return mMapView.toScreenLocation(location); } + + /** + * Calculates a zoom level based on minimum scale and current scale from MapView + * + * @param minScale The minimum scale to calculate the zoom level. + * @return zoom level that fits the MapView. + */ + public double calculateZoom(float minScale) { + return Math.log(mMapView.getScale() * minScale) / Math.log(2); + } } 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 147cd31b5a..8783712e10 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 @@ -1,6 +1,7 @@ package com.mapbox.mapboxsdk.maps; import android.os.Bundle; +import android.os.Handler; import android.support.annotation.NonNull; import android.support.v4.app.Fragment; import android.view.LayoutInflater; @@ -29,18 +30,10 @@ public class SupportMapFragment extends Fragment { private MapView mMap; - public static SupportMapFragment newInstance(){ + public static SupportMapFragment newInstance() { return new SupportMapFragment(); } - public static SupportMapFragment newInstance(MapboxMapOptions mapboxMapOptions) { - final SupportMapFragment mapFragment = new SupportMapFragment(); - Bundle bundle = new Bundle(); - bundle.putParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS, mapboxMapOptions); - mapFragment.setArguments(bundle); - return mapFragment; - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); @@ -94,7 +87,12 @@ public class SupportMapFragment extends Fragment { } @NonNull - public void getMapAsync(@NonNull OnMapReadyCallback onMapReadyCallback){ - mMap.getMapAsync(onMapReadyCallback); + public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) { + new Handler().post(new Runnable() { + @Override + public void run() { + mMap.getMapAsync(onMapReadyCallback); + } + }); } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java new file mode 100644 index 0000000000..543ff19e56 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java @@ -0,0 +1,121 @@ +package com.mapbox.mapboxsdk.maps; + +import android.support.annotation.NonNull; +import android.support.annotation.UiThread; + +import com.mapbox.mapboxsdk.constants.MyBearingTracking; +import com.mapbox.mapboxsdk.constants.MyLocationTracking; + +public class TrackingSettings { + + private MapView mapView; + private UiSettings uiSettings; + private boolean dismissTrackingOnGesture = true; + + @MyLocationTracking.Mode + private int mMyLocationTrackingMode; + + @MyBearingTracking.Mode + private int mMyBearingTrackingMode; + + TrackingSettings(@NonNull MapView mapView, UiSettings uiSettings) { + this.mapView = mapView; + 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) { + mMyLocationTrackingMode = myLocationTrackingMode; + mapView.setMyLocationTrackingMode(myLocationTrackingMode); + validateGesturesForTrackingModes(); + } + + /** + * 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 mMyLocationTrackingMode; + } + + /** + * <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 UserLocationView} is rotated + * When location tracking is enabled the {@link MapView} is rotated based on 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) { + mMyBearingTrackingMode = myBearingTrackingMode; + mapView.setMyBearingTrackingMode(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 + @MyLocationTracking.Mode + public int getMyBearingTrackingMode() { + return mMyBearingTrackingMode; + } + + public boolean isDismissTrackingOnGesture() { + return dismissTrackingOnGesture; + } + + public void setDismissTrackingOnGesture(boolean dismissTrackingOnGesture) { + this.dismissTrackingOnGesture = dismissTrackingOnGesture; + validateGesturesForTrackingModes(); + } + + private void validateGesturesForTrackingModes() { + if (!dismissTrackingOnGesture) { + int myLocationTrackingMode = getMyLocationTrackingMode(); + int myBearingTrackingMode = getMyBearingTrackingMode(); + + // Enable/disable gestures based on tracking mode + if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { + uiSettings.setScrollGesturesEnabled(true); + uiSettings.setRotateGesturesEnabled(true); + } else { + uiSettings.setScrollGesturesEnabled(false); + uiSettings.setRotateGesturesEnabled((myBearingTrackingMode == MyBearingTracking.NONE)); + } + } + } + + public boolean isLocationTrackingDisabled(){ + return mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE; + } +} 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 d6cb106054..1538f49d60 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 @@ -1,9 +1,14 @@ package com.mapbox.mapboxsdk.maps; +import android.support.annotation.FloatRange; import android.support.annotation.NonNull; import android.support.annotation.UiThread; +import android.util.Log; import android.view.Gravity; import android.view.View; +import android.widget.VideoView; + +import com.mapbox.mapboxsdk.constants.MapboxConstants; /** * Settings for the user interface of a MapboxMap. To obtain this interface, call getUiSettings(). @@ -12,17 +17,9 @@ public class UiSettings { private MapView mapView; - private boolean compassEnabled; - private int compassGravity; - private int[] compassMargins; - - private boolean logoEnabled; - private int logoGravity; - private int[] logoMargins; - - private boolean attributionEnabled; - private int attributionGravity; - private int[] attributionMargins; + private ViewSettings compassSettings; + private ViewSettings logoSettings; + private ViewSettings attributionSettings; private boolean rotateGesturesEnabled; private boolean tiltGesturesEnabled; @@ -30,11 +27,78 @@ public class UiSettings { private boolean zoomControlsEnabled; private boolean scrollGesturesEnabled; + private double maxZoomLevel = -1; + private double minZoomLevel = -1; + UiSettings(@NonNull MapView mapView) { this.mapView = mapView; - this.compassMargins = new int[4]; - this.attributionMargins = new int[4]; - this.logoMargins = new int[4]; + this.compassSettings = new ViewSettings(); + this.logoSettings = new ViewSettings(); + this.attributionSettings = new ViewSettings(); + } + + /** + * <p> + * Sets the minimum zoom level the map can be displayed at. + * </p> + * + * @param minZoom The new minimum zoom level. + */ + @UiThread + public void setMinZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double minZoom) { + if ((minZoom < MapboxConstants.MINIMUM_ZOOM) || (minZoom > MapboxConstants.MAXIMUM_ZOOM)) { + Log.e(MapboxConstants.TAG, "Not setting minZoom, value is in unsupported range: " + minZoom); + return; + } + minZoomLevel = minZoom; + mapView.setMinZoom(minZoom); + } + + /** + * <p> + * Gets the maximum zoom level the map can be displayed at. + * </p> + * + * @return The minimum zoom level. + */ + @UiThread + public double getMinZoom() { + if (minZoomLevel == -1) { + return minZoomLevel = mapView.getMinZoom(); + } + return minZoomLevel; + } + + /** + * <p> + * Sets the maximum zoom level the map can be displayed at. + * </p> + * + * @param maxZoom The new maximum zoom level. + */ + @UiThread + public void setMaxZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double maxZoom) { + if ((maxZoom < MapboxConstants.MINIMUM_ZOOM) || (maxZoom > MapboxConstants.MAXIMUM_ZOOM)) { + Log.e(MapboxConstants.TAG, "Not setting maxZoom, value is in unsupported range: " + maxZoom); + return; + } + maxZoomLevel = maxZoom; + mapView.setMaxZoom(maxZoom); + } + + /** + * <p> + * Gets the maximum zoom level the map can be displayed at. + * </p> + * + * @return The maximum zoom level. + */ + @UiThread + public double getMaxZoom() { + if (maxZoomLevel == -1) { + return maxZoomLevel = mapView.getMaxZoom(); + } + return maxZoomLevel; } /** @@ -49,8 +113,8 @@ public class UiSettings { * @param compassEnabled True to enable the compass; false to disable the compass. */ public void setCompassEnabled(boolean compassEnabled) { - this.compassEnabled = compassEnabled; - this.mapView.setCompassEnabled(compassEnabled); + compassSettings.setEnabled(compassEnabled); + mapView.setCompassEnabled(compassEnabled); } /** @@ -59,7 +123,7 @@ public class UiSettings { * @return True if the compass is enabled; false if the compass is disabled. */ public boolean isCompassEnabled() { - return compassEnabled; + return compassSettings.isEnabled(); } /** @@ -74,8 +138,8 @@ public class UiSettings { */ @UiThread public void setCompassGravity(int gravity) { - this.compassGravity = gravity; - this.mapView.setCompassGravity(gravity); + compassSettings.setGravity(gravity); + mapView.setCompassGravity(gravity); } /** @@ -84,7 +148,7 @@ public class UiSettings { * @return The gravity */ public int getCompassGravity() { - return compassGravity; + return compassSettings.getGravity(); } /** @@ -98,8 +162,8 @@ public class UiSettings { */ @UiThread public void setCompassMargins(int left, int top, int right, int bottom) { - this.compassMargins = new int[]{left, top, right, bottom}; - this.mapView.setCompassMargins(left, top, right, bottom); + compassSettings.setMargins(new int[]{left, top, right, bottom}); + mapView.setCompassMargins(left, top, right, bottom); } /** @@ -108,7 +172,7 @@ public class UiSettings { * @return The left margin in pixels */ public int getCompassMarginLeft() { - return compassMargins[0]; + return compassSettings.getMargins()[0]; } /** @@ -117,7 +181,7 @@ public class UiSettings { * @return The top margin in pixels */ public int getCompassMarginTop() { - return compassMargins[1]; + return compassSettings.getMargins()[1]; } /** @@ -126,7 +190,7 @@ public class UiSettings { * @return The right margin in pixels */ public int getCompassMarginRight() { - return compassMargins[2]; + return compassSettings.getMargins()[2]; } /** @@ -135,7 +199,7 @@ public class UiSettings { * @return The bottom margin in pixels */ public int getCompassMarginBottom() { - return compassMargins[3]; + return compassSettings.getMargins()[3]; } /** @@ -147,8 +211,8 @@ public class UiSettings { * @param enabled True to enable the logo; false to disable the logo. */ public void setLogoEnabled(boolean enabled) { - this.logoEnabled = enabled; - this.mapView.setLogoVisibility(enabled ); + logoSettings.setEnabled(enabled); + mapView.setLogoVisibility(enabled); } /** @@ -157,7 +221,7 @@ public class UiSettings { * @return True if the logo is enabled; false if the logo is disabled. */ public boolean isLogoEnabled() { - return logoEnabled; + return logoSettings.isEnabled(); } /** @@ -171,8 +235,8 @@ public class UiSettings { * @see Gravity */ public void setLogoGravity(int gravity) { - this.logoGravity = gravity; - this.mapView.setLogoGravity(gravity); + logoSettings.setGravity(gravity); + mapView.setLogoGravity(gravity); } /** @@ -181,7 +245,7 @@ public class UiSettings { * @return The gravity */ public int getLogoGravity() { - return logoGravity; + return logoSettings.getGravity(); } /** @@ -194,8 +258,8 @@ public class UiSettings { * @param bottom The bottom margin in pixels. */ public void setLogoMargins(int left, int top, int right, int bottom) { - this.logoMargins = new int[]{left, top, right, bottom}; - this.mapView.setLogoMargins(left, top, right, bottom); + logoSettings.setMargins(new int[]{left, top, right, bottom}); + mapView.setLogoMargins(left, top, right, bottom); } /** @@ -203,8 +267,8 @@ public class UiSettings { * * @return The left margin in pixels */ - public int getLogoMarginLeft(){ - return logoMargins[0]; + public int getLogoMarginLeft() { + return logoSettings.getMargins()[0]; } /** @@ -212,8 +276,8 @@ public class UiSettings { * * @return The top margin in pixels */ - public int getLogoMarginTop(){ - return logoMargins[1]; + public int getLogoMarginTop() { + return logoSettings.getMargins()[1]; } /** @@ -221,8 +285,8 @@ public class UiSettings { * * @return The right margin in pixels */ - public int getLogoMarginRight(){ - return logoMargins[2]; + public int getLogoMarginRight() { + return logoSettings.getMargins()[2]; } /** @@ -230,8 +294,8 @@ public class UiSettings { * * @return The bottom margin in pixels */ - public int getLogoMarginBottom(){ - return logoMargins[3]; + public int getLogoMarginBottom() { + return logoSettings.getMargins()[3]; } /** @@ -243,8 +307,8 @@ public class UiSettings { * @param enabled True to enable the logo; false to disable the logo. */ public void setAttributionEnabled(boolean enabled) { - this.attributionEnabled = enabled; - this.mapView.setAttributionVisibility(enabled ? View.VISIBLE : View.GONE); + attributionSettings.setEnabled(enabled); + mapView.setAttributionVisibility(enabled ? View.VISIBLE : View.GONE); } /** @@ -253,7 +317,7 @@ public class UiSettings { * @return True if the logo is enabled; false if the logo is disabled. */ public boolean isAttributionEnabled() { - return attributionEnabled; + return attributionSettings.isEnabled(); } /** @@ -267,8 +331,8 @@ public class UiSettings { * @see Gravity */ public void setAttributionGravity(int gravity) { - this.attributionGravity = gravity; - this.mapView.setAttributionGravity(gravity); + attributionSettings.setGravity(gravity); + mapView.setAttributionGravity(gravity); } /** @@ -277,7 +341,7 @@ public class UiSettings { * @return The gravity */ public int getAttributionGravity() { - return attributionGravity; + return attributionSettings.getGravity(); } /** @@ -290,8 +354,8 @@ public class UiSettings { * @param bottom The bottom margin in pixels. */ public void setAttributionMargins(int left, int top, int right, int bottom) { - this.attributionMargins = new int[]{left, top, right, bottom}; - this.mapView.setAttributionMargins(left, top, right, bottom); + attributionSettings.setMargins(new int[]{left, top, right, bottom}); + mapView.setAttributionMargins(left, top, right, bottom); } /** @@ -299,8 +363,8 @@ public class UiSettings { * * @return The left margin in pixels */ - public int getAttributionMarginLeft(){ - return attributionMargins[0]; + public int getAttributionMarginLeft() { + return attributionSettings.getMargins()[0]; } /** @@ -308,8 +372,8 @@ public class UiSettings { * * @return The top margin in pixels */ - public int getAttributionMarginTop(){ - return attributionMargins[1]; + public int getAttributionMarginTop() { + return attributionSettings.getMargins()[1]; } /** @@ -317,8 +381,8 @@ public class UiSettings { * * @return The right margin in pixels */ - public int getAttributionMarginRight(){ - return attributionMargins[2]; + public int getAttributionMarginRight() { + return attributionSettings.getMargins()[2]; } /** @@ -326,8 +390,8 @@ public class UiSettings { * * @return The bottom margin in pixels */ - public int getAttributionMarginBottom(){ - return attributionMargins[3]; + public int getAttributionMarginBottom() { + return attributionSettings.getMargins()[3]; } /** @@ -477,4 +541,31 @@ public class UiSettings { setTiltGesturesEnabled(enabled); setZoomGesturesEnabled(enabled); } + + /** + * Returns the measured height of the MapView + * + * @return height in pixels + */ + public float getHeight() { + return mapView.getMeasuredHeight(); + } + + /** + * Returns the measured width of the MapView + * + * @return widht in pixels + */ + public float getWidth() { + return mapView.getMeasuredWidth(); + } + + /** + * Invalidates the ViewSettings instances shown on top of the MapView + */ + public void invalidate() { + mapView.setLogoMargins(getLogoMarginLeft(), getLogoMarginTop(), getLogoMarginRight(), getLogoMarginBottom()); + mapView.setCompassMargins(getCompassMarginLeft(), getCompassMarginTop(), getCompassMarginRight(), getCompassMarginBottom()); + mapView.setAttributionMargins(getAttributionMarginLeft(), getAttributionMarginTop(), getAttributionMarginRight(), getAttributionMarginBottom()); + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ViewSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ViewSettings.java new file mode 100644 index 0000000000..a192a1b576 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ViewSettings.java @@ -0,0 +1,36 @@ +package com.mapbox.mapboxsdk.maps; + +public class ViewSettings { + + private boolean enabled; + private int gravity; + private int[]margins; + + public ViewSettings() { + margins = new int[4]; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public int getGravity() { + return gravity; + } + + public void setGravity(int gravity) { + this.gravity = gravity; + } + + public int[] getMargins() { + return margins; + } + + public void setMargins(int[] margins) { + this.margins = margins; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CompassView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java index 0d84289332..28afb70de3 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CompassView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java @@ -1,4 +1,4 @@ -package com.mapbox.mapboxsdk.maps; +package com.mapbox.mapboxsdk.maps.widgets; import android.content.Context; import android.support.v4.content.ContextCompat; @@ -11,6 +11,8 @@ import android.view.ViewGroup; import android.widget.ImageView; import com.mapbox.mapboxsdk.R; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; import java.lang.ref.WeakReference; import java.util.Timer; @@ -21,7 +23,7 @@ import java.util.TimerTask; * when it isn't true north (0.0). Tapping the compass resets the bearing to true * north and hides the compass. */ -public class CompassView extends ImageView { +public final class CompassView extends ImageView { private Timer mNorthTimer; private double mDirection = 0.0f; @@ -46,7 +48,7 @@ public class CompassView extends ImageView { // View configuration setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.compass)); - setContentDescription(getResources().getString(R.string.compassContentDescription)); + setContentDescription(getResources().getString(R.string.mapbox_compassContentDescription)); setEnabled(false); // Layout params @@ -139,17 +141,17 @@ public class CompassView extends ImageView { public static class CompassClickListener implements View.OnClickListener { - private WeakReference<MapView> mMapView; + private WeakReference<MapboxMap> mMapboxMap; - public CompassClickListener(final MapView mapView) { - mMapView = new WeakReference<>(mapView); + public CompassClickListener(final MapboxMap mapboxMap) { + mMapboxMap = new WeakReference<>(mapboxMap); } @Override public void onClick(View v) { - final MapView mapView = mMapView.get(); - if (mapView != null) { - mapView.resetNorth(); + final MapboxMap mapboxMap = mMapboxMap.get(); + if (mapboxMap != null) { + mapboxMap.resetNorth(); } } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UserLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/UserLocationView.java index 9f8261a0a7..98d66b9307 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UserLocationView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/UserLocationView.java @@ -1,4 +1,4 @@ -package com.mapbox.mapboxsdk.maps; +package com.mapbox.mapboxsdk.maps.widgets; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; @@ -27,11 +27,14 @@ import android.view.ViewGroup; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.constants.MyBearingTracking; import com.mapbox.mapboxsdk.constants.MyLocationTracking; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.location.LocationListener; -import com.mapbox.mapboxsdk.location.LocationServices; +import com.mapbox.mapboxsdk.location.LocationService; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.Projection; import java.lang.ref.WeakReference; @@ -42,9 +45,8 @@ import java.lang.ref.WeakReference; public final class UserLocationView extends View { - private MapView mMapView; - - private float mDensity; + private MapboxMap mMapboxMap; + private Projection mProjection; private boolean mShowMarker; private boolean mShowDirection; @@ -84,7 +86,6 @@ public final class UserLocationView extends View { private LatLng mCurrentMapViewCoordinate; private double mCurrentBearing; - private boolean mPaused = false; private Location mUserLocation; private UserLocationListener mUserLocationListener; @@ -99,7 +100,6 @@ public final class UserLocationView extends View { // Compass data private MyBearingListener mBearingChangeListener; - private static final long BEARING_DURATION = 100; public UserLocationView(Context context) { super(context); @@ -132,9 +132,9 @@ public final class UserLocationView extends View { // Setup the custom paint Resources resources = context.getResources(); - int accuracyColor = resources.getColor(R.color.my_location_ring); + int accuracyColor = ContextCompat.getColor(context,R.color.my_location_ring); - mDensity = resources.getDisplayMetrics().density; + float density = resources.getDisplayMetrics().density; mMarkerCoordinate = new LatLng(0.0, 0.0); mMarkerScreenPoint = new PointF(); mMarkerScreenMatrix = new Matrix(); @@ -148,7 +148,7 @@ public final class UserLocationView extends View { mAccuracyPaintStroke = new Paint(); mAccuracyPaintStroke.setAntiAlias(true); mAccuracyPaintStroke.setStyle(Paint.Style.STROKE); - mAccuracyPaintStroke.setStrokeWidth(0.5f * mDensity); + mAccuracyPaintStroke.setStrokeWidth(0.5f * density); mAccuracyPaintStroke.setColor(accuracyColor); mAccuracyPaintStroke.setAlpha((int) (255 * 0.5f)); @@ -195,8 +195,9 @@ public final class UserLocationView extends View { mUserLocationStaleDrawable.setBounds(mUserLocationStaleDrawableBounds); } - public void setMapView(MapView mapView) { - mMapView = mapView; + public void setMapboxMap(MapboxMap mapboxMap) { + mMapboxMap = mapboxMap; + mProjection = mapboxMap.getProjection(); } public void onStart() { @@ -247,21 +248,15 @@ public final class UserLocationView extends View { if (myLocationTrackingMode != MyLocationTracking.TRACKING_NONE && mUserLocation != null) { // center map directly if we have a location fix mMarkerCoordinate = new LatLng(mUserLocation.getLatitude(), mUserLocation.getLongitude()); - mMapView.getMapboxMap().moveCamera(CameraUpdateFactory.newLatLng(new LatLng(mUserLocation))); + mMapboxMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(mUserLocation))); // center view directly mMarkerScreenMatrix.reset(); - mMarkerScreenMatrix.setTranslate( - getMeasuredWidth() / 2, - getMeasuredHeight() / 2); + mMarkerScreenPoint = getMarkerScreenPoint(); + mMarkerScreenMatrix.setTranslate(mMarkerScreenPoint.x, mMarkerScreenPoint.y); } } - @MyLocationTracking.Mode - public int getMyLocationTrackingMode() { - return mMyLocationTrackingMode; - } - @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); @@ -278,7 +273,7 @@ public final class UserLocationView extends View { // compute new marker position // TODO add JNI method that takes existing pointf if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { - mMarkerScreenPoint = mMapView.toScreenLocation(mMarkerCoordinate); + mMarkerScreenPoint = getMarkerScreenPoint(); mMarkerScreenMatrix.reset(); mMarkerScreenMatrix.setTranslate( mMarkerScreenPoint.x, @@ -289,11 +284,11 @@ public final class UserLocationView extends View { if (mShowDirection) { bearing = mMyBearingTrackingMode == MyBearingTracking.COMPASS ? mBearingChangeListener.getCompassBearing() : mUserLocation.getBearing(); } else { - bearing = (float) mMapView.getBearing(); + bearing = mMapboxMap.getCameraPosition().bearing; } if (mCurrentMapViewCoordinate == null) { - mCurrentMapViewCoordinate = mMapView.getMapboxMap().getCameraPosition().target; + mCurrentMapViewCoordinate = mMapboxMap.getCameraPosition().target; } // only update if there is an actual change @@ -302,11 +297,10 @@ public final class UserLocationView extends View { .target(mMarkerCoordinate) .bearing(bearing) .build(); - mMapView.getMapboxMap().animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 300, null); + mMapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 300, null); mMarkerScreenMatrix.reset(); - mMarkerScreenMatrix.setTranslate( - getMeasuredWidth() / 2, - getMeasuredHeight() / 2); + mMarkerScreenPoint = getMarkerScreenPoint(); + mMarkerScreenMatrix.setTranslate(mMarkerScreenPoint.x, mMarkerScreenPoint.y); // set values for next check for actual change mCurrentMapViewCoordinate = mMarkerCoordinate; @@ -317,10 +311,10 @@ public final class UserLocationView extends View { // rotate so arrow in points to bearing if (mShowDirection) { if (mMyBearingTrackingMode == MyBearingTracking.COMPASS && mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { - mMarkerScreenMatrix.preRotate(mCompassMarkerDirection + (float) mMapView.getDirection()); + mMarkerScreenMatrix.preRotate(mCompassMarkerDirection + mMapboxMap.getCameraPosition().bearing); } else if (mMyBearingTrackingMode == MyBearingTracking.GPS) { if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { - mMarkerScreenMatrix.preRotate(mGpsMarkerDirection + (float) mMapView.getDirection()); + mMarkerScreenMatrix.preRotate(mGpsMarkerDirection + mMapboxMap.getCameraPosition().bearing); } else { mMarkerScreenMatrix.preRotate(mGpsMarkerDirection); } @@ -331,7 +325,7 @@ public final class UserLocationView extends View { if (mShowAccuracy && !mStaleMarker) { mAccuracyPath.reset(); mAccuracyPath.addCircle(0.0f, 0.0f, - (float) (mMarkerAccuracy / mMapView.getMetersPerPixelAtLatitude( + (float) (mMarkerAccuracy / mMapboxMap.getProjection().getMetersPerPixelAtLatitude( mMarkerCoordinate.getLatitude())), Path.Direction.CW); @@ -371,11 +365,11 @@ public final class UserLocationView extends View { */ private void toggleGps(boolean enableGps) { - LocationServices locationServices = LocationServices.getLocationServices(getContext()); + LocationService locationService = LocationService.getInstance(getContext()); if (enableGps) { // Set an initial location if one available - Location lastLocation = locationServices.getLastLocation(); + Location lastLocation = locationService.getLastLocation(); if (lastLocation != null) { setLocation(lastLocation); } @@ -385,16 +379,16 @@ public final class UserLocationView extends View { } // Register for Location Updates - locationServices.addLocationListener(mUserLocationListener); + locationService.addLocationListener(mUserLocationListener); } else { // Disable location and user dot setLocation(null); // Deregister for Location Updates - locationServices.removeLocationListener(mUserLocationListener); + locationService.removeLocationListener(mUserLocationListener); } - locationServices.toggleGPS(enableGps); + locationService.toggleGPS(enableGps); } public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) { @@ -415,11 +409,6 @@ public final class UserLocationView extends View { update(); } - @MyBearingTracking.Mode - public int getMyBearingTrackingMode() { - return mMyBearingTrackingMode; - } - private class MyBearingListener implements SensorEventListener { private SensorManager mSensorManager; @@ -483,9 +472,8 @@ public final class UserLocationView extends View { SensorManager.getRotationMatrix(mR, null, mLastAccelerometer, mLastMagnetometer); SensorManager.getOrientation(mR, mOrientation); float azimuthInRadians = mOrientation[0]; - float azimuthInDegress = (float) (Math.toDegrees(azimuthInRadians) + 360) % 360; - mCompassBearing = azimuthInDegress; + mCompassBearing = (float) (Math.toDegrees(azimuthInRadians) + 360) % 360; if (mCompassBearing < 0) { // only allow positive degrees mCompassBearing += 360; @@ -515,7 +503,7 @@ public final class UserLocationView extends View { /** - * Callback method for receiving location updates from LocationServices. + * Callback method for receiving location updates from LocationService. * * @param location The new Location data */ @@ -670,7 +658,7 @@ public final class UserLocationView extends View { } void updateOnNextFrame() { - mMapView.update(); + mMapboxMap.invalidate(); } /** @@ -770,4 +758,14 @@ public final class UserLocationView extends View { return mPaused; } + public PointF getMarkerScreenPoint() { + if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { + mMarkerScreenPoint = mProjection.toScreenLocation(mMarkerCoordinate); + } else { + int[] contentPadding = mMapboxMap.getPadding(); + mMarkerScreenPoint = new PointF(((getMeasuredWidth() + contentPadding[0] - contentPadding[2]) / 2) + , ((getMeasuredHeight() - contentPadding[3] + contentPadding[1]) / 2)); + } + return mMarkerScreenPoint; + } } |