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 779f482693c..3e2d9e4ac4e 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 @@ -37,10 +37,12 @@ import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.location.LocationSource; import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; import com.mapbox.mapboxsdk.style.layers.Filter; import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.sources.Source; +import com.mapbox.services.android.telemetry.location.LocationEngine; import com.mapbox.services.commons.geojson.Feature; import java.lang.reflect.ParameterizedType; @@ -1747,6 +1749,18 @@ public void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChange trackingSettings.setOnMyLocationChangeListener(listener); } + /** + * Replaces the location source of the my-location layer. + * + * @param locationSource A {@link LocationEngine} location source to use in the my-location layer. + * Set to null to use the default {@link LocationSource} + * location source. + */ + @UiThread + public void setLocationSource(@Nullable LocationEngine locationSource) { + trackingSettings.setLocationSource(locationSource); + } + /** * Sets a callback that's invoked when the location tracking mode changes. * 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 476f4554c1e..25b60aa72d9 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 @@ -12,6 +12,7 @@ import com.mapbox.mapboxsdk.constants.MyLocationTracking; import com.mapbox.mapboxsdk.location.LocationSource; import com.mapbox.mapboxsdk.maps.widgets.MyLocationView; +import com.mapbox.services.android.telemetry.location.LocationEngine; import com.mapbox.services.android.telemetry.location.LocationEngineListener; import com.mapbox.services.android.telemetry.permissions.PermissionsManager; @@ -26,6 +27,7 @@ public final class TrackingSettings { private final UiSettings uiSettings; private final FocalPointChangeListener focalPointChangedListener; private final CameraZoomInvalidator zoomInvalidator; + private LocationEngine locationSource; private LocationEngineListener myLocationListener; private boolean myLocationEnabled; @@ -45,6 +47,7 @@ public final class TrackingSettings { } void initialise(MapboxMapOptions options) { + locationSource = LocationSource.getLocationEngine(myLocationView.getContext()); setMyLocationEnabled(options.getLocationEnabled()); } @@ -328,9 +331,9 @@ public void onLocationChanged(Location location) { } } }; - LocationSource.getLocationEngine(myLocationView.getContext()).addLocationEngineListener(myLocationListener); + locationSource.addLocationEngineListener(myLocationListener); } else { - LocationSource.getLocationEngine(myLocationView.getContext()).removeLocationEngineListener(myLocationListener); + locationSource.removeLocationEngineListener(myLocationListener); myLocationListener = null; } } @@ -362,6 +365,11 @@ void setMyLocationEnabled(boolean locationEnabled) { myLocationView.setEnabled(locationEnabled); } + void setLocationSource(LocationEngine locationSource) { + this.locationSource = locationSource; + myLocationView.setLocationSource(locationSource); + } + void update() { if (!myLocationView.isEnabled()) { return; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java index d7f31b3fafb..aecf3cc6555 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java @@ -58,6 +58,7 @@ public class MyLocationView extends View { private LatLng latLng; private Location location; + private LocationEngine locationSource; private long locationUpdateTimestamp; private float previousDirection; @@ -320,8 +321,7 @@ public void setBearing(double bearing) { if (location != null) { setCompass(location.getBearing() - bearing); } - } else if (myBearingTrackingMode == MyBearingTracking.COMPASS - && compassListener.isSensorAvailable()) { + } else if (myBearingTrackingMode == MyBearingTracking.COMPASS && compassListener.isSensorAvailable()) { setCompass(magneticHeading - bearing); } } @@ -335,8 +335,7 @@ public void setCameraPosition(CameraPosition position) { } public void onStart() { - if (myBearingTrackingMode == MyBearingTracking.COMPASS - && compassListener.isSensorAvailable()) { + if (myBearingTrackingMode == MyBearingTracking.COMPASS && compassListener.isSensorAvailable()) { compassListener.onResume(); } if (isEnabled()) { @@ -369,7 +368,8 @@ protected void onDetachedFromWindow() { } if (userLocationListener != null) { - LocationSource.getLocationEngine(getContext()).removeLocationEngineListener(userLocationListener); + locationSource.removeLocationEngineListener(userLocationListener); + locationSource = null; userLocationListener = null; } } @@ -419,29 +419,32 @@ public void onRestoreInstanceState(Parcelable state) { * @param enableGps true if GPS is to be enabled, false if GPS is to be disabled */ private void toggleGps(boolean enableGps) { - LocationEngine locationEngine = LocationSource.getLocationEngine(getContext()); + if (locationSource == null) { + locationSource = LocationSource.getLocationEngine(this.getContext()); + } + if (enableGps) { // Set an initial location if one available - Location lastLocation = locationEngine.getLastLocation(); + Location lastLocation = locationSource.getLastLocation(); if (lastLocation != null) { setLocation(lastLocation); } if (userLocationListener == null) { - userLocationListener = new GpsLocationListener(this); + userLocationListener = new GpsLocationListener(this, locationSource); } - locationEngine.addLocationEngineListener(userLocationListener); - locationEngine.activate(); + locationSource.addLocationEngineListener(userLocationListener); + locationSource.activate(); } else { // Disable location and user dot location = null; - locationEngine.removeLocationEngineListener(userLocationListener); - locationEngine.deactivate(); + locationSource.removeLocationEngineListener(userLocationListener); + locationSource.deactivate(); } - locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY); + locationSource.setPriority(LocationEnginePriority.HIGH_ACCURACY); } public Location getLocation() { @@ -460,8 +463,7 @@ public void setLocation(Location location) { public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) { this.myBearingTrackingMode = myBearingTrackingMode; - if (myBearingTrackingMode == MyBearingTracking.COMPASS - && compassListener.isSensorAvailable()) { + if (myBearingTrackingMode == MyBearingTracking.COMPASS && compassListener.isSensorAvailable()) { compassListener.onResume(); } else { compassListener.onPause(); @@ -561,22 +563,28 @@ public void setContentPadding(int[] padding) { contentPaddingY = (padding[1] - padding[3]) / 2; } + public void setLocationSource(LocationEngine locationSource) { + this.locationSource = locationSource; + } + private static class GpsLocationListener implements LocationEngineListener { private WeakReference userLocationView; + private WeakReference locationSource; - GpsLocationListener(MyLocationView myLocationView) { + GpsLocationListener(MyLocationView myLocationView, LocationEngine locationEngine) { userLocationView = new WeakReference<>(myLocationView); + locationSource = new WeakReference<>(locationEngine); } @Override public void onConnected() { MyLocationView locationView = userLocationView.get(); if (locationView != null) { - LocationEngine locationSource = LocationSource.getLocationEngine(locationView.getContext()); - Location location = locationSource.getLastLocation(); + LocationEngine locationEngine = locationSource.get(); + Location location = locationEngine.getLastLocation(); locationView.setLocation(location); - locationSource.requestLocationUpdates(); + locationEngine.requestLocationUpdates(); } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index eee7c86403d..2d2a2620559 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -246,6 +246,17 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".activity.FeatureOverviewActivity"/> + + + + 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + enableLocation(true); + } + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java new file mode 100644 index 00000000000..b87c723fda7 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java @@ -0,0 +1,93 @@ +package com.mapbox.mapboxsdk.testapp.activity.userlocation; + + +import android.location.Location; +import android.os.Handler; + +import com.mapbox.services.android.telemetry.location.LocationEngine; +import com.mapbox.services.android.telemetry.location.LocationEngineListener; + +/** + * Sample LocationEngine that provides mocked locations simulating GPS updates + */ +public class MockLocationEngine extends LocationEngine { + + // Mocked data + private static final int UPDATE_INTERVAL_MS = 1000; + private static final double[][] locations = new double[][] { + new double[] {39.489309, -0.360415}, + new double[] {39.492469, -0.358777}, + new double[] {40.393285, -3.707260}, + new double[] {40.394374, -3.707767}, + new double[] {40.398012, -3.715943}, + new double[] {40.416913, -3.703861}}; + + private Handler handler; + int currentIndex; + + public MockLocationEngine() { + super(); + } + + @Override + public void activate() { + currentIndex = 0; + + // "Connection" is immediate here + for (LocationEngineListener listener : locationListeners) { + listener.onConnected(); + } + } + + @Override + public void deactivate() { + handler = null; + } + + @Override + public boolean isConnected() { + return true; // Always connected + } + + @Override + public Location getLastLocation() { + return getNextLocation(); + } + + @Override + public void requestLocationUpdates() { + // Fake regular updates with a handler + handler = new Handler(); + handler.postDelayed(new LocationUpdateRunnable(), UPDATE_INTERVAL_MS); + } + + @Override + public void removeLocationUpdates() { + handler.removeCallbacksAndMessages(null); + } + + private Location getNextLocation() { + // Build the next location and rotate the index + Location location = new Location(MockLocationEngine.class.getSimpleName()); + location.setLatitude(locations[currentIndex][0]); + location.setLongitude(locations[currentIndex][1]); + currentIndex = (currentIndex == locations.length - 1 ? 0 : currentIndex + 1); + return location; + } + + private class LocationUpdateRunnable implements Runnable { + @Override + public void run() { + // Notify of an update + Location location = getNextLocation(); + for (LocationEngineListener listener : locationListeners) { + listener.onLocationChanged(location); + } + + if (handler != null) { + // Schedule the next update + handler.postDelayed(new LocationUpdateRunnable(), UPDATE_INTERVAL_MS); + } + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_custom_location_engine.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_custom_location_engine.xml new file mode 100644 index 00000000000..e9f461c7eeb --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_custom_location_engine.xml @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml index 5e0d25d2ec2..3df14caf0c1 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml @@ -28,6 +28,7 @@ User location drawable User location tint color User location toggle + Custom location engine Custom Layer Map Padding Debug Mode @@ -62,6 +63,7 @@ Customize the location of the user Customize the user location color Toggle location of the user on and off + Customize location engine Overlay a custom native layer on the map Learn how to create a custom InfoWindow CameraPosition capabilities