summaryrefslogtreecommitdiff
path: root/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps')
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java67
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java489
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java355
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java297
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java96
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java14
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java77
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java145
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java115
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ViewSettings.java9
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java705
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java146
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/UserLocationView.java757
13 files changed, 2168 insertions, 1104 deletions
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 2fefd805ea..27ecb7520b 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
@@ -1,14 +1,19 @@
package com.mapbox.mapboxsdk.maps;
import android.app.Fragment;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
+import com.mapbox.mapboxsdk.MapboxAccountManager;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.exceptions.InvalidAccessTokenException;
/**
* Fragment wrapper around a map view.
@@ -44,7 +49,7 @@ public final class MapFragment extends Fragment {
}
/**
- * Creates the fragment view hierachy.
+ * Creates the fragment view hierarchy.
*
* @param inflater Inflater used to inflate content.
* @param container The parent layout for the map fragment.
@@ -54,11 +59,67 @@ public final class MapFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
- MapboxMapOptions options = getArguments().getParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS);
+ MapboxMapOptions options = null;
+
+ // Get bundle
+ Bundle bundle = getArguments();
+ if (bundle != null && bundle.containsKey(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS)) {
+ options = bundle.getParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS);
+ }
+
+ // Assign an AccessToken if needed
+ if (options == null || options.getAccessToken() == null) {
+ String token = null;
+ if (MapboxAccountManager.getInstance() != null) {
+ token = MapboxAccountManager.getInstance().getAccessToken();
+ } else {
+ token = getToken(inflater.getContext());
+ }
+ if (TextUtils.isEmpty(token)) {
+ throw new InvalidAccessTokenException();
+ }
+ if (options == null) {
+ options = new MapboxMapOptions().accessToken(token);
+ } else {
+ options.accessToken(token);
+ }
+ }
return mMap = new MapView(inflater.getContext(), options);
}
/**
+ * <p>
+ * Returns the Mapbox access token set in the app resources.
+ * </p>
+ * It will first search the application manifest for a {@link MapboxConstants#KEY_META_DATA_MANIFEST}
+ * meta-data value. If not found it will then attempt to load the access token from the
+ * {@code res/raw/token.txt} development file.
+ *
+ * @param context The {@link Context} of the {@link android.app.Activity} or {@link android.app.Fragment}.
+ * @return The Mapbox access token or null if not found.
+ * @see MapboxConstants#KEY_META_DATA_MANIFEST
+ *
+ * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)}
+ */
+ @Deprecated
+ private String getToken(@NonNull Context context) {
+ try {
+ // read out AndroidManifest
+ PackageManager packageManager = context.getPackageManager();
+ ApplicationInfo appInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
+ String token = appInfo.metaData.getString(MapboxConstants.KEY_META_DATA_MANIFEST);
+ if (token == null || token.isEmpty()) {
+ throw new IllegalArgumentException();
+ }
+ return token;
+ } catch (Exception e) {
+ // use fallback on string resource, used for development
+ int tokenResId = context.getResources().getIdentifier("mapbox_access_token", "string", context.getPackageName());
+ return tokenResId != 0 ? context.getString(tokenResId) : null;
+ }
+ }
+
+ /**
* Called when the fragment view hierarchy is created.
*
* @param view The content view of the fragment
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 38dd4bccb4..b34b947a2a 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,15 +10,15 @@ 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.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
+import android.graphics.drawable.ColorDrawable;
import android.location.Location;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
@@ -58,12 +58,14 @@ 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.MapboxAccountManager;
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.MarkerView;
import com.mapbox.mapboxsdk.annotations.Polygon;
import com.mapbox.mapboxsdk.annotations.Polyline;
import com.mapbox.mapboxsdk.camera.CameraPosition;
@@ -73,15 +75,17 @@ 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.location.LocationListener;
+import com.mapbox.mapboxsdk.location.LocationServices;
import com.mapbox.mapboxsdk.maps.widgets.CompassView;
-import com.mapbox.mapboxsdk.maps.widgets.UserLocationView;
+import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
+import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
import com.mapbox.mapboxsdk.telemetry.MapboxEvent;
import com.mapbox.mapboxsdk.telemetry.MapboxEventManager;
+import com.mapbox.mapboxsdk.utils.ColorUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -106,19 +110,25 @@ import java.util.concurrent.CopyOnWriteArrayList;
* </p>
* <strong>Warning:</strong> Please note that you are responsible for getting permission to use the map data,
* and for ensuring your use adheres to the relevant terms of use.
- *
- * @see MapView#setAccessToken(String)
*/
public class MapView extends FrameLayout {
private MapboxMap mMapboxMap;
+ private boolean mInitialLoad;
+ private boolean mDestroyed;
+
private List<Icon> mIcons;
+ private int mAverageIconHeight;
+ private int mAverageIconWidth;
private NativeMapView mNativeMapView;
+ private boolean mHasSurface = false;
+
private CompassView mCompassView;
private ImageView mLogoView;
private ImageView mAttributionsView;
- private UserLocationView mUserLocationView;
+ private MyLocationView mMyLocationView;
+ private LocationListener mMyLocationListener;
private CopyOnWriteArrayList<OnMapChangedListener> mOnMapChangedListener;
private ZoomButtonsController mZoomButtonsController;
@@ -140,11 +150,9 @@ public class MapView extends FrameLayout {
private int mContentPaddingRight;
private int mContentPaddingBottom;
- private String mStyleUrl;
+ private StyleInitializer mStyleInitializer;
private List<OnMapReadyCallback> mOnMapReadyCallbackList;
- private boolean mInitialLoad;
- private boolean mDestroyed;
@UiThread
public MapView(@NonNull Context context) {
@@ -176,6 +184,7 @@ public class MapView extends FrameLayout {
mOnMapChangedListener = new CopyOnWriteArrayList<>();
mMapboxMap = new MapboxMap(this);
mIcons = new ArrayList<>();
+ mStyleInitializer = new StyleInitializer(context);
View view = LayoutInflater.from(context).inflate(R.layout.mapview_internal, this);
if (!isInEditMode()) {
@@ -215,8 +224,8 @@ public class MapView extends FrameLayout {
// Connectivity
onConnectivityChanged(isConnected());
- mUserLocationView = (UserLocationView) view.findViewById(R.id.userLocationView);
- mUserLocationView.setMapboxMap(mMapboxMap);
+ mMyLocationView = (MyLocationView) view.findViewById(R.id.userLocationView);
+ mMyLocationView.setMapboxMap(mMapboxMap);
mCompassView = (CompassView) view.findViewById(R.id.compassView);
mCompassView.setMapboxMap(mMapboxMap);
@@ -245,24 +254,45 @@ public class MapView extends FrameLayout {
mMapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(position));
}
- String accessToken = options.getAccessToken();
- if (!TextUtils.isEmpty(accessToken)) {
- mMapboxMap.setAccessToken(accessToken);
+ String accessToken = null;
+ if (MapboxAccountManager.getInstance() != null) {
+ accessToken = MapboxAccountManager.getInstance().getAccessToken();
+ } else {
+ accessToken = options.getAccessToken();
}
String style = options.getStyle();
- if (!TextUtils.isEmpty(style)) {
- mMapboxMap.setStyleUrl(style);
+ if (!TextUtils.isEmpty(accessToken)) {
+ mMapboxMap.setAccessToken(accessToken);
+ if (style != null) {
+ setStyleUrl(style);
+ }
+ } else {
+ mStyleInitializer.setStyle(style, true);
}
+ // MyLocationView
+ MyLocationViewSettings myLocationViewSettings = mMapboxMap.getMyLocationViewSettings();
+ myLocationViewSettings.setForegroundDrawable(options.getMyLocationForegroundDrawable(), options.getMyLocationForegroundBearingDrawable());
+ myLocationViewSettings.setForegroundTintColor(options.getMyLocationForegroundTintColor());
+ myLocationViewSettings.setBackgroundDrawable(options.getMyLocationBackgroundDrawable(), options.getMyLocationBackgroundPadding());
+ myLocationViewSettings.setBackgroundTintColor(options.getMyLocationBackgroundTintColor());
+ myLocationViewSettings.setAccuracyAlpha(options.getMyLocationAccuracyAlpha());
+ myLocationViewSettings.setAccuracyTintColor(options.getMyLocationAccuracyTintColor());
mMapboxMap.setMyLocationEnabled(options.getLocationEnabled());
// Enable gestures
UiSettings uiSettings = mMapboxMap.getUiSettings();
uiSettings.setZoomGesturesEnabled(options.getZoomGesturesEnabled());
+ uiSettings.setZoomGestureChangeAllowed(options.getZoomGesturesEnabled());
uiSettings.setScrollGesturesEnabled(options.getScrollGesturesEnabled());
+ uiSettings.setScrollGestureChangeAllowed(options.getScrollGesturesEnabled());
uiSettings.setRotateGesturesEnabled(options.getRotateGesturesEnabled());
+ uiSettings.setRotateGestureChangeAllowed(options.getRotateGesturesEnabled());
uiSettings.setTiltGesturesEnabled(options.getTiltGesturesEnabled());
+ uiSettings.setTiltGestureChangeAllowed(options.getTiltGesturesEnabled());
+
+ // Ui Controls
uiSettings.setZoomControlsEnabled(options.getZoomControlsEnabled());
// Zoom
@@ -303,6 +333,10 @@ public class MapView extends FrameLayout {
int seventySixDp = (int) resources.getDimension(R.dimen.seventy_six_dp);
uiSettings.setAttributionMargins(seventySixDp, sevenDp, sevenDp, sevenDp);
}
+
+ int attributionTintColor = options.getAttributionTintColor();
+ uiSettings.setAttributionTintColor(attributionTintColor != -1 ?
+ attributionTintColor : ColorUtils.getPrimaryColor(getContext()));
}
//
@@ -323,7 +357,7 @@ public class MapView extends FrameLayout {
@UiThread
public void onCreate(@Nullable Bundle savedInstanceState) {
// Force a check for an access token
- validateAccessToken(getAccessToken());
+ MapboxAccountManager.validateAccessToken(getAccessToken());
if (savedInstanceState != null && savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) {
@@ -335,9 +369,13 @@ public class MapView extends FrameLayout {
UiSettings uiSettings = mMapboxMap.getUiSettings();
uiSettings.setZoomGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_ZOOM_ENABLED));
+ uiSettings.setZoomGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_ZOOM_ENABLED_CHANGE));
uiSettings.setScrollGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_SCROLL_ENABLED));
+ uiSettings.setScrollGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_SCROLL_ENABLED_CHANGE));
uiSettings.setRotateGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_ROTATE_ENABLED));
+ uiSettings.setRotateGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_ROTATE_ENABLED_CHANGE));
uiSettings.setTiltGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_TILT_ENABLED));
+ uiSettings.setTiltGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_TILT_ENABLED_CHANGE));
uiSettings.setZoomControlsEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_ZOOM_CONTROLS_ENABLED));
// Compass
@@ -366,9 +404,6 @@ public class MapView extends FrameLayout {
mMapboxMap.setDebugActive(savedInstanceState.getBoolean(MapboxConstants.STATE_DEBUG_ACTIVE));
mMapboxMap.setStyleUrl(savedInstanceState.getString(MapboxConstants.STATE_STYLE_URL));
- setAccessToken(savedInstanceState.getString(MapboxConstants.STATE_ACCESS_TOKEN));
- mNativeMapView.setDefaultTransitionDuration(
- savedInstanceState.getLong(MapboxConstants.STATE_DEFAULT_TRANSITION_DURATION));
// User location
try {
@@ -384,10 +419,8 @@ public class MapView extends FrameLayout {
//noinspection ResourceType
trackingSettings.setMyBearingTrackingMode(savedInstanceState.getInt(MapboxConstants.STATE_MY_BEARING_TRACKING_MODE, MyBearingTracking.NONE));
} else if (savedInstanceState == null) {
- // Force a check for Telemetry
- validateTelemetryServiceConfigured();
-
// Start Telemetry (authorization determined in initial MapboxEventManager constructor)
+ Log.i(MapView.class.getCanonicalName(), "MapView start Telemetry...");
MapboxEventManager eventManager = MapboxEventManager.getMapboxEventManager();
eventManager.initialize(getContext(), getAccessToken());
}
@@ -400,21 +433,21 @@ public class MapView extends FrameLayout {
addOnMapChangedListener(new OnMapChangedListener() {
@Override
public void onMapChanged(@MapChange int change) {
- if (change == DID_FINISH_RENDERING_MAP_FULLY_RENDERED) {
+ if (change == WILL_START_RENDERING_MAP && mInitialLoad) {
+ mInitialLoad = false;
reloadIcons();
reloadMarkers();
adjustTopOffsetPixels();
- if (mInitialLoad) {
- mInitialLoad = false;
- if (mOnMapReadyCallbackList.size() > 0) {
- Iterator<OnMapReadyCallback> iterator = mOnMapReadyCallbackList.iterator();
- while (iterator.hasNext()) {
- OnMapReadyCallback callback = iterator.next();
- callback.onMapReady(mMapboxMap);
- iterator.remove();
- }
+ if (mOnMapReadyCallbackList.size() > 0) {
+ Iterator<OnMapReadyCallback> iterator = mOnMapReadyCallbackList.iterator();
+ while (iterator.hasNext()) {
+ OnMapReadyCallback callback = iterator.next();
+ callback.onMapReady(mMapboxMap);
+ iterator.remove();
}
}
+ } else if (change == REGION_IS_CHANGING || change == REGION_DID_CHANGE || change == DID_FINISH_LOADING_MAP) {
+ mMapboxMap.getMarkerViewManager().scheduleViewMarkerInvalidation();
}
}
});
@@ -440,9 +473,7 @@ public class MapView extends FrameLayout {
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, mStyleUrl);
- outState.putString(MapboxConstants.STATE_ACCESS_TOKEN, mMapboxMap.getAccessToken());
- outState.putLong(MapboxConstants.STATE_DEFAULT_TRANSITION_DURATION, mNativeMapView.getDefaultTransitionDuration());
+ outState.putString(MapboxConstants.STATE_STYLE_URL, mStyleInitializer.getStyle());
outState.putBoolean(MapboxConstants.STATE_MY_LOCATION_ENABLED, mMapboxMap.isMyLocationEnabled());
// TrackingSettings
@@ -453,9 +484,13 @@ public class MapView extends FrameLayout {
// UiSettings
UiSettings uiSettings = mMapboxMap.getUiSettings();
outState.putBoolean(MapboxConstants.STATE_ZOOM_ENABLED, uiSettings.isZoomGesturesEnabled());
+ outState.putBoolean(MapboxConstants.STATE_ZOOM_ENABLED_CHANGE, uiSettings.isZoomGestureChangeAllowed());
outState.putBoolean(MapboxConstants.STATE_SCROLL_ENABLED, uiSettings.isScrollGesturesEnabled());
+ outState.putBoolean(MapboxConstants.STATE_SCROLL_ENABLED_CHANGE, uiSettings.isScrollGestureChangeAllowed());
outState.putBoolean(MapboxConstants.STATE_ROTATE_ENABLED, uiSettings.isRotateGesturesEnabled());
+ outState.putBoolean(MapboxConstants.STATE_ROTATE_ENABLED_CHANGE, uiSettings.isRotateGestureChangeAllowed());
outState.putBoolean(MapboxConstants.STATE_TILT_ENABLED, uiSettings.isTiltGesturesEnabled());
+ outState.putBoolean(MapboxConstants.STATE_TILT_ENABLED_CHANGE, uiSettings.isTiltGestureChangeAllowed());
outState.putBoolean(MapboxConstants.STATE_ZOOM_CONTROLS_ENABLED, uiSettings.isZoomControlsEnabled());
// UiSettings - Compass
@@ -505,8 +540,7 @@ public class MapView extends FrameLayout {
getContext().unregisterReceiver(mConnectivityReceiver);
mConnectivityReceiver = null;
- mUserLocationView.onPause();
- mNativeMapView.pause();
+ mMyLocationView.onPause();
}
/**
@@ -518,13 +552,12 @@ public class MapView extends FrameLayout {
mConnectivityReceiver = new ConnectivityReceiver();
getContext().registerReceiver(mConnectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
- mNativeMapView.resume();
mNativeMapView.update();
- mUserLocationView.onResume();
+ mMyLocationView.onResume();
- if (mStyleUrl == null) {
+ if (mStyleInitializer.isDefaultStyle()) {
// user has failed to supply a style url
- setStyleUrl(Style.MAPBOX_STREETS);
+ setStyleUrl(mStyleInitializer.getStyle());
}
}
@@ -567,20 +600,18 @@ public class MapView extends FrameLayout {
return mNativeMapView.getPitch();
}
- void setTilt(Double pitch, @Nullable Long duration) {
- long actualDuration = 0;
- if (duration != null) {
- actualDuration = duration;
- }
- mNativeMapView.setPitch(pitch, actualDuration);
+ void setTilt(Double pitch) {
+ mMyLocationView.setTilt(pitch);
+ mNativeMapView.setPitch(pitch, 0);
}
+
//
// Direction
//
double getDirection() {
- if(mDestroyed){
+ if (mDestroyed) {
return 0;
}
@@ -641,6 +672,14 @@ public class MapView extends FrameLayout {
return mContentPaddingBottom;
}
+ int getContentWidth(){
+ return getWidth() - mContentPaddingLeft - mContentPaddingRight;
+ }
+
+ int getContentHeight(){
+ return getHeight() - mContentPaddingBottom - mContentPaddingTop;
+ }
+
//
// Zoom
//
@@ -744,7 +783,7 @@ public class MapView extends FrameLayout {
* <li>{@code asset://...}:
* reads the style from the APK {@code assets/} directory.
* This is used to load a style bundled with your app.</li>
- * <li>{@code null}: loads the default {@link Style#MAPBOX_STREETS} style.</li>
+ * <li>{@code null}: loads the default {@link Style#getMapboxStreetsUrl(int)} style.</li>
* </ul>
* <p>
* This method is asynchronous and will return immediately before the style finishes loading.
@@ -760,7 +799,7 @@ public class MapView extends FrameLayout {
if (mDestroyed) {
return;
}
- mStyleUrl = url;
+ mStyleInitializer.setStyle(url);
mNativeMapView.setStyleUrl(url);
}
@@ -794,7 +833,7 @@ public class MapView extends FrameLayout {
@UiThread
@NonNull
public String getStyleUrl() {
- return mStyleUrl;
+ return mStyleInitializer.getStyle();
}
//
@@ -803,16 +842,21 @@ public class MapView extends FrameLayout {
/**
* <p>
- * Sets the current Mapbox access token used to load map styles and tiles.
+ * DEPRECATED @see MapboxAccountManager#start(String)
* </p>
* <p>
+ * <p>
+ * Sets the current Mapbox access token used to load map styles and tiles.
+ * <p>
* You must set a valid access token before you call {@link MapView#onCreate(Bundle)}
* or an exception will be thrown.
* </p>
*
* @param accessToken Your public Mapbox access token.
* @see MapView#onCreate(Bundle)
+ * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)}
*/
+ @Deprecated
@UiThread
public void setAccessToken(@NonNull String accessToken) {
if (mDestroyed) {
@@ -822,15 +866,22 @@ public class MapView extends FrameLayout {
if (!TextUtils.isEmpty(accessToken)) {
accessToken = accessToken.trim();
}
- validateAccessToken(accessToken);
+ MapboxAccountManager.validateAccessToken(accessToken);
mNativeMapView.setAccessToken(accessToken);
}
/**
+ * <p>
+ * DEPRECATED @see MapboxAccountManager#getAccessToken()
+ * </p>
+ * <p/>
* Returns the current Mapbox access token used to load map styles and tiles.
+ * </p>
*
* @return The current Mapbox access token.
+ * @deprecated As of release 4.1.0, replaced by {@link MapboxAccountManager#getAccessToken()}
*/
+ @Deprecated
@UiThread
@Nullable
public String getAccessToken() {
@@ -840,31 +891,6 @@ public class MapView extends FrameLayout {
return mNativeMapView.getAccessToken();
}
- // Checks if the given token is valid
- private void validateAccessToken(String accessToken) {
- if (TextUtils.isEmpty(accessToken) || (!accessToken.startsWith("pk.") && !accessToken.startsWith("sk."))) {
- throw new InvalidAccessTokenException();
- }
- }
-
- // 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(MapboxConstants.TAG, "Error checking for Telemetry Service Config: " + e);
- }
- throw new TelemetryServiceNotConfiguredException();
- }
-
//
// Projection
//
@@ -873,22 +899,17 @@ public class MapView extends FrameLayout {
if (mDestroyed) {
return new LatLng();
}
- float x = point.x;
- float y = point.y;
-
- return mNativeMapView.latLngForPixel(new PointF(x / mScreenDensity, y / mScreenDensity));
+ point.set(point.x / mScreenDensity, point.y / mScreenDensity);
+ return mNativeMapView.latLngForPixel(point);
}
PointF toScreenLocation(@NonNull LatLng location) {
- if (mDestroyed) {
+ if (mDestroyed || location == null) {
return new PointF();
}
- PointF point = mNativeMapView.pixelForLatLng(location);
-
- float x = point.x * mScreenDensity;
- float y = point.y * mScreenDensity;
-
- return new PointF(x, y);
+ PointF pointF = mNativeMapView.pixelForLatLng(location);
+ pointF.set(pointF.x * mScreenDensity, pointF.y * mScreenDensity);
+ return pointF;
}
//
@@ -897,10 +918,24 @@ public class MapView extends FrameLayout {
Icon loadIconForMarker(Marker marker) {
Icon icon = marker.getIcon();
+
+ // calculating average before adding
+ int iconSize = mIcons.size() + 1;
+
+ // TODO replace former if case with anchor implementation,
+ // current workaround for having extra pixels is diving height by 2
if (icon == null) {
icon = IconFactory.getInstance(getContext()).defaultMarker();
+ Bitmap bitmap = icon.getBitmap();
+ mAverageIconHeight = mAverageIconHeight + (bitmap.getHeight() / 2 - mAverageIconHeight) / iconSize;
+ mAverageIconWidth = mAverageIconHeight + (bitmap.getWidth() - mAverageIconHeight) / iconSize;
marker.setIcon(icon);
+ } else {
+ Bitmap bitmap = icon.getBitmap();
+ mAverageIconHeight = mAverageIconHeight + (bitmap.getHeight() - mAverageIconHeight) / iconSize;
+ mAverageIconWidth = mAverageIconHeight + (bitmap.getWidth() - mAverageIconHeight) / iconSize;
}
+
if (!mIcons.contains(icon)) {
mIcons.add(icon);
loadIcon(icon);
@@ -930,7 +965,6 @@ public class MapView extends FrameLayout {
density = DisplayMetrics.DENSITY_DEFAULT;
}
float scale = density / DisplayMetrics.DENSITY_DEFAULT;
-
mNativeMapView.addAnnotationIcon(
id,
bitmap.getWidth(),
@@ -987,6 +1021,9 @@ public class MapView extends FrameLayout {
}
long addMarker(@NonNull Marker marker) {
+ if(mDestroyed){
+ return 0l;
+ }
return mNativeMapView.addMarker(marker);
}
@@ -1039,16 +1076,11 @@ public class MapView extends FrameLayout {
mNativeMapView.removeAnnotations(ids);
}
- private List<Marker> getMarkersInBounds(@NonNull LatLngBounds bbox) {
- if (mDestroyed) {
+ List<Marker> getMarkersInBounds(@NonNull LatLngBounds bbox) {
+ if (mDestroyed || bbox == null) {
return new ArrayList<>();
}
- if (bbox == null) {
- Log.w(MapboxConstants.TAG, "bbox was null, so just returning null");
- return null;
- }
-
// TODO: filter in JNI using C++ parameter to getAnnotationsInBounds
long[] ids = mNativeMapView.getAnnotationsInBounds(bbox);
@@ -1070,10 +1102,35 @@ public class MapView extends FrameLayout {
return new ArrayList<>(annotations);
}
+ public List<MarkerView> getMarkerViewsInBounds(@NonNull LatLngBounds bbox) {
+ if (mDestroyed || bbox == null) {
+ return new ArrayList<>();
+ }
+
+ // TODO: filter in JNI using C++ parameter to getAnnotationsInBounds
+ long[] ids = mNativeMapView.getAnnotationsInBounds(bbox);
+
+ List<Long> idsList = new ArrayList<>(ids.length);
+ for (int i = 0; i < ids.length; i++) {
+ idsList.add(ids[i]);
+ }
+
+ List<MarkerView> annotations = new ArrayList<>(ids.length);
+ List<Annotation> annotationList = mMapboxMap.getAnnotations();
+ int count = annotationList.size();
+ for (int i = 0; i < count; i++) {
+ Annotation annotation = annotationList.get(i);
+ if (annotation instanceof MarkerView && idsList.contains(annotation.getId())) {
+ annotations.add((MarkerView) annotation);
+ }
+ }
+
+ return new ArrayList<>(annotations);
+ }
+
+
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 (mDestroyed || mNativeMapView.isPaused()) {
+ if (mDestroyed) {
return 0;
}
@@ -1086,18 +1143,28 @@ public class MapView extends FrameLayout {
return;
}
- if (left == mContentPaddingLeft && top == mContentPaddingTop && right == mContentPaddingRight && bottom == mContentPaddingBottom) {
- return;
- }
+// if (left == mContentPaddingLeft && top == mContentPaddingTop && right == mContentPaddingRight && bottom == mContentPaddingBottom) {
+// return;
+// }
mContentPaddingLeft = left;
mContentPaddingTop = top;
mContentPaddingRight = right;
mContentPaddingBottom = bottom;
+ int[] userLocationViewPadding = mMapboxMap.getMyLocationViewSettings().getPadding();
+ left += userLocationViewPadding[0];
+ top += userLocationViewPadding[1];
+ right += userLocationViewPadding[2];
+ bottom += userLocationViewPadding[3];
+
mNativeMapView.setContentPadding(top / mScreenDensity, left / mScreenDensity, bottom / mScreenDensity, right / mScreenDensity);
}
+ public void invalidateContentPadding() {
+ setContentPadding(mContentPaddingLeft, mContentPaddingTop, mContentPaddingRight, mContentPaddingBottom);
+ }
+
double getMetersPerPixelAtLatitude(@FloatRange(from = -180, to = 180) double latitude) {
if (mDestroyed) {
return 0;
@@ -1118,7 +1185,7 @@ public class MapView extends FrameLayout {
mNativeMapView.jumpTo(bearing, center, pitch, zoom);
}
- void easeTo(double bearing, LatLng center, long duration, double pitch, double zoom, @Nullable final MapboxMap.CancelableCallback cancelableCallback) {
+ void easeTo(double bearing, LatLng center, long duration, double pitch, double zoom, boolean easingInterpolator, @Nullable final MapboxMap.CancelableCallback cancelableCallback) {
if (mDestroyed) {
return;
}
@@ -1139,7 +1206,7 @@ public class MapView extends FrameLayout {
});
}
- mNativeMapView.easeTo(bearing, center, duration, pitch, zoom);
+ mNativeMapView.easeTo(bearing, center, duration, pitch, zoom, easingInterpolator);
}
void flyTo(double bearing, LatLng center, long duration, double pitch, double zoom, @Nullable final MapboxMap.CancelableCallback cancelableCallback) {
@@ -1220,11 +1287,15 @@ public class MapView extends FrameLayout {
return;
}
- if (mDestroyed || mNativeMapView.isPaused()) {
+ if (mDestroyed) {
+ return;
+ }
+
+ if (!mHasSurface) {
return;
}
- mNativeMapView.renderSync();
+ mNativeMapView.render();
}
@Override
@@ -1250,6 +1321,10 @@ public class MapView extends FrameLayout {
private class SurfaceTextureListener implements TextureView.SurfaceTextureListener {
private Surface mSurface;
+ private View mViewHolder;
+
+ private static final int VIEW_MARKERS_POOL_SIZE = 20;
+
// Called when the native surface texture has been created
// Must do all EGL/GL ES initialization here
@@ -1257,12 +1332,15 @@ public class MapView extends FrameLayout {
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mNativeMapView.createSurface(mSurface = new Surface(surface));
mNativeMapView.resizeFramebuffer(width, height);
+ mHasSurface = true;
}
// Called when the native surface texture has been destroyed
// Must do all EGL/GL ES destruction here
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ mHasSurface = false;
+
if (mNativeMapView != null) {
mNativeMapView.destroySurface();
}
@@ -1274,7 +1352,7 @@ public class MapView extends FrameLayout {
// Must handle window resizing here.
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
- if(mDestroyed){
+ if (mDestroyed) {
return;
}
@@ -1285,12 +1363,14 @@ public class MapView extends FrameLayout {
// Must sync with UI here
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
- if(mDestroyed){
+ if (mDestroyed) {
return;
}
mCompassView.update(getDirection());
- mUserLocationView.update();
+ mMyLocationView.update();
+ mMapboxMap.getMarkerViewManager().update();
+
for (InfoWindow infoWindow : mMapboxMap.getInfoWindows()) {
infoWindow.update();
}
@@ -1432,7 +1512,8 @@ public class MapView extends FrameLayout {
case MotionEvent.ACTION_POINTER_DOWN:
// Second pointer down
- mTwoTap = event.getPointerCount() == 2;
+ mTwoTap = event.getPointerCount() == 2
+ && mMapboxMap.getUiSettings().isZoomGesturesEnabled();
if (mTwoTap) {
// Confirmed 2nd Finger Down
trackGestureEvent(MapboxEvent.GESTURE_TWO_FINGER_SINGLETAP, event.getX(), event.getY());
@@ -1517,8 +1598,7 @@ public class MapView extends FrameLayout {
zoom(true, e.getX(), e.getY());
} else {
// Zoom in on user location view
- PointF centerPoint = mUserLocationView.getMarkerScreenPoint();
- zoom(true, centerPoint.x, centerPoint.y);
+ zoom(true, mMyLocationView.getCenterX(), mMyLocationView.getCenterY());
}
break;
}
@@ -1540,17 +1620,15 @@ public class MapView extends FrameLayout {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
- // Open / Close InfoWindow
- PointF tapPoint = new PointF(e.getX(), e.getY());
-
List<Marker> selectedMarkers = mMapboxMap.getSelectedMarkers();
- final float toleranceSides = 15 * mScreenDensity;
- final float toleranceTop = 20 * mScreenDensity;
- final float toleranceBottom = 5 * mScreenDensity;
-
- RectF tapRect = new RectF(tapPoint.x - toleranceSides, tapPoint.y + toleranceTop,
- tapPoint.x + toleranceSides, tapPoint.y - toleranceBottom);
+ PointF tapPoint = new PointF(e.getX(), e.getY());
+ float toleranceSides = 4 * mScreenDensity;
+ float toleranceTopBottom = 10 * mScreenDensity;
+ RectF tapRect = new RectF(tapPoint.x - mAverageIconWidth / 2 - toleranceSides,
+ tapPoint.y - mAverageIconHeight / 2 - toleranceTopBottom,
+ tapPoint.x + mAverageIconWidth / 2 + toleranceSides,
+ tapPoint.y + mAverageIconHeight / 2 + toleranceTopBottom);
LatLngBounds.Builder builder = new LatLngBounds.Builder();
builder.include(fromScreenLocation(new PointF(tapRect.left, tapRect.bottom)));
@@ -1585,7 +1663,10 @@ public class MapView extends FrameLayout {
if (annotation instanceof Marker) {
if (annotation.getId() == newSelectedMarkerId) {
if (selectedMarkers.isEmpty() || !selectedMarkers.contains(annotation)) {
- mMapboxMap.selectMarker((Marker) annotation);
+ // only handle click if no marker view is available
+ if (!(annotation instanceof MarkerView)) {
+ mMapboxMap.selectMarker((Marker) annotation);
+ }
}
break;
}
@@ -1625,9 +1706,7 @@ public class MapView extends FrameLayout {
}
// reset tracking modes if gesture occurs
- if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) {
- resetTrackingModes();
- }
+ resetTrackingModesIfRequired();
// Fling the map
float ease = 0.25f;
@@ -1663,10 +1742,9 @@ public class MapView extends FrameLayout {
return false;
}
- if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) {
- // reset tracking modes if gesture occurs
- resetTrackingModes();
- }
+ // reset tracking modes if gesture occurs
+ resetTrackingModesIfRequired();
+
// Cancel any animation
mNativeMapView.cancelTransitions();
@@ -1695,10 +1773,8 @@ public class MapView extends FrameLayout {
return false;
}
- if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) {
- // reset tracking modes if gesture occurs
- resetTrackingModes();
- }
+ // reset tracking modes if gesture occurs
+ resetTrackingModesIfRequired();
mBeginTime = detector.getEventTime();
trackGestureEvent(MapboxEvent.GESTURE_PINCH_START, detector.getFocusX(), detector.getFocusY());
@@ -1758,8 +1834,9 @@ public class MapView extends FrameLayout {
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);
+ float x = mMyLocationView.getX() + mMyLocationView.getWidth() / 2;
+ float y = mMyLocationView.getY() + mMyLocationView.getHeight() / 2;
+ mNativeMapView.scaleBy(detector.getScaleFactor(), x / mScreenDensity, y / mScreenDensity);
}
}
return true;
@@ -1780,10 +1857,8 @@ public class MapView extends FrameLayout {
return false;
}
- if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) {
- // reset tracking modes if gesture occurs
- resetTrackingModes();
- }
+ // reset tracking modes if gesture occurs
+ resetTrackingModesIfRequired();
mBeginTime = detector.getEventTime();
trackGestureEvent(MapboxEvent.GESTURE_ROTATION_START, detector.getFocusX(), detector.getFocusY());
@@ -1840,8 +1915,9 @@ public class MapView extends FrameLayout {
detector.getFocusY() / mScreenDensity);
} else {
// around center userlocation
- PointF centerPoint = mUserLocationView.getMarkerScreenPoint();
- mNativeMapView.setBearing(bearing, centerPoint.x / mScreenDensity, centerPoint.y / mScreenDensity);
+ float x = mMyLocationView.getX() + mMyLocationView.getWidth() / 2;
+ float y = mMyLocationView.getY() + mMyLocationView.getHeight() / 2;
+ mNativeMapView.setBearing(bearing, x / mScreenDensity, y / mScreenDensity);
}
return true;
}
@@ -1861,10 +1937,8 @@ public class MapView extends FrameLayout {
return false;
}
- if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) {
- // reset tracking modes if gesture occurs
- resetTrackingModes();
- }
+ // reset tracking modes if gesture occurs
+ resetTrackingModesIfRequired();
mBeginTime = detector.getEventTime();
trackGestureEvent(MapboxEvent.GESTURE_PITCH_START, detector.getFocusX(), detector.getFocusY());
@@ -1912,14 +1986,14 @@ public class MapView extends FrameLayout {
pitch = Math.max(MapboxConstants.MINIMUM_TILT, Math.min(MapboxConstants.MAXIMUM_TILT, pitch));
// Tilt the map
- setTilt(pitch, null);
+ mMapboxMap.setTilt(pitch);
return true;
}
}
// This class handles input events from the zoom control buttons
-// Zoom controls allow single touch only devices to zoom in and out
+ // Zoom controls allow single touch only devices to zoom in and out
private class OnZoomListener implements ZoomButtonsController.OnZoomListener {
// Not used
@@ -2269,7 +2343,6 @@ public class MapView extends FrameLayout {
* @param listener The callback that's invoked on every frame rendered to the map view.
* @see MapView#removeOnMapChangedListener(OnMapChangedListener)
*/
- @UiThread
public void addOnMapChangedListener(@Nullable OnMapChangedListener listener) {
if (listener != null) {
mOnMapChangedListener.add(listener);
@@ -2282,7 +2355,6 @@ public class MapView extends FrameLayout {
* @param listener The previously added callback to remove.
* @see MapView#addOnMapChangedListener(OnMapChangedListener)
*/
- @UiThread
public void removeOnMapChangedListener(@Nullable OnMapChangedListener listener) {
if (listener != null) {
mOnMapChangedListener.remove(listener);
@@ -2308,22 +2380,35 @@ public class MapView extends FrameLayout {
//
void setMyLocationEnabled(boolean enabled) {
- mUserLocationView.setEnabled(enabled);
+ mMyLocationView.setEnabled(enabled);
}
Location getMyLocation() {
- return mUserLocationView.getLocation();
+ return mMyLocationView.getLocation();
}
- void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener listener) {
- mUserLocationView.setOnMyLocationChangeListener(listener);
+ void setOnMyLocationChangeListener(@Nullable final MapboxMap.OnMyLocationChangeListener listener) {
+ if (listener != null) {
+ mMyLocationListener = new LocationListener() {
+ @Override
+ public void onLocationChanged(Location location) {
+ if (listener != null) {
+ listener.onMyLocationChange(location);
+ }
+ }
+ };
+ LocationServices.getLocationServices(getContext()).addLocationListener(mMyLocationListener);
+ } else {
+ LocationServices.getLocationServices(getContext()).removeLocationListener(mMyLocationListener);
+ mMyLocationListener = null;
+ }
}
void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) {
if (myLocationTrackingMode != MyLocationTracking.TRACKING_NONE && !mMapboxMap.isMyLocationEnabled()) {
mMapboxMap.setMyLocationEnabled(true);
}
- mUserLocationView.setMyLocationTrackingMode(myLocationTrackingMode);
+ mMyLocationView.setMyLocationTrackingMode(myLocationTrackingMode);
MapboxMap.OnMyLocationTrackingModeChangeListener listener = mMapboxMap.getOnMyLocationTrackingModeChangeListener();
if (listener != null) {
listener.onMyLocationTrackingModeChange(myLocationTrackingMode);
@@ -2334,7 +2419,7 @@ public class MapView extends FrameLayout {
if (myBearingTrackingMode != MyBearingTracking.NONE && !mMapboxMap.isMyLocationEnabled()) {
mMapboxMap.setMyLocationEnabled(true);
}
- mUserLocationView.setMyBearingTrackingMode(myBearingTrackingMode);
+ mMyLocationView.setMyBearingTrackingMode(myBearingTrackingMode);
MapboxMap.OnMyBearingTrackingModeChangeListener listener = mMapboxMap.getOnMyBearingTrackingModeChangeListener();
if (listener != null) {
listener.onMyBearingTrackingModeChange(myBearingTrackingMode);
@@ -2346,11 +2431,28 @@ public class MapView extends FrameLayout {
ContextCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
}
- private void resetTrackingModes() {
+ private void resetTrackingModesIfRequired() {
+ TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings();
+ if (trackingSettings.isDismissLocationTrackingOnGesture()) {
+ resetLocationTrackingMode();
+ }
+ if (trackingSettings.isDismissBearingTrackingOnGesture()) {
+ resetBearingTrackingMode();
+ }
+ }
+
+ private void resetLocationTrackingMode() {
try {
TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings();
trackingSettings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_NONE);
- trackingSettings.setMyBearingTrackingMode(MyBearingTracking.NONE);
+ } catch (SecurityException ignore) {
+ // User did not accept location permissions
+ }
+ }
+
+ private void resetBearingTrackingMode() {
+ try {
+ setMyBearingTrackingMode(MyBearingTracking.NONE);
} catch (SecurityException ignore) {
// User did not accept location permissions
}
@@ -2404,6 +2506,13 @@ public class MapView extends FrameLayout {
mAttributionsView.setVisibility(visibility);
}
+ void setAtttibutionTintColor(int tintColor) {
+ ColorUtils.setTintList(mAttributionsView, tintColor);
+ }
+
+ int getAttributionTintColor() {
+ return mMapboxMap.getUiSettings().getAttributionTintColor();
+ }
//
// Custom layer
@@ -2455,6 +2564,26 @@ public class MapView extends FrameLayout {
mMapboxMap = mapboxMap;
}
+ MyLocationView getUserLocationView() {
+ return mMyLocationView;
+ }
+
+ @UiThread
+ void snapshot(@NonNull final MapboxMap.SnapshotReadyCallback callback, @Nullable final Bitmap bitmap) {
+ TextureView textureView = (TextureView) findViewById(R.id.textureView);
+ final boolean canUseBitmap = bitmap != null && textureView.getWidth() == bitmap.getWidth() && textureView.getHeight() == bitmap.getHeight();
+
+ setDrawingCacheEnabled(true);
+ Bitmap content = Bitmap.createBitmap(getDrawingCache());
+ setDrawingCacheEnabled(false);
+
+ Bitmap output = Bitmap.createBitmap(content.getWidth(), content.getHeight(), Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(output);
+ canvas.drawBitmap(canUseBitmap ? textureView.getBitmap(bitmap) : textureView.getBitmap(), 0, 0, null);
+ canvas.drawBitmap(content, new Matrix(), null);
+ callback.onSnapshotReady(output);
+ }
+
//
// View utility methods
//
@@ -2494,7 +2623,8 @@ public class MapView extends FrameLayout {
AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.AttributionAlertDialogStyle);
builder.setTitle(R.string.attributionsDialogTitle);
builder.setAdapter(new ArrayAdapter<>(context, R.layout.attribution_list_item, items), this);
- builder.show();
+ AlertDialog dialog = builder.show();
+ dialog.getWindow().setBackgroundDrawable(new ColorDrawable(mMapView.getAttributionTintColor()));
}
// Called when someone selects an attribution, 'Improve this map' adds location data to the url
@@ -2570,6 +2700,41 @@ public class MapView extends FrameLayout {
}
/**
+ * Class responsible for managing state of Style loading.
+ */
+ static class StyleInitializer {
+
+ private String mStyle;
+ private boolean mDefaultStyle;
+
+ StyleInitializer(@NonNull Context context) {
+ mStyle = Style.getMapboxStreetsUrl(context.getResources().getInteger(R.integer.style_version));
+ mDefaultStyle = true;
+ }
+
+ void setStyle(@NonNull String style) {
+ setStyle(style, false);
+ }
+
+ void setStyle(@NonNull String style, boolean defaultStyle) {
+ if (style == null) {
+ // don't override default style
+ return;
+ }
+ mStyle = style;
+ mDefaultStyle = defaultStyle;
+ }
+
+ public String getStyle() {
+ return mStyle;
+ }
+
+ boolean isDefaultStyle() {
+ return mDefaultStyle;
+ }
+ }
+
+ /**
* Definition of a map change event.
*
* @see MapView.OnMapChangedListener#onMapChanged(int)
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 e1a9cd8cdf..fbe6a31f6a 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,5 +1,7 @@
package com.mapbox.mapboxsdk.maps;
+import android.content.Context;
+import android.graphics.Bitmap;
import android.location.Location;
import android.os.SystemClock;
import android.support.annotation.FloatRange;
@@ -7,16 +9,23 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.v4.util.LongSparseArray;
+import android.support.v4.util.Pools;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
+import com.mapbox.mapboxsdk.MapboxAccountManager;
import com.mapbox.mapboxsdk.annotations.Annotation;
import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
+import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
import com.mapbox.mapboxsdk.annotations.Icon;
+import com.mapbox.mapboxsdk.annotations.IconFactory;
import com.mapbox.mapboxsdk.annotations.InfoWindow;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
+import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
import com.mapbox.mapboxsdk.annotations.Polygon;
import com.mapbox.mapboxsdk.annotations.PolygonOptions;
import com.mapbox.mapboxsdk.annotations.Polyline;
@@ -30,7 +39,10 @@ import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.layers.CustomLayer;
+import com.mapbox.mapboxsdk.location.LocationListener;
+import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
+import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -49,13 +61,18 @@ public class MapboxMap {
private MapView mMapView;
private UiSettings mUiSettings;
private TrackingSettings mTrackingSettings;
+ private MyLocationViewSettings myLocationViewSettings;
private Projection mProjection;
private CameraPosition mCameraPosition;
private boolean mInvalidCameraPosition;
private LongSparseArray<Annotation> mAnnotations;
+
private List<Marker> mSelectedMarkers;
+ private MarkerViewManager mMarkerViewManager;
+
private List<InfoWindow> mInfoWindows;
private MapboxMap.InfoWindowAdapter mInfoWindowAdapter;
+ private Bitmap mViewMarkerBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
private boolean mMyLocationEnabled;
private boolean mAllowConcurrentMultipleInfoWindows;
@@ -85,6 +102,7 @@ public class MapboxMap {
mAnnotations = new LongSparseArray<>();
mSelectedMarkers = new ArrayList<>();
mInfoWindows = new ArrayList<>();
+ mMarkerViewManager = new MarkerViewManager(this, mapView);
}
//
@@ -186,6 +204,20 @@ public class MapboxMap {
}
//
+ // MyLocationViewSettings
+ //
+
+ /**
+ * Gets the settings of the user location for the map.
+ */
+ public MyLocationViewSettings getMyLocationViewSettings() {
+ if (myLocationViewSettings == null) {
+ myLocationViewSettings = new MyLocationViewSettings(mMapView, mMapView.getUserLocationView());
+ }
+ return myLocationViewSettings;
+ }
+
+ //
// Projection
//
@@ -254,10 +286,12 @@ public class MapboxMap {
}
/**
- * Ease the map according to the update with an animation over a specified duration, and calls an optional callback on completion. See CameraUpdateFactory for a set of updates.
- * If getCameraPosition() is called during the animation, it will return the current location of the camera in flight.
+ * Gradually move the camera by the default duration, zoom will not be affected unless specified
+ * within {@link CameraUpdate}. If {@link #getCameraPosition()} is called during the animation,
+ * it will return the current location of the camera in flight.
*
* @param update The change that should be applied to the camera.
+ * @see {@link CameraUpdateFactory} for a set of updates.
*/
@UiThread
public final void easeCamera(CameraUpdate update) {
@@ -265,11 +299,14 @@ public class MapboxMap {
}
/**
- * Ease the map according to the update with an animation over a specified duration, and calls an optional callback on completion. See CameraUpdateFactory for a set of updates.
- * If getCameraPosition() is called during the animation, it will return the current location of the camera in flight.
+ * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected
+ * unless specified within {@link CameraUpdate}. If {@link #getCameraPosition()} is called
+ * during the animation, it will return the current location of the camera in flight.
*
* @param update The change that should be applied to the camera.
- * @param durationMs The duration of the animation in milliseconds. This must be strictly positive, otherwise an IllegalArgumentException will be thrown.
+ * @param durationMs The duration of the animation in milliseconds. This must be strictly
+ * positive, otherwise an IllegalArgumentException will be thrown.
+ * @see {@link CameraUpdateFactory} for a set of updates.
*/
@UiThread
public final void easeCamera(CameraUpdate update, int durationMs) {
@@ -277,17 +314,35 @@ public class MapboxMap {
}
/**
- * Ease the map according to the update with an animation over a specified duration, and calls an optional callback on completion. See CameraUpdateFactory for a set of updates.
- * If getCameraPosition() is called during the animation, it will return the current location of the camera in flight.
+ * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected
+ * unless specified within {@link CameraUpdate}. A callback can be used to be notified when
+ * easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it
+ * will return the current location of the camera in flight.
*
* @param update The change that should be applied to the camera.
- * @param durationMs The duration of the animation in milliseconds. This must be strictly positive, otherwise an IllegalArgumentException will be thrown.
- * @param callback An optional callback to be notified from the main thread when the animation stops. If the animation stops due to its natural completion, the callback will be notified with onFinish(). If the animation stops due to interruption by a later camera movement or a user gesture, onCancel() will be called. The callback should not attempt to move or animate the camera in its cancellation method. If a callback isn't required, leave it as null.
+ * @param durationMs The duration of the animation in milliseconds. This must be strictly
+ * positive, otherwise an IllegalArgumentException will be thrown.
+ * @param callback An optional callback to be notified from the main thread when the animation
+ * stops. If the animation stops due to its natural completion, the callback
+ * will be notified with onFinish(). If the animation stops due to interruption
+ * by a later camera movement or a user gesture, onCancel() will be called.
+ * Do not update or ease the camera from within onCancel().
+ * @see {@link CameraUpdateFactory} for a set of camera updates.
*/
@UiThread
public final void easeCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) {
+ easeCamera(update, durationMs, true, callback);
+ }
+
+ @UiThread
+ public final void easeCamera(CameraUpdate update, int durationMs, boolean easingInterpolator) {
+ easeCamera(update, durationMs, easingInterpolator, null);
+ }
+
+ @UiThread
+ public final void easeCamera(CameraUpdate update, int durationMs, boolean easingInterpolator, final MapboxMap.CancelableCallback callback) {
mCameraPosition = update.getCameraPosition(this);
- mMapView.easeTo(mCameraPosition.bearing, mCameraPosition.target, getDurationNano(durationMs), mCameraPosition.tilt, mCameraPosition.zoom, new CancelableCallback() {
+ mMapView.easeTo(mCameraPosition.bearing, mCameraPosition.target, getDurationNano(durationMs), mCameraPosition.tilt, mCameraPosition.zoom, easingInterpolator, new CancelableCallback() {
@Override
public void onCancel() {
if (callback != null) {
@@ -307,12 +362,13 @@ public class MapboxMap {
}
/**
- * Animates the movement of the camera from the current position to the position defined in the update.
- * During the animation, a call to getCameraPosition() returns an intermediate location of the camera.
- * <p/>
- * See CameraUpdateFactory for a set of updates.
+ * Animate the camera to a new location defined within {@link CameraUpdate} using a transition
+ * animation that evokes powered flight. The animation will last the default amount of time.
+ * During the animation, a call to {@link #getCameraPosition()} returns an intermediate location
+ * of the camera in flight.
*
* @param update The change that should be applied to the camera.
+ * @see {@link CameraUpdateFactory} for a set of updates.
*/
@UiThread
public final void animateCamera(CameraUpdate update) {
@@ -320,12 +376,16 @@ public class MapboxMap {
}
/**
- * Animates the movement of the camera from the current position to the position defined in the update and calls an optional callback on completion.
- * See CameraUpdateFactory for a set of updates.
- * During the animation, a call to getCameraPosition() returns an intermediate location of the camera.
+ * Animate the camera to a new location defined within {@link CameraUpdate} using a transition
+ * animation that evokes powered flight. The animation will last the default amount of time. A
+ * callback can be used to be notified when animating the camera stops. During the animation, a
+ * call to {@link #getCameraPosition()} returns an intermediate location of the camera in flight.
*
* @param update The change that should be applied to the camera.
- * @param callback The callback to invoke from the main thread when the animation stops. If the animation completes normally, onFinish() is called; otherwise, onCancel() is called. Do not update or animate the camera from within onCancel().
+ * @param callback The callback to invoke from the main thread when the animation stops. If the
+ * animation completes normally, onFinish() is called; otherwise, onCancel() is
+ * called. Do not update or animate the camera from within onCancel().
+ * @see {@link CameraUpdateFactory} for a set of updates.
*/
@UiThread
public final void animateCamera(CameraUpdate update, MapboxMap.CancelableCallback callback) {
@@ -333,11 +393,15 @@ public class MapboxMap {
}
/**
- * Moves the map according to the update with an animation over a specified duration. See CameraUpdateFactory for a set of updates.
- * If getCameraPosition() is called during the animation, it will return the current location of the camera in flight.
+ * Animate the camera to a new location defined within {@link CameraUpdate} using a transition
+ * animation that evokes powered flight. The animation will last a specified amount of time
+ * given in milliseconds. During the animation, a call to {@link #getCameraPosition()} returns
+ * an intermediate location of the camera in flight.
*
* @param update The change that should be applied to the camera.
- * @param durationMs The duration of the animation in milliseconds. This must be strictly positive, otherwise an IllegalArgumentException will be thrown.
+ * @param durationMs The duration of the animation in milliseconds. This must be strictly
+ * positive, otherwise an IllegalArgumentException will be thrown.
+ * @see {@link CameraUpdateFactory} for a set of updates.
*/
@UiThread
public final void animateCamera(CameraUpdate update, int durationMs) {
@@ -345,12 +409,22 @@ public class MapboxMap {
}
/**
- * Moves the map according to the update with an animation over a specified duration, and calls an optional callback on completion. See CameraUpdateFactory for a set of updates.
- * If getCameraPosition() is called during the animation, it will return the current location of the camera in flight.
+ * Animate the camera to a new location defined within {@link CameraUpdate} using a transition
+ * animation that evokes powered flight. The animation will last a specified amount of time
+ * given in milliseconds. A callback can be used to be notified when animating the camera stops.
+ * During the animation, a call to {@link #getCameraPosition()} returns an intermediate location
+ * of the camera in flight.
*
* @param update The change that should be applied to the camera.
- * @param durationMs The duration of the animation in milliseconds. This must be strictly positive, otherwise an IllegalArgumentException will be thrown.
- * @param callback An optional callback to be notified from the main thread when the animation stops. If the animation stops due to its natural completion, the callback will be notified with onFinish(). If the animation stops due to interruption by a later camera movement or a user gesture, onCancel() will be called. The callback should not attempt to move or animate the camera in its cancellation method. If a callback isn't required, leave it as null.
+ * @param durationMs The duration of the animation in milliseconds. This must be strictly
+ * positive, otherwise an IllegalArgumentException will be thrown.
+ * @param callback An optional callback to be notified from the main thread when the animation
+ * stops. If the animation stops due to its natural completion, the callback
+ * will be notified with onFinish(). If the animation stops due to interruption
+ * by a later camera movement or a user gesture, onCancel() will be called.
+ * Do not update or animate the camera from within onCancel(). If a callback
+ * isn't required, leave it as null.
+ * @see {@link CameraUpdateFactory} for a set of updates.
*/
@UiThread
public final void animateCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) {
@@ -476,7 +550,7 @@ public class MapboxMap {
* <li>{@code asset://...}:
* reads the style from the APK {@code assets/} directory.
* This is used to load a style bundled with your app.</li>
- * <li>{@code null}: loads the default {@link Style#MAPBOX_STREETS} style.</li>
+ * <li>{@code null}: loads the default {@link Style#getMapboxStreetsUrl(int)} style.</li>
* </ul>
* <p>
* This method is asynchronous and will return immediately before the style finishes loading.
@@ -506,8 +580,10 @@ public class MapboxMap {
*
* @param style The bundled style. Accepts one of the values from {@link Style}.
* @see Style
+ * @deprecated use {@link #setStyleUrl(String)} instead with versioned url methods from {@link Style}
*/
@UiThread
+ @Deprecated
public void setStyle(@Style.StyleUrl String style) {
setStyleUrl(style);
}
@@ -532,22 +608,34 @@ public class MapboxMap {
/**
* <p>
+ * DEPRECATED @see MapboxAccountManager#start(String)
+ * </p>
+ * <p>
* Sets the current Mapbox access token used to load map styles and tiles.
* </p>
*
* @param accessToken Your public Mapbox access token.
* @see MapView#setAccessToken(String)
+ * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)}
*/
+ @Deprecated
@UiThread
public void setAccessToken(@NonNull String accessToken) {
mMapView.setAccessToken(accessToken);
}
/**
+ * <p>
+ * DEPRECATED @see MapboxAccountManager#getAccessToken()
+ * </p>
+ * <p>
* Returns the current Mapbox access token used to load map styles and tiles.
+ * </p>
*
* @return The current Mapbox access token.
+ * @deprecated As of release 4.1.0, replaced by {@link MapboxAccountManager#getAccessToken()}
*/
+ @Deprecated
@UiThread
@Nullable
public String getAccessToken() {
@@ -558,6 +646,12 @@ public class MapboxMap {
// Annotations
//
+ void setTilt(double tilt) {
+ mMarkerViewManager.setTilt((float) tilt);
+ mMapView.setTilt(tilt);
+ }
+
+
/**
* <p>
* Adds a marker to this map.
@@ -597,6 +691,27 @@ public class MapboxMap {
/**
* <p>
+ * Adds a marker to this map.
+ * </p>
+ * The marker's icon is rendered on the map at the location {@code Marker.position}.
+ * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet.
+ *
+ * @param markerOptions A marker options object that defines how to render the marker.
+ * @return The {@code Marker} that was added to the map.
+ */
+ @UiThread
+ @NonNull
+ public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions) {
+ MarkerView marker = prepareViewMarker(markerOptions);
+ long id = mMapView.addMarker(marker);
+ marker.setMapboxMap(this);
+ marker.setId(id);
+ mAnnotations.put(id, 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}.
@@ -607,11 +722,11 @@ public class MapboxMap {
*/
@UiThread
@NonNull
- public List<Marker> addMarkers(@NonNull List<MarkerOptions> markerOptionsList) {
+ public List<Marker> addMarkers(@NonNull List<? extends BaseMarkerOptions> markerOptionsList) {
int count = markerOptionsList.size();
List<Marker> markers = new ArrayList<>(count);
if (count > 0) {
- MarkerOptions markerOptions;
+ BaseMarkerOptions markerOptions;
Marker marker;
for (int i = 0; i < count; i++) {
markerOptions = markerOptionsList.get(i);
@@ -693,7 +808,7 @@ public class MapboxMap {
Polyline polyline;
List<Polyline> polylines = new ArrayList<>(count);
- if(count>0) {
+ if (count > 0) {
for (PolylineOptions options : polylineOptionsList) {
polyline = options.getPolyline();
if (!polyline.getPoints().isEmpty()) {
@@ -757,7 +872,7 @@ public class MapboxMap {
Polygon polygon;
List<Polygon> polygons = new ArrayList<>(count);
- if(count>0) {
+ if (count > 0) {
for (PolygonOptions polygonOptions : polygonOptionsList) {
polygon = polygonOptions.getPolygon();
if (!polygon.getPoints().isEmpty()) {
@@ -767,8 +882,8 @@ public class MapboxMap {
long[] ids = mMapView.addPolygons(polygons);
- // if unit tests or polygons correcly added to map
- if(ids==null || ids.length==polygons.size()) {
+ // if unit tests or polygons correctly added to map
+ if (ids == null || ids.length == polygons.size()) {
long id = 0;
for (int i = 0; i < polygons.size(); i++) {
polygon = polygons.get(i);
@@ -834,7 +949,11 @@ public class MapboxMap {
@UiThread
public void removeAnnotation(@NonNull Annotation annotation) {
if (annotation instanceof Marker) {
- ((Marker) annotation).hideInfoWindow();
+ Marker marker = (Marker) annotation;
+ marker.hideInfoWindow();
+ if (marker instanceof MarkerView) {
+ mMarkerViewManager.removeMarkerView((MarkerView) marker, true);
+ }
}
long id = annotation.getId();
mMapView.removeAnnotation(id);
@@ -864,7 +983,11 @@ public class MapboxMap {
for (int i = 0; i < count; i++) {
Annotation annotation = annotationList.get(i);
if (annotation instanceof Marker) {
- ((Marker) annotation).hideInfoWindow();
+ Marker marker = (Marker) annotation;
+ marker.hideInfoWindow();
+ if (marker instanceof MarkerView) {
+ mMarkerViewManager.removeMarkerView((MarkerView) marker, true);
+ }
}
ids[i] = annotationList.get(i).getId();
}
@@ -886,7 +1009,11 @@ public class MapboxMap {
ids[i] = mAnnotations.keyAt(i);
annotation = mAnnotations.get(ids[i]);
if (annotation instanceof Marker) {
- ((Marker) annotation).hideInfoWindow();
+ Marker marker = (Marker) annotation;
+ marker.hideInfoWindow();
+ if (marker instanceof MarkerView) {
+ mMarkerViewManager.removeMarkerView((MarkerView) marker, true);
+ }
}
}
mMapView.removeAnnotations(ids);
@@ -894,11 +1021,18 @@ public class MapboxMap {
}
/**
+ * Removes all markers, polylines, polygons, overlays, etc from the map.
+ */
+ @UiThread
+ public void clear() {
+ removeAnnotations();
+ }
+
+ /**
* 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);
@@ -989,8 +1123,7 @@ public class MapboxMap {
@UiThread
public void selectMarker(@NonNull Marker marker) {
if (marker == null) {
- Log.w(MapboxConstants.TAG, "marker was null, so just" +
- " returning");
+ Log.w(MapboxConstants.TAG, "marker was null, so just returning");
return;
}
@@ -1031,6 +1164,10 @@ public class MapboxMap {
if (marker.isInfoWindowShown()) {
marker.hideInfoWindow();
}
+
+ if (marker instanceof MarkerView) {
+ mMarkerViewManager.deselect((MarkerView) marker);
+ }
}
// Removes all selected markers from the list
@@ -1070,6 +1207,22 @@ public class MapboxMap {
return marker;
}
+ private MarkerView prepareViewMarker(BaseMarkerViewOptions markerViewOptions) {
+ MarkerView marker = markerViewOptions.getMarker();
+ Icon icon = IconFactory.recreate("markerViewSettings", mViewMarkerBitmap);
+ marker.setIcon(icon);
+ return marker;
+ }
+
+ /**
+ * Get the MarkerViewManager associated to the MapView.
+ *
+ * @return the associated MarkerViewManager
+ */
+ public MarkerViewManager getMarkerViewManager() {
+ return mMarkerViewManager;
+ }
+
//
// InfoWindow
//
@@ -1142,6 +1295,7 @@ public class MapboxMap {
* view’s frame. Otherwise, those properties are inset, excluding part of the
* frame from the viewport. For instance, if the only the top edge is inset, the
* map center is effectively shifted downward.
+ * </p>
*
* @param left The left margin in pixels.
* @param top The top margin in pixels.
@@ -1151,8 +1305,6 @@ public class MapboxMap {
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()));
}
/**
@@ -1381,8 +1533,10 @@ public class MapboxMap {
*
* @param listener The callback that's invoked when the user clicks on a marker.
* To unset the callback, use null.
+ * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.location.LocationServices#addLocationListener(LocationListener)})}
*/
@UiThread
+ @Deprecated
public void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener listener) {
mMapView.setOnMyLocationChangeListener(listener);
}
@@ -1451,6 +1605,14 @@ public class MapboxMap {
return mMapView;
}
+ void setUiSettings(UiSettings uiSettings) {
+ mUiSettings = uiSettings;
+ }
+
+ void setProjection(Projection projection) {
+ mProjection = projection;
+ }
+
//
// Invalidate
//
@@ -1462,6 +1624,27 @@ public class MapboxMap {
mMapView.update();
}
+ /**
+ * Takes a snapshot of the map.
+ *
+ * @param callback Callback method invoked when the snapshot is taken.
+ * @param bitmap A pre-allocated bitmap.
+ */
+ @UiThread
+ public void snapshot(@NonNull SnapshotReadyCallback callback, @Nullable final Bitmap bitmap) {
+ mMapView.snapshot(callback, bitmap);
+ }
+
+ /**
+ * Takes a snapshot of the map.
+ *
+ * @param callback Callback method invoked when the snapshot is taken.
+ */
+ @UiThread
+ public void snapshot(@NonNull SnapshotReadyCallback callback) {
+ mMapView.snapshot(callback, null);
+ }
+
//
// Interfaces
//
@@ -1624,10 +1807,92 @@ public class MapboxMap {
}
/**
+ * Interface definition for a callback to be invoked when an MarkerView will be shown.
+ *
+ * @param <U> the instance type of MarkerView
+ */
+ public static abstract class MarkerViewAdapter<U extends MarkerView> {
+
+ private Context context;
+ private final Class<U> persistentClass;
+ private final Pools.SimplePool<View> mViewReusePool;
+
+ /**
+ * Create an instance of MarkerViewAdapter.
+ *
+ * @param context the context associated to a MapView
+ */
+ @SuppressWarnings("unchecked")
+ public MarkerViewAdapter(Context context) {
+ this.context = context;
+ persistentClass = (Class<U>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
+ mViewReusePool = new Pools.SimplePool<>(20);
+ }
+
+ /**
+ * Called when an MarkerView will be added to the MapView.
+ *
+ * @param marker the model representing the MarkerView
+ * @param convertView the reusable view
+ * @param parent the parent ViewGroup of the convertview
+ * @return the View that is adapted to the contents of MarkerView
+ */
+ @Nullable
+ public abstract View getView(@NonNull U marker, @NonNull View convertView, @NonNull ViewGroup parent);
+
+ /**
+ * Returns the generic type of the used MarkerView.
+ *
+ * @return the generic type
+ */
+ public Class<U> getMarkerClass() {
+ return persistentClass;
+ }
+
+ /**
+ * Returns the pool used to store reusable Views.
+ *
+ * @return the pool associated to this adapter
+ */
+ public Pools.SimplePool<View> getViewReusePool() {
+ return mViewReusePool;
+ }
+
+ /**
+ * Returns the context associated to the hosting MapView.
+ *
+ * @return the context used
+ */
+ public Context getContext() {
+ return context;
+ }
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the user clicks on a MarkerView.
+ *
+ * @see MarkerViewManager#setOnMarkerViewClickListener(OnMarkerViewClickListener)
+ */
+ public interface OnMarkerViewClickListener {
+
+ /**
+ * Called when the user clicks on a MarkerView.
+ *
+ * @param marker the MarkerView associated to the clicked View
+ * @param view the clicked View
+ * @param adapter the adapter used to adapt the MarkerView to the View
+ * @return If true the listener has consumed the event and the info window will not be shown
+ */
+ boolean onMarkerClick(@NonNull Marker marker, @NonNull View view, @NonNull MarkerViewAdapter adapter);
+ }
+
+ /**
* Interface definition for a callback to be invoked when the the My Location view changes location.
*
* @see MapboxMap#setOnMyLocationChangeListener(OnMyLocationChangeListener)
+ * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.location.LocationListener}
*/
+ @Deprecated
public interface OnMyLocationChangeListener {
/**
* Called when the location of the My Location view has changed
@@ -1683,6 +1948,16 @@ public class MapboxMap {
void onFinish();
}
+ /**
+ * Interface definition for a callback to be invoked when the snapshot has been taken.
+ */
+ public interface SnapshotReadyCallback {
+ /**
+ * Invoked when the snapshot has been taken.
+ */
+ void onSnapshotReady(Bitmap snapshot);
+ }
+
private class MapChangeCameraPositionListener implements MapView.OnMapChangedListener {
private static final long UPDATE_RATE_MS = 400;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java
index 6b7a0db8bd..17593129e7 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java
@@ -2,16 +2,22 @@ package com.mapbox.mapboxsdk.maps;
import android.content.Context;
import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.annotation.ColorInt;
+import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.Gravity;
-
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.utils.ColorUtils;
+import java.util.Arrays;
/**
* Defines configuration MapboxMapMapOptions for a MapboxMap. These options can be used when adding a
@@ -40,6 +46,8 @@ public class MapboxMapOptions implements Parcelable {
private int logoGravity = Gravity.BOTTOM | Gravity.START;
private int logoMargins[];
+ @ColorInt
+ private int attributionTintColor = -1;
private boolean attributionEnabled = true;
private int attributionGravity = Gravity.BOTTOM;
private int attributionMargins[];
@@ -53,9 +61,18 @@ public class MapboxMapOptions implements Parcelable {
private boolean zoomGesturesEnabled = true;
private boolean zoomControlsEnabled = false;
- private boolean locationEnabled;
+ private boolean myLocationEnabled;
+ private Drawable myLocationForegroundDrawable;
+ private Drawable myLocationForegroundBearingDrawable;
+ private Drawable myLocationBackgroundDrawable;
+ private int myLocationForegroundTintColor;
+ private int myLocationBackgroundTintColor;
+ private int[] myLocationBackgroundPadding;
+ private int myLocationAccuracyTintColor;
+ private int myLocationAccuracyAlpha;
private String style;
+ @Deprecated
private String accessToken;
/**
@@ -79,6 +96,7 @@ public class MapboxMapOptions implements Parcelable {
attributionEnabled = in.readByte() != 0;
attributionGravity = in.readInt();
attributionMargins = in.createIntArray();
+ attributionTintColor = in.readInt();
minZoom = in.readFloat();
maxZoom = in.readFloat();
@@ -89,7 +107,15 @@ public class MapboxMapOptions implements Parcelable {
zoomControlsEnabled = in.readByte() != 0;
zoomGesturesEnabled = in.readByte() != 0;
- locationEnabled = in.readByte() != 0;
+ myLocationEnabled = in.readByte() != 0;
+ //myLocationForegroundDrawable;
+ //myLocationForegroundBearingDrawable;
+ //myLocationBackgroundDrawable;
+ myLocationForegroundTintColor = in.readInt();
+ myLocationBackgroundTintColor = in.readInt();
+ myLocationBackgroundPadding = in.createIntArray();
+ myLocationAccuracyAlpha = in.readInt();
+ myLocationAccuracyTintColor = in.readInt();
style = in.readString();
accessToken = in.readString();
@@ -137,6 +163,7 @@ public class MapboxMapOptions implements Parcelable {
, (int) (typedArray.getDimension(R.styleable.MapView_logo_margin_right, DIMENSION_SIXTEEN_DP) * screenDensity)
, (int) (typedArray.getDimension(R.styleable.MapView_logo_margin_bottom, DIMENSION_SIXTEEN_DP) * screenDensity)});
+ mapboxMapOptions.attributionTintColor(typedArray.getColor(R.styleable.MapView_attribution_tint, -1));
mapboxMapOptions.attributionEnabled(typedArray.getBoolean(R.styleable.MapView_attribution_enabled, true));
mapboxMapOptions.attributionGravity(typedArray.getInt(R.styleable.MapView_attribution_gravity, Gravity.BOTTOM));
mapboxMapOptions.attributionMargins(new int[]{(int) (typedArray.getDimension(R.styleable.MapView_attribution_margin_left, DIMENSION_SEVENTY_SIX_DP) * screenDensity)
@@ -145,6 +172,32 @@ public class MapboxMapOptions implements Parcelable {
, (int) (typedArray.getDimension(R.styleable.MapView_attribution_margin_bottom, DIMENSION_SEVEN_DP) * screenDensity)});
mapboxMapOptions.locationEnabled(typedArray.getBoolean(R.styleable.MapView_my_location_enabled, false));
+ mapboxMapOptions.myLocationForegroundTintColor(typedArray.getColor(R.styleable.MapView_my_location_foreground_tint, Color.TRANSPARENT));
+ mapboxMapOptions.myLocationBackgroundTintColor(typedArray.getColor(R.styleable.MapView_my_location_background_tint, Color.TRANSPARENT));
+
+ Drawable foregroundDrawable = typedArray.getDrawable(R.styleable.MapView_my_location_foreground);
+ if(foregroundDrawable==null){
+ foregroundDrawable = ContextCompat.getDrawable(context,R.drawable.ic_mylocationview_normal);
+ }
+
+ Drawable foregroundBearingDrawable = typedArray.getDrawable(R.styleable.MapView_my_location_foreground_bearing);
+ if(foregroundBearingDrawable==null){
+ foregroundBearingDrawable = ContextCompat.getDrawable(context,R.drawable.ic_mylocationview_bearing);
+ }
+
+ Drawable backgroundDrawable = typedArray.getDrawable(R.styleable.MapView_my_location_background);
+ if(backgroundDrawable==null){
+ backgroundDrawable = ContextCompat.getDrawable(context, R.drawable.ic_mylocationview_background);
+ }
+
+ mapboxMapOptions.myLocationForegroundDrawables(foregroundDrawable, foregroundBearingDrawable);
+ mapboxMapOptions.myLocationBackgroundDrawable(backgroundDrawable);
+ mapboxMapOptions.myLocationBackgroundPadding(new int[]{(int) (typedArray.getDimension(R.styleable.MapView_my_location_background_left, 0) * screenDensity)
+ , (int) (typedArray.getDimension(R.styleable.MapView_my_location_background_top, 0) * screenDensity)
+ , (int) (typedArray.getDimension(R.styleable.MapView_my_location_background_right, 0) * screenDensity)
+ , (int) (typedArray.getDimension(R.styleable.MapView_my_location_background_bottom, 0) * screenDensity)});
+ mapboxMapOptions.myLocationAccuracyAlpha(typedArray.getInt(R.styleable.MapView_my_location_accuracy_alpha, 100));
+ mapboxMapOptions.myLocationAccuracyTint(typedArray.getColor(R.styleable.MapView_my_location_accuracy_tint, ColorUtils.getPrimaryColor(context)));
} finally {
typedArray.recycle();
}
@@ -163,11 +216,15 @@ public class MapboxMapOptions implements Parcelable {
}
/**
+ * <p>
+ * DEPRECATED @see MapboxAccountManager#start(String)
+ * </p>
* Specifies the accesstoken associated with a map view.
*
* @param accessToken Token to be used to access the service
* @return This
*/
+ @Deprecated
public MapboxMapOptions accessToken(String accessToken) {
this.accessToken = accessToken;
return this;
@@ -317,6 +374,17 @@ public class MapboxMapOptions implements Parcelable {
}
/**
+ * Specifies the tint color of the attribution for a map view
+ *
+ * @param color integer resembling a color
+ * @return This
+ */
+ public MapboxMapOptions attributionTintColor(@ColorInt int color) {
+ attributionTintColor = color;
+ return this;
+ }
+
+ /**
* Specifies if the rotate gesture is enabled for a map view.
*
* @param enabled True and gesture will be enabled
@@ -378,7 +446,73 @@ public class MapboxMapOptions implements Parcelable {
* @return This
*/
public MapboxMapOptions locationEnabled(boolean locationEnabled) {
- this.locationEnabled = locationEnabled;
+ this.myLocationEnabled = locationEnabled;
+ return this;
+ }
+
+ /**
+ *
+ * @param myLocationForegroundDrawable
+ * @param myLocationBearingDrawable
+ * @return This
+ */
+ public MapboxMapOptions myLocationForegroundDrawables(Drawable myLocationForegroundDrawable, Drawable myLocationBearingDrawable ) {
+ this.myLocationForegroundDrawable = myLocationForegroundDrawable;
+ this.myLocationForegroundBearingDrawable = myLocationBearingDrawable;
+ return this;
+ }
+
+ /**
+ * @param myLocationBackgroundDrawable
+ * @return This
+ */
+ public MapboxMapOptions myLocationBackgroundDrawable(Drawable myLocationBackgroundDrawable) {
+ this.myLocationBackgroundDrawable = myLocationBackgroundDrawable;
+ return this;
+ }
+
+ /**
+ * @param myLocationForegroundTintColor
+ * @return This
+ */
+ public MapboxMapOptions myLocationForegroundTintColor(@ColorInt int myLocationForegroundTintColor) {
+ this.myLocationForegroundTintColor = myLocationForegroundTintColor;
+ return this;
+ }
+
+ /**
+ * @param myLocationBackgroundTintColor
+ * @return This
+ */
+ public MapboxMapOptions myLocationBackgroundTintColor(@ColorInt int myLocationBackgroundTintColor) {
+ this.myLocationBackgroundTintColor = myLocationBackgroundTintColor;
+ return this;
+ }
+
+ /**
+ * @param myLocationBackgroundPadding
+ * @return This
+ */
+ public MapboxMapOptions myLocationBackgroundPadding(int[] myLocationBackgroundPadding) {
+ this.myLocationBackgroundPadding = myLocationBackgroundPadding;
+ return this;
+ }
+
+ /**
+ * @param myLocationAccuracyTintColor
+ * @return This
+ */
+ public MapboxMapOptions myLocationAccuracyTint(@ColorInt int myLocationAccuracyTintColor) {
+ this.myLocationAccuracyTintColor = myLocationAccuracyTintColor;
+ return this;
+ }
+
+ /**
+ * @param alpha
+ * @return This
+ */
+ public MapboxMapOptions myLocationAccuracyAlpha(@IntRange(from = 0, to = 255) int alpha) {
+ this.myLocationAccuracyAlpha = alpha;
return this;
}
@@ -464,10 +598,14 @@ public class MapboxMapOptions implements Parcelable {
}
/**
+ * <p>
+ * DEPRECATED @see MapboxAccountManager#start(String)
+ * </p>
* Get the current configured access token for a map view.
*
* @return Access token to be used.
*/
+ @Deprecated
public String getAccessToken() {
return accessToken;
}
@@ -553,13 +691,74 @@ public class MapboxMapOptions implements Parcelable {
return attributionMargins;
}
+ @ColorInt
+ public int getAttributionTintColor() {
+ return attributionTintColor;
+ }
+
/**
* Get the current configured user location view state for a map view.
*
* @return True and user location will be shown
*/
public boolean getLocationEnabled() {
- return locationEnabled;
+ return myLocationEnabled;
+ }
+
+ /**
+ * @return
+ */
+ public Drawable getMyLocationForegroundDrawable() {
+ return myLocationForegroundDrawable;
+ }
+
+ /**
+ * @return
+ */
+ public Drawable getMyLocationForegroundBearingDrawable() {
+ return myLocationForegroundBearingDrawable;
+ }
+
+ /**
+ * @return
+ */
+ public Drawable getMyLocationBackgroundDrawable() {
+ return myLocationBackgroundDrawable;
+ }
+
+ /**
+ * @return
+ */
+ public int getMyLocationForegroundTintColor() {
+ return myLocationForegroundTintColor;
+ }
+
+ /**
+ * @return
+ */
+ public int getMyLocationBackgroundTintColor() {
+ return myLocationBackgroundTintColor;
+ }
+
+ /**
+ * @return
+ */
+ public int[] getMyLocationBackgroundPadding() {
+ return myLocationBackgroundPadding;
+ }
+
+ /**
+ * @return
+ */
+ public int getMyLocationAccuracyTintColor() {
+ return myLocationAccuracyTintColor;
+ }
+
+ /**
+ * @return
+ */
+ public int getMyLocationAccuracyAlpha() {
+ return myLocationAccuracyAlpha;
}
/**
@@ -603,6 +802,7 @@ public class MapboxMapOptions implements Parcelable {
dest.writeByte((byte) (attributionEnabled ? 1 : 0));
dest.writeInt(attributionGravity);
dest.writeIntArray(attributionMargins);
+ dest.writeInt(attributionTintColor);
dest.writeFloat(minZoom);
dest.writeFloat(maxZoom);
@@ -613,9 +813,94 @@ public class MapboxMapOptions implements Parcelable {
dest.writeByte((byte) (zoomControlsEnabled ? 1 : 0));
dest.writeByte((byte) (zoomGesturesEnabled ? 1 : 0));
- dest.writeByte((byte) (locationEnabled ? 1 : 0));
+ dest.writeByte((byte) (myLocationEnabled ? 1 : 0));
+ //myLocationForegroundDrawable;
+ //myLocationForegroundBearingDrawable;
+ //myLocationBackgroundDrawable;
+ dest.writeInt(myLocationForegroundTintColor);
+ dest.writeInt(myLocationBackgroundTintColor);
+ dest.writeIntArray(myLocationBackgroundPadding);
+ dest.writeInt(myLocationAccuracyAlpha);
+ dest.writeInt(myLocationAccuracyTintColor);
dest.writeString(style);
dest.writeString(accessToken);
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ MapboxMapOptions options = (MapboxMapOptions) o;
+
+ if (debugActive != options.debugActive) return false;
+ if (compassEnabled != options.compassEnabled) return false;
+ if (compassGravity != options.compassGravity) return false;
+ if (logoEnabled != options.logoEnabled) return false;
+ if (logoGravity != options.logoGravity) return false;
+ if (attributionEnabled != options.attributionEnabled) return false;
+ if (attributionGravity != options.attributionGravity) return false;
+ if (Float.compare(options.minZoom, minZoom) != 0) return false;
+ if (Float.compare(options.maxZoom, maxZoom) != 0) return false;
+ if (rotateGesturesEnabled != options.rotateGesturesEnabled) return false;
+ if (scrollGesturesEnabled != options.scrollGesturesEnabled) return false;
+ if (tiltGesturesEnabled != options.tiltGesturesEnabled) return false;
+ if (zoomGesturesEnabled != options.zoomGesturesEnabled) return false;
+ if (zoomControlsEnabled != options.zoomControlsEnabled) return false;
+ if (myLocationEnabled != options.myLocationEnabled) return false;
+ if (myLocationForegroundTintColor != options.myLocationForegroundTintColor) return false;
+ if (myLocationBackgroundTintColor != options.myLocationBackgroundTintColor) return false;
+ if (myLocationAccuracyTintColor != options.myLocationAccuracyTintColor) return false;
+ if (myLocationAccuracyAlpha != options.myLocationAccuracyAlpha) return false;
+ if (cameraPosition != null ? !cameraPosition.equals(options.cameraPosition) : options.cameraPosition != null)
+ return false;
+ if (!Arrays.equals(compassMargins, options.compassMargins)) return false;
+ if (!Arrays.equals(logoMargins, options.logoMargins)) return false;
+ if (!Arrays.equals(attributionMargins, options.attributionMargins)) return false;
+ if (myLocationForegroundDrawable != null ? !myLocationForegroundDrawable.equals(options.myLocationForegroundDrawable) : options.myLocationForegroundDrawable != null)
+ return false;
+ if (myLocationForegroundBearingDrawable != null ? !myLocationForegroundBearingDrawable.equals(options.myLocationForegroundBearingDrawable) : options.myLocationForegroundBearingDrawable != null)
+ return false;
+ if (myLocationBackgroundDrawable != null ? !myLocationBackgroundDrawable.equals(options.myLocationBackgroundDrawable) : options.myLocationBackgroundDrawable != null)
+ return false;
+ if (!Arrays.equals(myLocationBackgroundPadding, options.myLocationBackgroundPadding))
+ return false;
+ if (style != null ? !style.equals(options.style) : options.style != null) return false;
+ return accessToken != null ? accessToken.equals(options.accessToken) : options.accessToken == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = cameraPosition != null ? cameraPosition.hashCode() : 0;
+ result = 31 * result + (debugActive ? 1 : 0);
+ result = 31 * result + (compassEnabled ? 1 : 0);
+ result = 31 * result + compassGravity;
+ result = 31 * result + Arrays.hashCode(compassMargins);
+ result = 31 * result + (logoEnabled ? 1 : 0);
+ result = 31 * result + logoGravity;
+ result = 31 * result + Arrays.hashCode(logoMargins);
+ result = 31 * result + (attributionEnabled ? 1 : 0);
+ result = 31 * result + attributionGravity;
+ result = 31 * result + Arrays.hashCode(attributionMargins);
+ result = 31 * result + (minZoom != +0.0f ? Float.floatToIntBits(minZoom) : 0);
+ result = 31 * result + (maxZoom != +0.0f ? Float.floatToIntBits(maxZoom) : 0);
+ result = 31 * result + (rotateGesturesEnabled ? 1 : 0);
+ result = 31 * result + (scrollGesturesEnabled ? 1 : 0);
+ result = 31 * result + (tiltGesturesEnabled ? 1 : 0);
+ result = 31 * result + (zoomGesturesEnabled ? 1 : 0);
+ result = 31 * result + (zoomControlsEnabled ? 1 : 0);
+ result = 31 * result + (myLocationEnabled ? 1 : 0);
+ result = 31 * result + (myLocationForegroundDrawable != null ? myLocationForegroundDrawable.hashCode() : 0);
+ result = 31 * result + (myLocationForegroundBearingDrawable != null ? myLocationForegroundBearingDrawable.hashCode() : 0);
+ result = 31 * result + (myLocationBackgroundDrawable != null ? myLocationBackgroundDrawable.hashCode() : 0);
+ result = 31 * result + myLocationForegroundTintColor;
+ result = 31 * result + myLocationBackgroundTintColor;
+ result = 31 * result + Arrays.hashCode(myLocationBackgroundPadding);
+ result = 31 * result + myLocationAccuracyTintColor;
+ result = 31 * result + myLocationAccuracyAlpha;
+ result = 31 * result + (style != null ? style.hashCode() : 0);
+ result = 31 * result + (accessToken != null ? accessToken.hashCode() : 0);
+ return result;
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
index c6ac6dc6e2..6c092ee0c8 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
@@ -119,24 +119,12 @@ final class NativeMapView {
nativeDestroySurface(mNativeMapViewPtr);
}
- public void pause() {
- nativePause(mNativeMapViewPtr);
- }
-
- public boolean isPaused() {
- return nativeIsPaused(mNativeMapViewPtr);
- }
-
- public void resume() {
- nativeResume(mNativeMapViewPtr);
- }
-
public void update() {
nativeUpdate(mNativeMapViewPtr);
}
- public void renderSync() {
- nativeRenderSync(mNativeMapViewPtr);
+ public void render() {
+ nativeRender(mNativeMapViewPtr);
}
public void resizeView(int width, int height) {
@@ -201,34 +189,12 @@ final class NativeMapView {
return nativeGetClasses(mNativeMapViewPtr);
}
- public void setDefaultTransitionDuration() {
- setDefaultTransitionDuration(0);
- }
-
- public long getDefaultTransitionDuration() {
- return nativeGetDefaultTransitionDuration(mNativeMapViewPtr);
- }
-
- public void setDefaultTransitionDuration(long milliseconds) {
- if (milliseconds < 0) {
- throw new IllegalArgumentException(
- "milliseconds cannot be negative.");
- }
-
- nativeSetDefaultTransitionDuration(mNativeMapViewPtr,
- milliseconds);
- }
-
public void setStyleUrl(String url) {
nativeSetStyleUrl(mNativeMapViewPtr, url);
}
public void setStyleJson(String newStyleJson) {
- setStyleJson(newStyleJson, "");
- }
-
- public void setStyleJson(String newStyleJson, String base) {
- nativeSetStyleJson(mNativeMapViewPtr, newStyleJson, base);
+ nativeSetStyleJson(mNativeMapViewPtr, newStyleJson);
}
public String getStyleJson() {
@@ -377,27 +343,30 @@ final class NativeMapView {
}
public long addMarker(Marker marker) {
- return nativeAddMarker(mNativeMapViewPtr, marker);
+ Marker[] markers = { marker };
+ return nativeAddMarkers(mNativeMapViewPtr, markers)[0];
}
public long[] addMarkers(List<Marker> markers) {
- return nativeAddMarkers(mNativeMapViewPtr, markers);
+ return nativeAddMarkers(mNativeMapViewPtr, markers.toArray(new Marker[markers.size()]));
}
public long addPolyline(Polyline polyline) {
- return nativeAddPolyline(mNativeMapViewPtr, polyline);
+ Polyline[] polylines = { polyline };
+ return nativeAddPolylines(mNativeMapViewPtr, polylines)[0];
}
public long[] addPolylines(List<Polyline> polylines) {
- return nativeAddPolylines(mNativeMapViewPtr, polylines);
+ return nativeAddPolylines(mNativeMapViewPtr, polylines.toArray(new Polyline[polylines.size()]));
}
public long addPolygon(Polygon polygon) {
- return nativeAddPolygon(mNativeMapViewPtr, polygon);
+ Polygon[] polygons = { polygon };
+ return nativeAddPolygons(mNativeMapViewPtr, polygons)[0];
}
- public long[] addPolygons(List<Polygon> polygon) {
- return nativeAddPolygons(mNativeMapViewPtr, polygon);
+ public long[] addPolygons(List<Polygon> polygons) {
+ return nativeAddPolygons(mNativeMapViewPtr, polygons.toArray(new Polygon[polygons.size()]));
}
public void updateMarker(Marker marker) {
@@ -405,7 +374,8 @@ final class NativeMapView {
}
public void removeAnnotation(long id) {
- nativeRemoveAnnotation(mNativeMapViewPtr, id);
+ long[] ids = { id };
+ removeAnnotations(ids);
}
public void removeAnnotations(long[] ids) {
@@ -476,8 +446,8 @@ final class NativeMapView {
nativeJumpTo(mNativeMapViewPtr, angle, center, pitch, zoom);
}
- public void easeTo(double angle, LatLng center, long duration, double pitch, double zoom) {
- nativeEaseTo(mNativeMapViewPtr, angle, center, duration, pitch, zoom);
+ public void easeTo(double angle, LatLng center, long duration, double pitch, double zoom, boolean easingInterpolator) {
+ nativeEaseTo(mNativeMapViewPtr, angle, center, duration, pitch, zoom, easingInterpolator);
}
public void flyTo(double angle, LatLng center, long duration, double pitch, double zoom) {
@@ -533,15 +503,9 @@ final class NativeMapView {
private native void nativeDestroySurface(long nativeMapViewPtr);
- private native void nativePause(long nativeMapViewPtr);
-
- private native boolean nativeIsPaused(long nativeMapViewPtr);
-
- private native void nativeResume(long nativeMapViewPtr);
-
private native void nativeUpdate(long nativeMapViewPtr);
- private native void nativeRenderSync(long nativeMapViewPtr);
+ private native void nativeRender(long nativeMapViewPtr);
private native void nativeViewResize(long nativeMapViewPtr, int width, int height);
@@ -558,15 +522,9 @@ final class NativeMapView {
private native List<String> nativeGetClasses(long nativeMapViewPtr);
- private native void nativeSetDefaultTransitionDuration(
- long nativeMapViewPtr, long duration);
-
- private native long nativeGetDefaultTransitionDuration(long nativeMapViewPtr);
-
private native void nativeSetStyleUrl(long nativeMapViewPtr, String url);
- private native void nativeSetStyleJson(long nativeMapViewPtr,
- String newStyleJson, String base);
+ private native void nativeSetStyleJson(long nativeMapViewPtr, String newStyleJson);
private native String nativeGetStyleJson(long nativeMapViewPtr);
@@ -630,21 +588,13 @@ final class NativeMapView {
private native void nativeResetNorth(long nativeMapViewPtr);
- 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);
-
- private native long[] nativeAddPolylines(long mNativeMapViewPtr, List<Polyline> polygon);
-
- private native long nativeAddPolygon(long mNativeMapViewPtr, Polygon polygon);
+ private native long[] nativeAddMarkers(long nativeMapViewPtr, Marker[] markers);
- private native long[] nativeAddPolygons(long mNativeMapViewPtr, List<Polygon> polygon);
+ private native long[] nativeAddPolylines(long mNativeMapViewPtr, Polyline[] polylines);
- private native void nativeRemoveAnnotation(long nativeMapViewPtr, long id);
+ private native long[] nativeAddPolygons(long mNativeMapViewPtr, Polygon[] polygons);
private native void nativeRemoveAnnotations(long nativeMapViewPtr, long[] id);
@@ -682,7 +632,7 @@ final class NativeMapView {
private native void nativeJumpTo(long nativeMapViewPtr, double angle, LatLng center, double pitch, double zoom);
- private native void nativeEaseTo(long nativeMapViewPtr, double angle, LatLng center, long duration, double pitch, double zoom);
+ private native void nativeEaseTo(long nativeMapViewPtr, double angle, LatLng center, long duration, double pitch, double zoom, boolean easingInterpolator);
private native void nativeFlyTo(long nativeMapViewPtr, double angle, LatLng center, long duration, double pitch, double zoom);
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 9482b1a2f7..d37c3a02ea 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
@@ -57,13 +57,15 @@ public class Projection {
public VisibleRegion getVisibleRegion() {
LatLngBounds.Builder builder = new LatLngBounds.Builder();
- int viewportWidth = mMapView.getWidth();
- int viewportHeight = mMapView.getHeight();
+ float left = mMapView.getContentPaddingLeft();
+ float right = mMapView.getWidth() - mMapView.getContentPaddingRight();
+ float top = mMapView.getContentPaddingTop();
+ float bottom = mMapView.getHeight() - mMapView.getContentPaddingBottom();
- LatLng topLeft = fromScreenLocation(new PointF(0, 0));
- LatLng topRight = fromScreenLocation(new PointF(viewportWidth, 0));
- LatLng bottomRight = fromScreenLocation(new PointF(viewportWidth, viewportHeight));
- LatLng bottomLeft = fromScreenLocation(new PointF(0, viewportHeight));
+ LatLng topLeft = fromScreenLocation(new PointF(left, top));
+ LatLng topRight = fromScreenLocation(new PointF(right, top));
+ LatLng bottomRight = fromScreenLocation(new PointF(right, bottom));
+ LatLng bottomLeft = fromScreenLocation(new PointF(left, bottom));
builder.include(topLeft)
.include(topRight)
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 ffdb57de8c..3d96727758 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,14 +1,19 @@
package com.mapbox.mapboxsdk.maps;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
+import com.mapbox.mapboxsdk.MapboxAccountManager;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.exceptions.InvalidAccessTokenException;
/**
* Support Fragment wrapper around a map view.
@@ -32,6 +37,15 @@ public class SupportMapFragment extends Fragment {
/**
* Creates a MapFragment instance
*
+ * @return MapFragment created
+ */
+ public static SupportMapFragment newInstance() {
+ return new SupportMapFragment();
+ }
+
+ /**
+ * Creates a MapFragment instance
+ *
* @param mapboxMapOptions The configuration options to be used.
* @return MapFragment created.
*/
@@ -44,7 +58,7 @@ public class SupportMapFragment extends Fragment {
}
/**
- * Creates the fragment view hierachy.
+ * Creates the fragment view hierarchy.
*
* @param inflater Inflater used to inflate content.
* @param container The parent layout for the map fragment.
@@ -54,11 +68,66 @@ public class SupportMapFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
- MapboxMapOptions options = getArguments().getParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS);
+ MapboxMapOptions options = null;
+
+ // Get bundle
+ Bundle bundle = getArguments();
+ if (bundle != null && bundle.containsKey(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS)) {
+ options = bundle.getParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS);
+ }
+
+ // Assign an AccessToken if needed
+ if (options == null || options.getAccessToken() == null) {
+ String token = null;
+ if (MapboxAccountManager.getInstance() != null) {
+ token = MapboxAccountManager.getInstance().getAccessToken();
+ } else {
+ token = getToken(inflater.getContext());
+ }
+ if (TextUtils.isEmpty(token)) {
+ throw new InvalidAccessTokenException();
+ }
+ if (options == null) {
+ options = new MapboxMapOptions().accessToken(token);
+ } else {
+ options.accessToken(token);
+ }
+ }
return mMap = new MapView(inflater.getContext(), options);
}
/**
+ * <p>
+ * Returns the Mapbox access token set in the app resources.
+ * </p>
+ * It will first search the application manifest for a {@link MapboxConstants#KEY_META_DATA_MANIFEST}
+ * meta-data value. If not found it will then attempt to load the access token from the
+ * {@code res/raw/token.txt} development file.
+ *
+ * @param context The {@link Context} of the {@link android.app.Activity} or {@link android.app.Fragment}.
+ * @return The Mapbox access token or null if not found.
+ * @see MapboxConstants#KEY_META_DATA_MANIFEST
+ * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)}
+ */
+ @Deprecated
+ private String getToken(@NonNull Context context) {
+ try {
+ // read out AndroidManifest
+ PackageManager packageManager = context.getPackageManager();
+ ApplicationInfo appInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
+ String token = appInfo.metaData.getString(MapboxConstants.KEY_META_DATA_MANIFEST);
+ if (token == null || token.isEmpty()) {
+ throw new IllegalArgumentException();
+ }
+ return token;
+ } catch (Exception e) {
+ // use fallback on string resource, used for development
+ int tokenResId = context.getResources().getIdentifier("mapbox_access_token", "string", context.getPackageName());
+ return tokenResId != 0 ? context.getString(tokenResId) : null;
+ }
+ }
+
+ /**
* Called when the fragment view hierarchy is created.
*
* @param view The content view of the fragment
@@ -142,4 +211,4 @@ public class SupportMapFragment extends Fragment {
public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) {
mOnMapReadyCallback = onMapReadyCallback;
}
-} \ No newline at end of file
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
index 90147929e9..30492bc421 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
@@ -5,7 +5,7 @@ import android.support.annotation.UiThread;
import com.mapbox.mapboxsdk.constants.MyBearingTracking;
import com.mapbox.mapboxsdk.constants.MyLocationTracking;
-import com.mapbox.mapboxsdk.maps.widgets.UserLocationView;
+import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
/**
* Settings for the user location and bearing tracking of a MapboxMap.
@@ -14,13 +14,14 @@ public class TrackingSettings {
private MapView mapView;
private UiSettings uiSettings;
- private boolean dismissTrackingOnGesture = true;
+ private boolean dismissLocationTrackingOnGesture = true;
+ private boolean dismissBearingTrackingOnGesture = true;
@MyLocationTracking.Mode
- private int mMyLocationTrackingMode;
+ private int myLocationTrackingMode;
@MyBearingTracking.Mode
- private int mMyBearingTrackingMode;
+ private int myBearingTrackingMode;
TrackingSettings(@NonNull MapView mapView, UiSettings uiSettings) {
this.mapView = mapView;
@@ -42,9 +43,9 @@ public class TrackingSettings {
*/
@UiThread
public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) {
- mMyLocationTrackingMode = myLocationTrackingMode;
+ this.myLocationTrackingMode = myLocationTrackingMode;
mapView.setMyLocationTrackingMode(myLocationTrackingMode);
- validateGesturesForTrackingModes();
+ validateGesturesForLocationTrackingMode();
}
/**
@@ -57,7 +58,7 @@ public class TrackingSettings {
@UiThread
@MyLocationTracking.Mode
public int getMyLocationTrackingMode() {
- return mMyLocationTrackingMode;
+ return myLocationTrackingMode;
}
/**
@@ -66,7 +67,7 @@ public class TrackingSettings {
* </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 disabled the direction of {@link MyLocationView} is rotated
* When location tracking is enabled the {@link MapView} is rotated based on bearing value.
* </p>
* See {@link MyBearingTracking} for different values.
@@ -77,8 +78,9 @@ public class TrackingSettings {
*/
@UiThread
public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) {
- mMyBearingTrackingMode = myBearingTrackingMode;
+ this.myBearingTrackingMode = myBearingTrackingMode;
mapView.setMyBearingTrackingMode(myBearingTrackingMode);
+ validateGesturesForBearingTrackingMode();
}
/**
@@ -89,52 +91,137 @@ public class TrackingSettings {
* @see MyBearingTracking
*/
@UiThread
- @MyLocationTracking.Mode
+ @MyBearingTracking.Mode
public int getMyBearingTrackingMode() {
- return mMyBearingTrackingMode;
+ return myBearingTrackingMode;
}
/**
* Returns if the tracking modes will be dismissed when a gesture occurs.
*
* @return True to indicate the tracking modes will be dismissed.
- */
+ * @deprecated use @link #isAllDismissTrackingOnGestureinstead
+ */
+ @Deprecated
public boolean isDismissTrackingOnGesture() {
- return dismissTrackingOnGesture;
+ return dismissLocationTrackingOnGesture && dismissBearingTrackingOnGesture;
+ }
+
+ /**
+ * Returns if all tracking modes will be dismissed when a gesture occurs.
+ *
+ * @return True to indicate that location and bearing tracking will be dismissed.
+ */
+ public boolean isAllDismissTrackingOnGesture() {
+ return dismissLocationTrackingOnGesture && dismissBearingTrackingOnGesture;
}
/**
* Set the dismissal of the tracking modes if a gesture occurs.
*
* @param dismissTrackingOnGesture True to dismiss the tracking modes.
+ * @deprecated use @link #setDismissAllTrackingOnGesture instead
*/
+ @Deprecated
public void setDismissTrackingOnGesture(boolean dismissTrackingOnGesture) {
- this.dismissTrackingOnGesture = dismissTrackingOnGesture;
- validateGesturesForTrackingModes();
+ setDismissAllTrackingOnGesture(dismissTrackingOnGesture);
+ }
+
+ /**
+ * Set the dismissal of the tracking modes if a gesture occurs.
+ *
+ * @param dismissTrackingOnGesture True to dismiss all the tracking modes.
+ */
+ public void setDismissAllTrackingOnGesture(boolean dismissTrackingOnGesture) {
+ dismissLocationTrackingOnGesture = dismissTrackingOnGesture;
+ dismissBearingTrackingOnGesture = dismissTrackingOnGesture;
+ validateAllGesturesForTrackingModes();
+ }
+
+ /**
+ * Set the dismissal of the tracking modes if a gesture occurs.
+ *
+ * @param dismissLocationTrackingOnGesture True to dismiss the location tracking mode.
+ */
+ public void setDismissLocationTrackingOnGesture(boolean dismissLocationTrackingOnGesture) {
+ this.dismissLocationTrackingOnGesture = dismissLocationTrackingOnGesture;
+ validateGesturesForLocationTrackingMode();
+ }
+
+ /**
+ * Returns if the location tracking will be disabled when a gesture occurs
+ *
+ * @return True if location tracking will be disabled.
+ */
+ public boolean isDismissLocationTrackingOnGesture() {
+ return dismissLocationTrackingOnGesture;
+ }
+
+ /**
+ * Set the dismissal of the bearing tracking modes if a gesture occurs.
+ *
+ * @param dismissBearingTrackingOnGesture True to dimsiss the bearinf tracking mode
+ */
+ public void setDismissBearingTrackingOnGesture(boolean dismissBearingTrackingOnGesture) {
+ this.dismissBearingTrackingOnGesture = dismissBearingTrackingOnGesture;
+ validateGesturesForBearingTrackingMode();
}
- private void validateGesturesForTrackingModes() {
- if (!dismissTrackingOnGesture) {
- int myLocationTrackingMode = getMyLocationTrackingMode();
- int myBearingTrackingMode = getMyBearingTrackingMode();
+ /**
+ * Returns if bearing will disabled when a gesture occurs
+ *
+ * @return True if bearing tracking will be disabled
+ */
+ public boolean isDismissBearingTrackingOnGesture() {
+ return dismissBearingTrackingOnGesture;
+ }
- // Enable/disable gestures based on tracking mode
+ /**
+ * Returns if location tracking is disabled
+ *
+ * @return True if location tracking is disabled.
+ */
+ public boolean isLocationTrackingDisabled() {
+ return myLocationTrackingMode == MyLocationTracking.TRACKING_NONE;
+ }
+
+ /**
+ * Retyrns uf bearing tracking is disabled
+ *
+ * @return True if bearing tracking will be disabled.
+ */
+ public boolean isBearingTrackingDisabled() {
+ return myBearingTrackingMode == MyBearingTracking.NONE;
+ }
+
+ private void validateAllGesturesForTrackingModes() {
+ validateGesturesForBearingTrackingMode();
+ validateGesturesForLocationTrackingMode();
+ }
+
+ private void validateGesturesForLocationTrackingMode() {
+ int myLocationTrackingMode = getMyLocationTrackingMode();
+ if (!dismissLocationTrackingOnGesture) {
if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
uiSettings.setScrollGesturesEnabled(true);
- uiSettings.setRotateGesturesEnabled(true);
} else {
uiSettings.setScrollGesturesEnabled(false);
- uiSettings.setRotateGesturesEnabled((myBearingTrackingMode == MyBearingTracking.NONE));
}
+ } else {
+ uiSettings.setScrollGesturesEnabled(true);
}
}
- /**
- * Return if location tracking is disabled
- *
- * @return True if location tracking is disabled.
- */
- public boolean isLocationTrackingDisabled() {
- return mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE;
+ private void validateGesturesForBearingTrackingMode() {
+ int myBearingTrackingMode = getMyBearingTrackingMode();
+ if (!dismissBearingTrackingOnGesture) {
+ if (myBearingTrackingMode == MyBearingTracking.NONE) {
+ uiSettings.setRotateGesturesEnabled(true);
+ } else {
+ uiSettings.setRotateGesturesEnabled(false);
+ }
+ } else {
+ uiSettings.setRotateGesturesEnabled(true);
+ }
}
}
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 f87ddb4ca1..3cd9efb13e 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,13 +1,12 @@
package com.mapbox.mapboxsdk.maps;
-import android.support.annotation.FloatRange;
+import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
-import android.util.Log;
import android.view.Gravity;
import android.view.View;
-import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
/**
* Settings for the user interface of a MapboxMap. To obtain this interface, call getUiSettings().
@@ -20,11 +19,19 @@ public class UiSettings {
private ViewSettings logoSettings;
private ViewSettings attributionSettings;
- private boolean rotateGesturesEnabled;
- private boolean tiltGesturesEnabled;
- private boolean zoomGesturesEnabled;
+ private boolean rotateGesturesEnabled = true;
+ private boolean rotateGestureChangeAllowed = true;
+
+ private boolean tiltGesturesEnabled = true;
+ private boolean tiltGestureChangeAllowed = true;
+
+ private boolean zoomGesturesEnabled = true;
+ private boolean zoomGestureChangeAllowed = true;
+
+ private boolean scrollGesturesEnabled = true;
+ private boolean scrollGestureChangeAllowed = true;
+
private boolean zoomControlsEnabled;
- private boolean scrollGesturesEnabled;
UiSettings(@NonNull MapView mapView) {
this.mapView = mapView;
@@ -232,11 +239,11 @@ public class UiSettings {
/**
* <p>
- * Enables or disables the Mapbox logo.
+ * Enables or disables the attribution.
* </p>
- * By default, the compass is enabled.
+ * By default, the attribution is enabled.
*
- * @param enabled True to enable the logo; false to disable the logo.
+ * @param enabled True to enable the attribution; false to disable the attribution.
*/
public void setAttributionEnabled(boolean enabled) {
attributionSettings.setEnabled(enabled);
@@ -244,9 +251,9 @@ public class UiSettings {
}
/**
- * Returns whether the logo is enabled.
+ * Returns whether the attribution is enabled.
*
- * @return True if the logo is enabled; false if the logo is disabled.
+ * @return True if the attribution is enabled; false if the attribution is disabled.
*/
public boolean isAttributionEnabled() {
return attributionSettings.isEnabled();
@@ -254,10 +261,9 @@ public class UiSettings {
/**
* <p>
- * Sets the gravity of the logo view. Use this to change the corner of the map view that the
- * Mapbox logo is displayed in.
+ * Sets the gravity of the attribution.
* </p>
- * By default, the logo is in the bottom left corner.
+ * By default, the attribution is in the bottom left corner next to the Mapbox logo.
*
* @param gravity One of the values from {@link Gravity}.
* @see Gravity
@@ -277,8 +283,7 @@ public class UiSettings {
}
/**
- * Sets the margins of the logo view. Use this to change the distance of the Mapbox logo from the
- * map view edge.
+ * Sets the margins of the attribution view.
*
* @param left The left margin in pixels.
* @param top The top margin in pixels.
@@ -291,7 +296,29 @@ public class UiSettings {
}
/**
- * Returns the left side margin of the logo
+ * <p>
+ * Sets the tint of the attribution view. Use this to change the color of the attribution.
+ * </p>
+ * By default, the logo is tinted with the primary color of your theme.
+ *
+ * @param tintColor Color to tint the attribution.
+ */
+ public void setAttributionTintColor(@ColorInt int tintColor) {
+ attributionSettings.setTintColor(tintColor);
+ mapView.setAtttibutionTintColor(tintColor);
+ }
+
+ /**
+ * Returns the tint color value of the attribution view.
+ *
+ * @return The tint color
+ */
+ public int getAttributionTintColor() {
+ return attributionSettings.getTintColor();
+ }
+
+ /**
+ * Returns the left side margin of the attribution view.
*
* @return The left margin in pixels
*/
@@ -300,7 +327,7 @@ public class UiSettings {
}
/**
- * Returns the top side margin of the logo
+ * Returns the top side margin of the attribution view.
*
* @return The top margin in pixels
*/
@@ -309,7 +336,7 @@ public class UiSettings {
}
/**
- * Returns the right side margin of the logo
+ * Returns the right side margin of the attribution view.
*
* @return The right margin in pixels
*/
@@ -339,7 +366,9 @@ public class UiSettings {
* @param rotateGesturesEnabled If true, rotating is enabled.
*/
public void setRotateGesturesEnabled(boolean rotateGesturesEnabled) {
- this.rotateGesturesEnabled = rotateGesturesEnabled;
+ if (rotateGestureChangeAllowed) {
+ this.rotateGesturesEnabled = rotateGesturesEnabled;
+ }
}
/**
@@ -351,6 +380,14 @@ public class UiSettings {
return rotateGesturesEnabled;
}
+ void setRotateGestureChangeAllowed(boolean rotateGestureChangeAllowed) {
+ this.rotateGestureChangeAllowed = rotateGestureChangeAllowed;
+ }
+
+ boolean isRotateGestureChangeAllowed() {
+ return rotateGestureChangeAllowed;
+ }
+
/**
* <p>
* Changes whether the user may tilt the map.
@@ -364,7 +401,9 @@ public class UiSettings {
* @param tiltGesturesEnabled If true, tilting is enabled.
*/
public void setTiltGesturesEnabled(boolean tiltGesturesEnabled) {
- this.tiltGesturesEnabled = tiltGesturesEnabled;
+ if (tiltGestureChangeAllowed) {
+ this.tiltGesturesEnabled = tiltGesturesEnabled;
+ }
}
/**
@@ -376,6 +415,14 @@ public class UiSettings {
return tiltGesturesEnabled;
}
+ void setTiltGestureChangeAllowed(boolean tiltGestureChangeAllowed) {
+ this.tiltGestureChangeAllowed = tiltGestureChangeAllowed;
+ }
+
+ boolean isTiltGestureChangeAllowed() {
+ return tiltGestureChangeAllowed;
+ }
+
/**
* <p>
* Changes whether the user may zoom the map.
@@ -389,7 +436,9 @@ public class UiSettings {
* @param zoomGesturesEnabled If true, zooming is enabled.
*/
public void setZoomGesturesEnabled(boolean zoomGesturesEnabled) {
- this.zoomGesturesEnabled = zoomGesturesEnabled;
+ if (zoomGestureChangeAllowed) {
+ this.zoomGesturesEnabled = zoomGesturesEnabled;
+ }
}
/**
@@ -401,6 +450,14 @@ public class UiSettings {
return zoomGesturesEnabled;
}
+ void setZoomGestureChangeAllowed(boolean zoomGestureChangeAllowed) {
+ this.zoomGestureChangeAllowed = zoomGestureChangeAllowed;
+ }
+
+ boolean isZoomGestureChangeAllowed() {
+ return zoomGestureChangeAllowed;
+ }
+
/**
* <p>
* Sets whether the zoom controls are enabled.
@@ -439,7 +496,9 @@ public class UiSettings {
* @param scrollGesturesEnabled If true, scrolling is enabled.
*/
public void setScrollGesturesEnabled(boolean scrollGesturesEnabled) {
- this.scrollGesturesEnabled = scrollGesturesEnabled;
+ if (scrollGestureChangeAllowed) {
+ this.scrollGesturesEnabled = scrollGesturesEnabled;
+ }
}
/**
@@ -451,6 +510,14 @@ public class UiSettings {
return scrollGesturesEnabled;
}
+ void setScrollGestureChangeAllowed(boolean scrollGestureChangeAllowed) {
+ this.scrollGestureChangeAllowed = scrollGestureChangeAllowed;
+ }
+
+ boolean isScrollGestureChangeAllowed() {
+ return scrollGestureChangeAllowed;
+ }
+
/**
* <p>
* Sets the preference for whether all gestures should be enabled or disabled.
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
index 0726b7bbbf..f08a1bdeb4 100644
--- 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
@@ -8,6 +8,7 @@ class ViewSettings {
private boolean enabled;
private int gravity;
private int[]margins;
+ private int tintColor;
public ViewSettings() {
margins = new int[4];
@@ -36,4 +37,12 @@ class ViewSettings {
public void setMargins(int[] margins) {
this.margins = margins;
}
+
+ public int getTintColor() {
+ return tintColor;
+ }
+
+ public void setTintColor(int tintColor) {
+ this.tintColor = tintColor;
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
new file mode 100644
index 0000000000..aed4e87c07
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
@@ -0,0 +1,705 @@
+package com.mapbox.mapboxsdk.maps.widgets;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.location.Location;
+import android.os.SystemClock;
+import android.support.annotation.ColorInt;
+import android.support.annotation.FloatRange;
+import android.support.annotation.IntRange;
+import android.support.annotation.NonNull;
+import android.util.AttributeSet;
+import android.view.View;
+
+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.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.Projection;
+import com.mapbox.mapboxsdk.maps.UiSettings;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * UI element overlaid on a map to show the user's location.
+ */
+public class MyLocationView extends View {
+
+ private MyLocationBehavior myLocationBehavior;
+ private MapboxMap mapboxMap;
+ private Projection projection;
+ private int maxSize;
+ private int[] contentPadding = new int[4];
+
+ private Location location;
+ private LatLng latLng;
+ private LatLng interpolatedLocation;
+ private LatLng previousLocation;
+ private long locationUpdateTimestamp;
+
+ private float gpsDirection;
+ private float previousDirection;
+
+ private float accuracy = 0;
+ private Paint accuracyPaint = new Paint();
+
+ private ValueAnimator locationChangeAnimator;
+ private ValueAnimator accuracyAnimator;
+ private ObjectAnimator directionAnimator;
+
+ private Drawable foregroundDrawable;
+ private Drawable foregroundBearingDrawable;
+ private Drawable backgroundDrawable;
+
+ private int foregroundTintColor;
+ private int backgroundTintColor;
+
+ private Rect foregroundBounds;
+ private Rect backgroundBounds;
+
+ private int backgroundOffsetLeft;
+ private int backgroundOffsetTop;
+ private int backgroundOffsetRight;
+ private int backgroundOffsetBottom;
+
+ @MyLocationTracking.Mode
+ private int myLocationTrackingMode;
+
+ @MyBearingTracking.Mode
+ private int myBearingTrackingMode;
+
+ private GpsLocationListener userLocationListener;
+ private CompassListener compassListener;
+
+ public MyLocationView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public MyLocationView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public MyLocationView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context);
+ }
+
+ private void init(Context context) {
+ setEnabled(false);
+ myLocationBehavior = new MyLocationBehaviorFactory().getBehavioralModel(MyLocationTracking.TRACKING_NONE);
+ compassListener = new CompassListener(context);
+ maxSize = (int) context.getResources().getDimension(R.dimen.my_locationview_size);
+ }
+
+ public final void setForegroundDrawables(Drawable defaultDrawable, Drawable bearingDrawable) {
+ if (defaultDrawable == null || bearingDrawable == null) {
+ return;
+ }
+ if (defaultDrawable.getIntrinsicWidth() != bearingDrawable.getIntrinsicWidth() || defaultDrawable.getIntrinsicHeight() != bearingDrawable.getIntrinsicHeight()) {
+ throw new RuntimeException("The dimensions from location and bearing drawables should be match");
+ }
+
+ foregroundDrawable = defaultDrawable;
+ foregroundBearingDrawable = bearingDrawable;
+ setForegroundDrawableTint(foregroundTintColor);
+
+ invalidateBounds();
+ }
+
+ public final void setForegroundDrawableTint(@ColorInt int color) {
+ if (color != Color.TRANSPARENT) {
+ foregroundTintColor = color;
+ if (foregroundDrawable != null) {
+ foregroundDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ }
+ if (foregroundBearingDrawable != null) {
+ foregroundBearingDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ }
+ }
+ invalidate();
+ }
+
+ public final void setShadowDrawable(Drawable drawable) {
+ setShadowDrawable(drawable, 0, 0, 0, 0);
+ }
+
+ public final void setShadowDrawable(Drawable drawable, int left, int top, int right, int bottom) {
+ if (drawable != null) {
+ backgroundDrawable = drawable;
+ }
+
+ backgroundOffsetLeft = left;
+ backgroundOffsetTop = top;
+ backgroundOffsetRight = right;
+ backgroundOffsetBottom = bottom;
+
+ setShadowDrawableTint(backgroundTintColor);
+
+ invalidateBounds();
+ }
+
+ public final void setShadowDrawableTint(@ColorInt int color) {
+ if (color != Color.TRANSPARENT) {
+ backgroundTintColor = color;
+ if (backgroundDrawable == null) {
+ return;
+ }
+ backgroundDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ }
+ invalidate();
+ }
+
+ public final void setAccuracyTint(@ColorInt int color) {
+ int alpha = accuracyPaint.getAlpha();
+ accuracyPaint.setColor(color);
+ accuracyPaint.setAlpha(alpha);
+ invalidate();
+ }
+
+ public final void setAccuracyAlpha(@IntRange(from = 0, to = 255) int alpha) {
+ accuracyPaint.setAlpha(alpha);
+ invalidate();
+ }
+
+ private void invalidateBounds() {
+ if (backgroundDrawable == null || foregroundDrawable == null || foregroundBearingDrawable == null) {
+ return;
+ }
+
+ int backgroundWidth = backgroundDrawable.getIntrinsicWidth();
+ int backgroundHeight = backgroundDrawable.getIntrinsicHeight();
+
+ int foregroundWidth = foregroundDrawable.getIntrinsicWidth();
+ int foregroundHeight = foregroundDrawable.getIntrinsicHeight();
+
+ int horizontalOffset = backgroundOffsetLeft - backgroundOffsetRight;
+ int verticalOffset = backgroundOffsetTop - backgroundOffsetBottom;
+
+ int accuracyWidth = 2 * maxSize;
+
+ backgroundBounds = new Rect(accuracyWidth - (backgroundWidth / 2) + horizontalOffset, accuracyWidth + verticalOffset - (backgroundWidth / 2), accuracyWidth + (backgroundWidth / 2) + horizontalOffset, accuracyWidth + (backgroundHeight / 2) + verticalOffset);
+ foregroundBounds = new Rect(accuracyWidth - (foregroundWidth / 2), accuracyWidth - (foregroundHeight / 2), accuracyWidth + (foregroundWidth / 2), accuracyWidth + (foregroundHeight / 2));
+
+ // invoke a new measure
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ if (location == null || foregroundBounds == null || backgroundBounds == null || accuracyAnimator == null) {
+ // Not ready yet
+ return;
+ }
+
+ // Draw circle
+ float metersPerPixel = (float) projection.getMetersPerPixelAtLatitude(location.getLatitude());
+ float accuracyPixels = (Float) accuracyAnimator.getAnimatedValue() / metersPerPixel;
+ float maxRadius = getWidth() / 2;
+ canvas.drawCircle(foregroundBounds.centerX(), foregroundBounds.centerY(), accuracyPixels <= maxRadius ? accuracyPixels : maxRadius, accuracyPaint);
+
+ // Draw shadow
+ if (backgroundDrawable != null) {
+ backgroundDrawable.draw(canvas);
+ }
+
+ // Draw foreground
+ if (myBearingTrackingMode == MyBearingTracking.NONE) {
+ if (foregroundDrawable != null) {
+ foregroundDrawable.draw(canvas);
+ }
+ } else if (foregroundBearingDrawable != null && foregroundBounds != null) {
+ foregroundBearingDrawable.draw(canvas);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ if (foregroundDrawable != null && foregroundBounds != null) {
+ foregroundDrawable.setBounds(foregroundBounds);
+ }
+
+ if (foregroundBearingDrawable != null && foregroundBounds != null) {
+ foregroundBearingDrawable.setBounds(foregroundBounds);
+ }
+
+ if (backgroundDrawable != null && backgroundBounds != null) {
+ backgroundDrawable.setBounds(backgroundBounds);
+ }
+
+ setMeasuredDimension(4 * maxSize, 4 * maxSize);
+ }
+
+ public void setTilt(@FloatRange(from = 0, to = 60.0f) double tilt) {
+ setRotationX((float) tilt);
+ }
+
+ void updateOnNextFrame() {
+ mapboxMap.invalidate();
+ }
+
+ public void onPause() {
+ compassListener.onPause();
+ toggleGps(false);
+ }
+
+ public void onResume() {
+ if (myBearingTrackingMode == MyBearingTracking.COMPASS) {
+ compassListener.onResume();
+ }
+ if (isEnabled()) {
+ toggleGps(true);
+ }
+ }
+
+ public void update() {
+ if (isEnabled()) {
+ myLocationBehavior.invalidate();
+ } else {
+ setVisibility(View.INVISIBLE);
+ }
+ }
+
+ public void setMapboxMap(MapboxMap mapboxMap) {
+ this.mapboxMap = mapboxMap;
+ this.projection = mapboxMap.getProjection();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
+ toggleGps(enabled);
+ }
+
+ /**
+ * Enabled / Disable GPS location updates along with updating the UI
+ *
+ * @param enableGps true if GPS is to be enabled, false if GPS is to be disabled
+ */
+ private void toggleGps(boolean enableGps) {
+ LocationServices locationServices = LocationServices.getLocationServices(getContext());
+ if (enableGps) {
+ // Set an initial location if one available
+ Location lastLocation = locationServices.getLastLocation();
+
+ if (lastLocation != null) {
+ setLocation(lastLocation);
+ }
+
+ if (userLocationListener == null) {
+ userLocationListener = new GpsLocationListener(this);
+ }
+
+ locationServices.addLocationListener(userLocationListener);
+ } else {
+ // Disable location and user dot
+ location = null;
+ locationServices.removeLocationListener(userLocationListener);
+ }
+
+ locationServices.toggleGPS(enableGps);
+ }
+
+ public Location getLocation() {
+ return location;
+ }
+
+ public void setLocation(Location location) {
+ if (location == null) {
+ this.location = null;
+ return;
+ }
+
+ this.location = location;
+ myLocationBehavior.updateLatLng(location);
+ }
+
+ public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) {
+ this.myBearingTrackingMode = myBearingTrackingMode;
+ if (myBearingTrackingMode == MyBearingTracking.COMPASS) {
+ compassListener.onResume();
+ } else {
+ compassListener.onPause();
+ if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
+ // always face north
+ gpsDirection = 0;
+ setCompass(gpsDirection);
+ }
+ }
+ invalidate();
+ update();
+ }
+
+ public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) {
+ this.myLocationTrackingMode = myLocationTrackingMode;
+
+ MyLocationBehaviorFactory factory = new MyLocationBehaviorFactory();
+ myLocationBehavior = factory.getBehavioralModel(myLocationTrackingMode);
+
+ if (myLocationTrackingMode != MyLocationTracking.TRACKING_NONE && location != null) {
+ // center map directly if we have a location fix
+ myLocationBehavior.updateLatLng(location);
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(location)));
+ }
+ invalidate();
+ update();
+ }
+
+ private void setCompass(float bearing) {
+ float oldDir = previousDirection;
+ if (directionAnimator != null) {
+ oldDir = (Float) directionAnimator.getAnimatedValue();
+ directionAnimator.end();
+ directionAnimator = null;
+ }
+
+ float newDir = bearing;
+ float diff = oldDir - newDir;
+ if (diff > 180.0f) {
+ newDir += 360.0f;
+ } else if (diff < -180.0f) {
+ newDir -= 360.f;
+ }
+ previousDirection = newDir;
+
+ directionAnimator = ObjectAnimator.ofFloat(this, View.ROTATION, oldDir, newDir);
+ directionAnimator.setDuration(1000);
+ directionAnimator.start();
+ }
+
+ public float getCenterX() {
+ return getX() + getMeasuredWidth() / 2;
+ }
+
+ public float getCenterY() {
+ return getY() + getMeasuredHeight() / 2;
+ }
+
+ public void setContentPadding(int[] padding) {
+ contentPadding = padding;
+ }
+
+ private static class GpsLocationListener implements LocationListener {
+
+ private WeakReference<MyLocationView> mUserLocationView;
+
+ public GpsLocationListener(MyLocationView myLocationView) {
+ mUserLocationView = new WeakReference<>(myLocationView);
+ }
+
+ /**
+ * Callback method for receiving location updates from LocationServices.
+ *
+ * @param location The new Location data
+ */
+ @Override
+ public void onLocationChanged(Location location) {
+ MyLocationView locationView = mUserLocationView.get();
+ if (locationView != null) {
+ locationView.setLocation(location);
+ }
+ }
+ }
+
+ private class CompassListener implements SensorEventListener {
+
+ private SensorManager mSensorManager;
+ private Sensor mAccelerometer;
+ private Sensor mMagnetometer;
+ private boolean paused;
+
+ private float mCurrentDegree = 0f;
+
+ private float[] mOrientation = new float[3];
+ private float[] mGData = new float[3];
+ private float[] mMData = new float[3];
+ private float[] mR = new float[16];
+ private float[] mI = new float[16];
+
+ // Controls the sensor updateLatLng rate in milliseconds
+ private static final int UPDATE_RATE_MS = 500;
+
+ // Compass data
+ private long mCompassUpdateNextTimestamp = 0;
+
+ public CompassListener(Context context) {
+ mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
+ }
+
+ public void onResume() {
+ paused = false;
+ mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
+ mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME);
+ }
+
+ public void onPause() {
+ paused = true;
+ mSensorManager.unregisterListener(this, mAccelerometer);
+ mSensorManager.unregisterListener(this, mMagnetometer);
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (paused) {
+ return;
+ }
+
+ long currentTime = SystemClock.elapsedRealtime();
+ if (currentTime < mCompassUpdateNextTimestamp) {
+ return;
+ }
+
+ int type = event.sensor.getType();
+ float[] data;
+ if (type == Sensor.TYPE_ACCELEROMETER) {
+ data = mGData;
+ } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
+ data = mMData;
+ } else {
+ // we should not be here.
+ return;
+ }
+
+ for (int i = 0; i < 3; i++) {
+ data[i] = event.values[i];
+ }
+
+ SensorManager.getRotationMatrix(mR, mI, mGData, mMData);
+ SensorManager.getOrientation(mR, mOrientation);
+ setCompass((int) (mOrientation[0] * 180.0f / Math.PI));
+ mCompassUpdateNextTimestamp = currentTime + UPDATE_RATE_MS;
+ }
+
+ public float getCurrentDegree() {
+ return mCurrentDegree;
+ }
+
+ public boolean isPaused() {
+ return paused;
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ }
+
+ private class MarkerCoordinateAnimatorListener implements ValueAnimator.AnimatorUpdateListener {
+
+ private MyLocationBehavior behavior;
+ private double fromLat;
+ private double fromLng;
+ private double toLat;
+ private double toLng;
+
+ private MarkerCoordinateAnimatorListener(MyLocationBehavior myLocationBehavior, LatLng from, LatLng to) {
+ behavior = myLocationBehavior;
+ fromLat = from.getLatitude();
+ fromLng = from.getLongitude();
+ toLat = to.getLatitude();
+ toLng = to.getLongitude();
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float frac = animation.getAnimatedFraction();
+ double latitude = fromLat + (toLat - fromLat) * frac;
+ double longitude = fromLng + (toLng - fromLng) * frac;
+ behavior.updateLatLng(latitude, longitude);
+ updateOnNextFrame();
+ }
+ }
+
+ private class MyLocationBehaviorFactory {
+
+ public MyLocationBehavior getBehavioralModel(@MyLocationTracking.Mode int mode) {
+ if (mode == MyLocationTracking.TRACKING_NONE) {
+ return new MyLocationShowBehavior();
+ } else {
+ return new MyLocationTrackingBehavior();
+ }
+ }
+ }
+
+ private abstract class MyLocationBehavior {
+
+ abstract void updateLatLng(@NonNull Location location);
+
+ public void updateLatLng(double lat, double lon) {
+ if (latLng != null) {
+ latLng.setLatitude(lat);
+ latLng.setLongitude(lon);
+ }
+ }
+
+ protected void updateAccuracy(@NonNull Location location) {
+ if (accuracyAnimator != null && accuracyAnimator.isRunning()) {
+ // use current accuracy as a starting point
+ accuracy = (Float) accuracyAnimator.getAnimatedValue();
+ accuracyAnimator.end();
+ }
+
+ accuracyAnimator = ValueAnimator.ofFloat(accuracy * 10, location.getAccuracy() * 10);
+ accuracyAnimator.setDuration(750);
+ accuracyAnimator.start();
+ accuracy = location.getAccuracy();
+ }
+
+ abstract void invalidate();
+ }
+
+ private class MyLocationTrackingBehavior extends MyLocationBehavior {
+
+ @Override
+ void updateLatLng(@NonNull Location location) {
+ if (latLng == null) {
+ // first location fix
+ latLng = new LatLng(location);
+ locationUpdateTimestamp = SystemClock.elapsedRealtime();
+ }
+
+ // updateLatLng timestamp
+ long previousUpdateTimeStamp = locationUpdateTimestamp;
+ locationUpdateTimestamp = SystemClock.elapsedRealtime();
+
+ // calculate animation duration
+ long locationUpdateDuration;
+ if (previousUpdateTimeStamp == 0) {
+ locationUpdateDuration = 0;
+ } else {
+ locationUpdateDuration = locationUpdateTimestamp - previousUpdateTimeStamp;
+ }
+
+ // calculate interpolated location
+ previousLocation = latLng;
+ latLng = new LatLng(location);
+ interpolatedLocation = new LatLng((latLng.getLatitude() + previousLocation.getLatitude()) / 2, (latLng.getLongitude() + previousLocation.getLongitude()) / 2);
+
+ // build new camera
+ CameraPosition.Builder builder = new CameraPosition.Builder().target(interpolatedLocation);
+
+ // add direction
+ if (myBearingTrackingMode == MyBearingTracking.GPS) {
+ if (location.hasBearing()) {
+ builder.bearing(location.getBearing());
+ }
+ gpsDirection = 0;
+ setCompass(gpsDirection);
+ } else if (myBearingTrackingMode == MyBearingTracking.COMPASS) {
+ if (!compassListener.isPaused()) {
+ builder.bearing(compassListener.getCurrentDegree());
+ setCompass(0);
+ }
+ }
+
+ // accuracy
+ updateAccuracy(location);
+
+ // ease to new camera position with a linear interpolator
+ mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), (int) locationUpdateDuration, false /*linear interpolator*/);
+ }
+
+ @Override
+ void invalidate() {
+ int[] mapPadding = mapboxMap.getPadding();
+ UiSettings uiSettings = mapboxMap.getUiSettings();
+ setX((uiSettings.getWidth() - getWidth() + mapPadding[0] - mapPadding[2]) / 2 + (contentPadding[0] - contentPadding[2]) / 2);
+ setY((uiSettings.getHeight() - getHeight() - mapPadding[3] + mapPadding[1]) / 2 + (contentPadding[1] - contentPadding[3]) / 2);
+ MyLocationView.this.invalidate();
+ }
+ }
+
+ private class MyLocationShowBehavior extends MyLocationBehavior {
+
+ @Override
+ void updateLatLng(@NonNull final Location location) {
+ if (latLng == null) {
+ // first location update
+ latLng = new LatLng(location);
+ locationUpdateTimestamp = SystemClock.elapsedRealtime();
+ }
+
+ // update LatLng location
+ previousLocation = latLng;
+ latLng = new LatLng(location);
+
+ // update LatLng direction
+ if (location.hasBearing()) {
+ gpsDirection = clamp(location.getBearing() - (float) mapboxMap.getCameraPosition().bearing);
+ setCompass(gpsDirection);
+ }
+
+ // update LatLng accuracy
+ updateAccuracy(location);
+
+ // calculate updateLatLng time + add some extra offset to improve animation
+ long previousUpdateTimeStamp = locationUpdateTimestamp;
+ locationUpdateTimestamp = SystemClock.elapsedRealtime();
+ long locationUpdateDuration = (long) ((locationUpdateTimestamp - previousUpdateTimeStamp) * 1.1);
+
+ // calculate interpolated entity
+ interpolatedLocation = new LatLng((latLng.getLatitude() + previousLocation.getLatitude()) / 2, (latLng.getLongitude() + previousLocation.getLongitude()) / 2);
+
+ // animate changes
+ if (locationChangeAnimator != null) {
+ locationChangeAnimator.end();
+ locationChangeAnimator = null;
+ }
+
+ locationChangeAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
+ locationChangeAnimator.setDuration((long) (locationUpdateDuration * 1.2));
+ locationChangeAnimator.addUpdateListener(new MarkerCoordinateAnimatorListener(this,
+ previousLocation, interpolatedLocation
+ ));
+ locationChangeAnimator.start();
+
+ // use interpolated location as current location
+ latLng = interpolatedLocation;
+ }
+
+ private float clamp(float direction) {
+ float diff = previousDirection - direction;
+ if (diff > 180.0f) {
+ direction += 360.0f;
+ } else if (diff < -180.0f) {
+ direction -= 360.f;
+ }
+ previousDirection = direction;
+ return direction;
+ }
+
+ @Override
+ void invalidate() {
+ PointF screenLocation = projection.toScreenLocation(latLng);
+ if (screenLocation != null) {
+ setX((screenLocation.x - getWidth() / 2));
+ setY((screenLocation.y - getHeight() / 2));
+ }
+ MyLocationView.this.invalidate();
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java
new file mode 100644
index 0000000000..9ae96ebf7b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java
@@ -0,0 +1,146 @@
+package com.mapbox.mapboxsdk.maps.widgets;
+
+import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
+import android.support.annotation.IntRange;
+
+import com.mapbox.mapboxsdk.maps.MapView;
+
+public class MyLocationViewSettings {
+
+ private MapView mapView;
+ private MyLocationView myLocationView;
+
+ //
+ // State
+ //
+
+ private boolean enabled;
+
+ //
+ // Foreground
+ //
+
+ private Drawable foregroundDrawable;
+ private Drawable foregroundBearingDrawable;
+
+ @ColorInt
+ private int foregroundTintColor;
+
+ //
+ // Background
+ //
+
+ private Drawable backgroundDrawable;
+ private int[] backgroundOffset = new int[4];
+
+ @ColorInt
+ private int backgroundTintColor;
+
+ //
+ // Accuracy
+ //
+
+ private int accuracyAlpha;
+
+ @ColorInt
+ private int accuracyTintColor;
+
+ //
+ // Padding
+ //
+
+ private int[] padding = new int[4];
+
+ public MyLocationViewSettings(MapView mapView, MyLocationView myLocationView) {
+ this.mapView = mapView;
+ this.myLocationView = myLocationView;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ myLocationView.setEnabled(enabled);
+ }
+
+ public void setForegroundDrawable(Drawable foregroundDrawable, Drawable foregroundBearingDrawable) {
+ this.foregroundDrawable = foregroundDrawable;
+ this.foregroundBearingDrawable = foregroundBearingDrawable;
+ myLocationView.setForegroundDrawables(foregroundDrawable, foregroundBearingDrawable);
+ }
+
+ public Drawable getForegroundDrawable() {
+ return foregroundDrawable;
+ }
+
+ public Drawable getForegroundBearingDrawable() {
+ return foregroundBearingDrawable;
+ }
+
+ public void setForegroundTintColor(@ColorInt int foregroundTintColor) {
+ this.foregroundTintColor = foregroundTintColor;
+ myLocationView.setForegroundDrawableTint(foregroundTintColor);
+ }
+
+ public int getForegroundTintColor() {
+ return foregroundTintColor;
+ }
+
+ public void setBackgroundDrawable(Drawable backgroundDrawable, int[] padding) {
+ this.backgroundDrawable = backgroundDrawable;
+ this.backgroundOffset = padding;
+ if (padding != null && padding.length == 4) {
+ myLocationView.setShadowDrawable(backgroundDrawable, padding[0], padding[1], padding[2], padding[3]);
+ } else {
+ myLocationView.setShadowDrawable(backgroundDrawable);
+ }
+ }
+
+ public Drawable getBackgroundDrawable() {
+ return backgroundDrawable;
+ }
+
+ public void setBackgroundTintColor(@ColorInt int backgroundTintColor) {
+ this.backgroundTintColor = backgroundTintColor;
+ myLocationView.setShadowDrawableTint(backgroundTintColor);
+ }
+
+ public int getBackgroundTintColor() {
+ return backgroundTintColor;
+ }
+
+ public int[] getBackgroundOffset() {
+ return backgroundOffset;
+ }
+
+ public void setPadding(int left, int top, int right, int bottom) {
+ padding = new int[]{left, top, right, bottom};
+ myLocationView.setContentPadding(padding);
+ mapView.invalidateContentPadding();
+ }
+
+ public int[] getPadding() {
+ return padding;
+ }
+
+ public int getAccuracyAlpha() {
+ return accuracyAlpha;
+ }
+
+ public void setAccuracyAlpha(@IntRange(from = 0, to = 255) int arruracyAlpha) {
+ this.accuracyAlpha = arruracyAlpha;
+ myLocationView.setAccuracyAlpha(arruracyAlpha);
+ }
+
+ public int getAccuracyTintColor() {
+ return accuracyTintColor;
+ }
+
+ public void setAccuracyTintColor(@ColorInt int accuracyTintColor) {
+ this.accuracyTintColor = accuracyTintColor;
+ myLocationView.setAccuracyTint(accuracyTintColor);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/UserLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/UserLocationView.java
deleted file mode 100644
index 36e48488fa..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/UserLocationView.java
+++ /dev/null
@@ -1,757 +0,0 @@
-package com.mapbox.mapboxsdk.maps.widgets;
-
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.location.Location;
-import android.os.Build;
-import android.os.SystemClock;
-import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
-import android.util.AttributeSet;
-import android.view.View;
-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.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.Projection;
-
-import java.lang.ref.WeakReference;
-
-/**
- * UI element overlaid on a map to show the user's location.
- */
-public final class UserLocationView extends View {
-
- private MapboxMap mMapboxMap;
- private Projection mProjection;
-
- private boolean mShowMarker;
- private boolean mShowDirection;
- private boolean mShowAccuracy;
- private boolean mStaleMarker;
-
- private PointF mMarkerScreenPoint;
- private Matrix mMarkerScreenMatrix;
-
- private Paint mAccuracyPaintFill;
- private Paint mAccuracyPaintStroke;
- private Path mAccuracyPath;
- private RectF mAccuracyBounds;
-
- private Drawable mUserLocationDrawable;
- private RectF mUserLocationDrawableBoundsF;
- private Rect mUserLocationDrawableBounds;
-
- private Drawable mUserLocationBearingDrawable;
- private RectF mUserLocationBearingDrawableBoundsF;
- private Rect mUserLocationBearingDrawableBounds;
-
- private Drawable mUserLocationStaleDrawable;
- private RectF mUserLocationStaleDrawableBoundsF;
- private Rect mUserLocationStaleDrawableBounds;
-
- private Rect mDirtyRect;
- private RectF mDirtyRectF;
-
- private LatLng mMarkerCoordinate;
- private ValueAnimator mMarkerCoordinateAnimator;
- private float mGpsMarkerDirection;
- private float mCompassMarkerDirection;
- private ObjectAnimator mMarkerDirectionAnimator;
- private float mMarkerAccuracy;
- private ObjectAnimator mMarkerAccuracyAnimator;
-
- private LatLng mCurrentMapViewCoordinate;
- private double mCurrentBearing;
- private boolean mPaused = false;
- private Location mUserLocation;
- private UserLocationListener mUserLocationListener;
-
- private MapboxMap.OnMyLocationChangeListener mOnMyLocationChangeListener;
-
- @MyLocationTracking.Mode
- private int mMyLocationTrackingMode;
-
- @MyBearingTracking.Mode
- private int mMyBearingTrackingMode;
-
- // Compass data
- private MyBearingListener mBearingChangeListener;
-
- public UserLocationView(Context context) {
- super(context);
- initialize(context);
- }
-
- public UserLocationView(Context context, AttributeSet attrs) {
- super(context, attrs);
- initialize(context);
- }
-
- public UserLocationView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- initialize(context);
- }
-
- private void initialize(Context context) {
- // View configuration
- setEnabled(false);
- setWillNotDraw(false);
-
- // Layout params
- ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT);
- setLayoutParams(lp);
-
- // Setup sensors
- mBearingChangeListener = new MyBearingListener(context);
-
- // Setup the custom paint
- Resources resources = context.getResources();
- int accuracyColor = ContextCompat.getColor(context,R.color.my_location_ring);
-
- float density = resources.getDisplayMetrics().density;
- mMarkerCoordinate = new LatLng(0.0, 0.0);
- mMarkerScreenPoint = new PointF();
- mMarkerScreenMatrix = new Matrix();
-
- mAccuracyPaintFill = new Paint();
- mAccuracyPaintFill.setAntiAlias(true);
- mAccuracyPaintFill.setStyle(Paint.Style.FILL);
- mAccuracyPaintFill.setColor(accuracyColor);
- mAccuracyPaintFill.setAlpha((int) (255 * 0.25f));
-
- mAccuracyPaintStroke = new Paint();
- mAccuracyPaintStroke.setAntiAlias(true);
- mAccuracyPaintStroke.setStyle(Paint.Style.STROKE);
- mAccuracyPaintStroke.setStrokeWidth(0.5f * density);
- mAccuracyPaintStroke.setColor(accuracyColor);
- mAccuracyPaintStroke.setAlpha((int) (255 * 0.5f));
-
- mAccuracyPath = new Path();
- mAccuracyBounds = new RectF();
-
- mUserLocationDrawable = ContextCompat.getDrawable(getContext(), R.drawable.my_location);
- mUserLocationDrawableBounds = new Rect(
- -mUserLocationDrawable.getIntrinsicWidth() / 2,
- -mUserLocationDrawable.getIntrinsicHeight() / 2,
- mUserLocationDrawable.getIntrinsicWidth() / 2,
- mUserLocationDrawable.getIntrinsicHeight() / 2);
- mUserLocationDrawableBoundsF = new RectF(
- -mUserLocationDrawable.getIntrinsicWidth() / 2,
- -mUserLocationDrawable.getIntrinsicHeight() / 2,
- mUserLocationDrawable.getIntrinsicWidth() / 2,
- mUserLocationDrawable.getIntrinsicHeight() / 2);
- mUserLocationDrawable.setBounds(mUserLocationDrawableBounds);
-
- mUserLocationBearingDrawable = ContextCompat.getDrawable(getContext(), R.drawable.my_location_bearing);
- mUserLocationBearingDrawableBounds = new Rect(
- -mUserLocationBearingDrawable.getIntrinsicWidth() / 2,
- -mUserLocationBearingDrawable.getIntrinsicHeight() / 2,
- mUserLocationBearingDrawable.getIntrinsicWidth() / 2,
- mUserLocationBearingDrawable.getIntrinsicHeight() / 2);
- mUserLocationBearingDrawableBoundsF = new RectF(
- -mUserLocationBearingDrawable.getIntrinsicWidth() / 2,
- -mUserLocationBearingDrawable.getIntrinsicHeight() / 2,
- mUserLocationBearingDrawable.getIntrinsicWidth() / 2,
- mUserLocationBearingDrawable.getIntrinsicHeight() / 2);
- mUserLocationBearingDrawable.setBounds(mUserLocationBearingDrawableBounds);
-
- mUserLocationStaleDrawable = ContextCompat.getDrawable(getContext(), R.drawable.my_location_stale);
- mUserLocationStaleDrawableBounds = new Rect(
- -mUserLocationStaleDrawable.getIntrinsicWidth() / 2,
- -mUserLocationStaleDrawable.getIntrinsicHeight() / 2,
- mUserLocationStaleDrawable.getIntrinsicWidth() / 2,
- mUserLocationStaleDrawable.getIntrinsicHeight() / 2);
- mUserLocationStaleDrawableBoundsF = new RectF(
- -mUserLocationStaleDrawable.getIntrinsicWidth() / 2,
- -mUserLocationStaleDrawable.getIntrinsicHeight() / 2,
- mUserLocationStaleDrawable.getIntrinsicWidth() / 2,
- mUserLocationStaleDrawable.getIntrinsicHeight() / 2);
- mUserLocationStaleDrawable.setBounds(mUserLocationStaleDrawableBounds);
- }
-
- public void setMapboxMap(MapboxMap mapboxMap) {
- mMapboxMap = mapboxMap;
- mProjection = mapboxMap.getProjection();
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- if (!mShowMarker) {
- return;
- }
-
- canvas.concat(mMarkerScreenMatrix);
-
- Drawable dotDrawable = mShowDirection ? mUserLocationBearingDrawable : mUserLocationDrawable;
- dotDrawable = mStaleMarker ? mUserLocationStaleDrawable : dotDrawable;
- // IMPORTANT also update in update()
- RectF dotBounds = mShowDirection ? mUserLocationBearingDrawableBoundsF : mUserLocationDrawableBoundsF;
- dotBounds = mStaleMarker ? mUserLocationStaleDrawableBoundsF : dotBounds;
-
- boolean willDraw = true;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN || !canvas.isHardwareAccelerated()) {
- willDraw = mShowAccuracy && !mStaleMarker && !canvas.quickReject(mAccuracyPath, Canvas.EdgeType.AA);
- }
- willDraw |= !canvas.quickReject(dotBounds, Canvas.EdgeType.AA);
-
- if (willDraw) {
- if (mShowAccuracy && !mStaleMarker) {
- canvas.drawPath(mAccuracyPath, mAccuracyPaintFill);
- canvas.drawPath(mAccuracyPath, mAccuracyPaintStroke);
- }
- dotDrawable.draw(canvas);
- }
- }
-
- public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) {
- mMyLocationTrackingMode = myLocationTrackingMode;
-
- if (myLocationTrackingMode != MyLocationTracking.TRACKING_NONE && mUserLocation != null) {
- // center map directly if we have a location fix
- mMarkerCoordinate = new LatLng(mUserLocation.getLatitude(), mUserLocation.getLongitude());
- mMapboxMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(mUserLocation)));
-
- // center view directly
- mMarkerScreenMatrix.reset();
- mMarkerScreenPoint = getMarkerScreenPoint();
- mMarkerScreenMatrix.setTranslate(mMarkerScreenPoint.x, mMarkerScreenPoint.y);
- }
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- super.setEnabled(enabled);
- setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
- toggleGps(enabled);
- }
-
- public void update() {
- if (isEnabled() && mShowMarker) {
- setVisibility(View.VISIBLE);
-
- mStaleMarker = isStale(mUserLocation);
-
- // compute new marker position
- // TODO add JNI method that takes existing pointf
- if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
- mMarkerScreenPoint = getMarkerScreenPoint();
- mMarkerScreenMatrix.reset();
- mMarkerScreenMatrix.setTranslate(
- mMarkerScreenPoint.x,
- mMarkerScreenPoint.y);
-
- } else if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
- double bearing;
- if (mShowDirection) {
- bearing = mMyBearingTrackingMode == MyBearingTracking.COMPASS ? mBearingChangeListener.getCompassBearing() : mUserLocation.getBearing();
- } else {
- bearing = mMapboxMap.getCameraPosition().bearing;
- }
-
- if (mCurrentMapViewCoordinate == null) {
- mCurrentMapViewCoordinate = mMapboxMap.getCameraPosition().target;
- }
-
- // only update if there is an actual change
- if ((!mCurrentMapViewCoordinate.equals(mMarkerCoordinate)) || (!(mCurrentBearing == bearing))) {
- CameraPosition cameraPosition = new CameraPosition.Builder()
- .target(mMarkerCoordinate)
- .bearing(bearing)
- .build();
- mMapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 300, null);
- mMarkerScreenMatrix.reset();
- mMarkerScreenPoint = getMarkerScreenPoint();
- mMarkerScreenMatrix.setTranslate(mMarkerScreenPoint.x, mMarkerScreenPoint.y);
-
- // set values for next check for actual change
- mCurrentMapViewCoordinate = mMarkerCoordinate;
- mCurrentBearing = bearing;
- }
- }
-
- // rotate so arrow in points to bearing
- if (mShowDirection) {
- if (mMyBearingTrackingMode == MyBearingTracking.COMPASS && mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
- mMarkerScreenMatrix.preRotate((float)(mCompassMarkerDirection + mMapboxMap.getCameraPosition().bearing));
- } else if (mMyBearingTrackingMode == MyBearingTracking.GPS) {
- if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
- mMarkerScreenMatrix.preRotate((float)(mGpsMarkerDirection + mMapboxMap.getCameraPosition().bearing));
- } else {
- mMarkerScreenMatrix.preRotate(mGpsMarkerDirection);
- }
- }
- }
-
- // adjust accuracy circle
- if (mShowAccuracy && !mStaleMarker) {
- mAccuracyPath.reset();
- mAccuracyPath.addCircle(0.0f, 0.0f,
- (float) (mMarkerAccuracy / mMapboxMap.getProjection().getMetersPerPixelAtLatitude(
- mMarkerCoordinate.getLatitude())),
- Path.Direction.CW);
-
- mAccuracyPath.computeBounds(mAccuracyBounds, false);
- mAccuracyBounds.inset(-1.0f, -1.0f);
- }
-
- // invalidate changed pixels
- if (mDirtyRect == null) {
- mDirtyRect = new Rect();
- mDirtyRectF = new RectF();
- } else {
- // the old marker location
- invalidate(mDirtyRect);
- }
-
- RectF dotBounds = mShowDirection ? mUserLocationBearingDrawableBoundsF : mUserLocationDrawableBoundsF;
- dotBounds = mStaleMarker ? mUserLocationStaleDrawableBoundsF : dotBounds;
- RectF largerBounds = mShowAccuracy && !mStaleMarker && mAccuracyBounds.contains(dotBounds)
- ? mAccuracyBounds : dotBounds;
- mMarkerScreenMatrix.mapRect(mDirtyRectF, largerBounds);
- mDirtyRectF.roundOut(mDirtyRect);
- invalidate(mDirtyRect); // the new marker location
- } else {
- setVisibility(View.INVISIBLE);
- }
- }
-
- public Location getLocation() {
- return mUserLocation;
- }
-
- /**
- * Enabled / Disable GPS location updates along with updating the UI
- *
- * @param enableGps true if GPS is to be enabled, false if GPS is to be disabled
- */
- private void toggleGps(boolean enableGps) {
-
- LocationServices locationServices = LocationServices.getLocationServices(getContext());
-
- if (enableGps) {
- // Set an initial location if one available
- Location lastLocation = locationServices.getLastLocation();
- if (lastLocation != null) {
- setLocation(lastLocation);
- }
-
- if (mUserLocationListener == null) {
- mUserLocationListener = new UserLocationListener(this);
- }
-
- // Register for Location Updates
- locationServices.addLocationListener(mUserLocationListener);
- } else {
- // Disable location and user dot
- setLocation(null);
-
- // Deregister for Location Updates
- locationServices.removeLocationListener(mUserLocationListener);
- }
-
- locationServices.toggleGPS(enableGps);
- }
-
- public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) {
- mMyBearingTrackingMode = myBearingTrackingMode;
-
- if (myBearingTrackingMode == MyBearingTracking.COMPASS) {
- mShowAccuracy = false;
- mShowDirection = true;
- mBearingChangeListener.onResume();
- } else {
- mBearingChangeListener.onPause();
- if (myBearingTrackingMode == MyBearingTracking.GPS) {
- mShowDirection = (mUserLocation != null) && mUserLocation.hasBearing();
- } else {
- mShowDirection = false;
- }
- }
- update();
- }
-
- private class MyBearingListener implements SensorEventListener {
-
- private SensorManager mSensorManager;
- private Sensor mAccelerometer;
- private Sensor mMagnetometer;
- private float[] mLastAccelerometer = new float[3];
- private float[] mLastMagnetometer = new float[3];
- private boolean mLastAccelerometerSet = false;
- private boolean mLastMagnetometerSet = false;
- private float[] mR = new float[9];
- private float[] mOrientation = new float[3];
- private float mCurrentDegree = 0f;
-
- // Controls the sensor update rate in milliseconds
- private static final int UPDATE_RATE_MS = 300;
-
- // Compass data
- private float mCompassBearing;
- private long mCompassUpdateNextTimestamp = 0;
-
- public MyBearingListener(Context context) {
- mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
- mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
- }
-
- public void onResume() {
- mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
- mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME);
- }
-
- public void onPause() {
- mSensorManager.unregisterListener(this, mAccelerometer);
- mSensorManager.unregisterListener(this, mMagnetometer);
- }
-
- public float getCompassBearing() {
- return mCurrentDegree;
- }
-
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (mPaused) {
- return;
- }
-
- long currentTime = SystemClock.elapsedRealtime();
- if (currentTime < mCompassUpdateNextTimestamp) {
- return;
- }
-
- if (event.sensor == mAccelerometer) {
- System.arraycopy(event.values, 0, mLastAccelerometer, 0, event.values.length);
- mLastAccelerometerSet = true;
- } else if (event.sensor == mMagnetometer) {
- System.arraycopy(event.values, 0, mLastMagnetometer, 0, event.values.length);
- mLastMagnetometerSet = true;
- }
-
- if (mLastAccelerometerSet && mLastMagnetometerSet) {
- SensorManager.getRotationMatrix(mR, null, mLastAccelerometer, mLastMagnetometer);
- SensorManager.getOrientation(mR, mOrientation);
- float azimuthInRadians = mOrientation[0];
-
- mCompassBearing = (float) (Math.toDegrees(azimuthInRadians) + 360) % 360;
- if (mCompassBearing < 0) {
- // only allow positive degrees
- mCompassBearing += 360;
- }
-
- if (mCompassBearing > mCurrentDegree + 15 || mCompassBearing < mCurrentDegree - 15) {
- mCurrentDegree = mCompassBearing;
- setCompass(mCurrentDegree);
- }
- }
- mCompassUpdateNextTimestamp = currentTime + UPDATE_RATE_MS;
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // TODO add accuracy to the equiation
- }
- }
-
- private static class UserLocationListener implements LocationListener {
-
- private WeakReference<UserLocationView> mUserLocationView;
-
- public UserLocationListener(UserLocationView userLocationView) {
- mUserLocationView = new WeakReference<>(userLocationView);
- }
-
-
- /**
- * Callback method for receiving location updates from LocationServices.
- *
- * @param location The new Location data
- */
- @Override
- public void onLocationChanged(Location location) {
- UserLocationView locationView = mUserLocationView.get();
- if (locationView != null && !locationView.isPaused()) {
- locationView.setLocation(location);
- }
- }
- }
-
- private boolean isStale(Location location) {
- if (location != null && mMyBearingTrackingMode != MyBearingTracking.COMPASS) {
- long ageInNanos;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- ageInNanos = SystemClock.elapsedRealtimeNanos() -
- location.getElapsedRealtimeNanos();
- } else {
- ageInNanos = (System.currentTimeMillis() - location.getTime()) * 1000 * 1000;
- }
- final long oneMinuteInNanos = 60L * 1000 * 1000 * 1000;
- return ageInNanos > oneMinuteInNanos;
- } else {
- return false;
- }
- }
-
- // Handles location updates from GPS
- private void setLocation(Location location) {
- // if null we should hide the marker
- if (location == null) {
- mShowMarker = false;
- mShowDirection = false;
- mShowAccuracy = false;
-
- cancelAnimations();
-
- mUserLocation = null;
- return;
- }
-
- if (mMarkerCoordinateAnimator != null) {
- mMarkerCoordinateAnimator.end();
- mMarkerCoordinateAnimator = null;
- }
-
- if (mMarkerDirectionAnimator != null) {
- mMarkerDirectionAnimator.end();
- mMarkerDirectionAnimator = null;
- }
-
- if (mMarkerAccuracyAnimator != null) {
- mMarkerAccuracyAnimator.end();
- mMarkerAccuracyAnimator = null;
- }
-
- mShowMarker = true;
-
- LatLng previousCoordinate;
- if (mUserLocation == null) {
- previousCoordinate = new LatLng(location);
- } else {
- previousCoordinate = new LatLng(mUserLocation);
- }
-
- if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
- // moving marker above map
- mMarkerCoordinateAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
- mMarkerCoordinateAnimator.setDuration(1000);
- mMarkerCoordinateAnimator.addUpdateListener(new MarkerCoordinateAnimatorListener(
- previousCoordinate, new LatLng(location)
- ));
- mMarkerCoordinateAnimator.start();
- mMarkerCoordinate = new LatLng(location);
- } else {
- // moving map under the tracker
- mMarkerCoordinate = new LatLng(location);
- }
-
- if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE && mMyBearingTrackingMode == MyBearingTracking.GPS) {
- // show GPS direction
- mShowDirection = location.hasBearing();
- if (mShowDirection) {
- if (mUserLocation != null && mUserLocation.hasBearing()) {
- mGpsMarkerDirection = mUserLocation.getBearing();
- }
- float oldDir = mGpsMarkerDirection;
- float newDir = location.getBearing();
- float diff = oldDir - newDir;
- if (diff > 180.0f) {
- newDir += 360.0f;
- } else if (diff < -180.0f) {
- newDir -= 360.f;
- }
- mMarkerDirectionAnimator = ObjectAnimator.ofFloat(this, "direction", oldDir, newDir);
- mMarkerDirectionAnimator.setDuration(1000);
- mMarkerDirectionAnimator.start();
- }
- } else if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW && mMyBearingTrackingMode == MyBearingTracking.GPS) {
- // always show north & rotate map below
- mShowDirection = true;
- mGpsMarkerDirection = 0;
- }
-
- mShowAccuracy = location.hasAccuracy();
- if (mShowAccuracy) {
- if (mUserLocation != null && mUserLocation.hasAccuracy()) {
- mMarkerAccuracy = mUserLocation.getAccuracy();
- }
- mMarkerAccuracyAnimator = ObjectAnimator.ofFloat(this, "accuracy", location.getAccuracy());
- mMarkerAccuracyAnimator.setDuration(1000);
- mMarkerAccuracyAnimator.start();
- }
-
- mUserLocation = location;
- updateOnNextFrame();
-
- if (mOnMyLocationChangeListener != null) {
- mOnMyLocationChangeListener.onMyLocationChange(location);
- }
- }
-
- // handles compass sensor updates
- private void setCompass(float bearing) {
- if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
- // animate marker
- mShowDirection = true;
- float oldDir = mCompassMarkerDirection;
- float newDir = bearing;
- float diff = oldDir - newDir;
- if (diff > 180.0f) {
- newDir += 360.0f;
- } else if (diff < -180.0f) {
- newDir -= 360.f;
- }
- mMarkerDirectionAnimator = ObjectAnimator.ofFloat(this, "direction", oldDir, newDir);
- mMarkerDirectionAnimator.setDuration(1000);
- mMarkerDirectionAnimator.start();
- mCompassMarkerDirection = bearing;
-
- } else if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
- cancelAnimations();
- if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) {
- // always show north & change map direction
- mShowDirection = true;
- mGpsMarkerDirection = 0;
- mCompassMarkerDirection = 0;
- update();
- }
- }
- }
-
- void updateOnNextFrame() {
- mMapboxMap.invalidate();
- }
-
- public void onPause() {
- mPaused = true;
- mBearingChangeListener.onPause();
- cancelAnimations();
- toggleGps(false);
- }
-
- public void onResume() {
- mPaused = false;
- if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) {
- mBearingChangeListener.onResume();
- }
- if (isEnabled()) {
- toggleGps(true);
- }
- }
-
- public void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener listener) {
- mOnMyLocationChangeListener = listener;
- }
-
- // public for animator only
- public float getDirection() {
- if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) {
- return mCompassMarkerDirection;
- }
- return mGpsMarkerDirection;
- }
-
- // public for animator only
- public void setDirection(float direction) {
- if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) {
- mCompassMarkerDirection = direction % 360.0f;
- } else {
- mGpsMarkerDirection = direction % 360.0f;
- }
- updateOnNextFrame();
- }
-
- // public for animator only
- public float getAccuracy() {
- return mMarkerAccuracy;
- }
-
- // public for animator only
- public void setAccuracy(float accuracy) {
- mMarkerAccuracy = accuracy;
- updateOnNextFrame();
- }
-
- private class MarkerCoordinateAnimatorListener implements ValueAnimator.AnimatorUpdateListener {
-
- private double mFromLat;
- private double mFromLng;
- private double mToLat;
- private double mToLng;
-
- private MarkerCoordinateAnimatorListener(LatLng from, LatLng to) {
- mFromLat = from.getLatitude();
- mFromLng = from.getLongitude();
- mToLat = to.getLatitude();
- mToLng = to.getLongitude();
- }
-
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float frac = animation.getAnimatedFraction();
- double latitude = mFromLat + (mToLat - mFromLat) * frac;
- double longitude = mFromLng + (mToLng - mFromLng) * frac;
- mMarkerCoordinate.setLatitude(latitude);
- mMarkerCoordinate.setLongitude(longitude);
- updateOnNextFrame();
- }
- }
-
- public void cancelAnimations() {
- if (mMarkerCoordinateAnimator != null) {
- mMarkerCoordinateAnimator.cancel();
- mMarkerCoordinateAnimator = null;
- }
-
- if (mMarkerDirectionAnimator != null) {
- mMarkerDirectionAnimator.cancel();
- mMarkerDirectionAnimator = null;
- }
-
- if (mMarkerAccuracyAnimator != null) {
- mMarkerAccuracyAnimator.cancel();
- mMarkerAccuracyAnimator = null;
- }
- }
-
- public boolean isPaused() {
- 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;
- }
-}