diff options
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java')
-rw-r--r-- | platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java | 367 |
1 files changed, 316 insertions, 51 deletions
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 f5ef46a5d3..aa7934ec1e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java @@ -1,6 +1,7 @@ package com.mapbox.mapboxsdk.maps.widgets; import android.animation.ValueAnimator; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Camera; import android.graphics.Canvas; @@ -26,6 +27,7 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; +import com.mapbox.mapboxsdk.Mapbox; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.constants.MyBearingTracking; @@ -40,11 +42,21 @@ import com.mapbox.services.android.telemetry.location.LocationEnginePriority; import java.lang.ref.WeakReference; +import timber.log.Timber; + /** * UI element overlaid on a map to show the user's location. + * <p> + * Use {@link MyLocationViewSettings} to manipulate the state of this view. + * </p> + * + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ +@Deprecated public class MyLocationView extends View { + private static final int UNDEFINED_TINT_COLOR = -1; private MyLocationBehavior myLocationBehavior; private MapboxMap mapboxMap; @@ -58,16 +70,18 @@ public class MyLocationView extends View { private LatLng latLng; private Location location; - private LocationEngine locationSource; + private LocationEngine locationEngine; private long locationUpdateTimestamp; private float previousDirection; private float accuracy; private Paint accuracyPaint; + private float accuracyThreshold; private ValueAnimator locationChangeAnimator; private ValueAnimator accuracyAnimator; private ValueAnimator directionAnimator; + private boolean locationChangeAnimationEnabled = true; private ValueAnimator.AnimatorUpdateListener invalidateSelfOnUpdateListener = new ValueAnimator.AnimatorUpdateListener() { @@ -147,6 +161,17 @@ public class MyLocationView extends View { compassListener = new CompassListener(context); } + @Deprecated + public void init(LocationSource locationSource) { + this.locationEngine = locationSource; + } + + /** + * Set the foreground drawable, for internal use only. + * + * @param defaultDrawable The drawable shown when showing this view + * @param bearingDrawable The drawable shown when tracking of bearing is enabled + */ public final void setForegroundDrawables(Drawable defaultDrawable, Drawable bearingDrawable) { if (defaultDrawable == null) { return; @@ -175,20 +200,35 @@ public class MyLocationView extends View { invalidateBounds(); } + /** + * Set the foreground drawable tint, for internal use only. + * + * @param color The color to tint the drawable with + */ public final void setForegroundDrawableTint(@ColorInt int color) { - if (foregroundDrawable != null) { - foregroundDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN); - } - if (foregroundBearingDrawable != null) { - foregroundBearingDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN); - } + applyDrawableTint(foregroundDrawable, color); + applyDrawableTint(foregroundBearingDrawable, color); invalidate(); } + /** + * Set the shadow drawable, for internal use only. + * + * @param drawable The drawable shown as shadow + */ public final void setShadowDrawable(Drawable drawable) { setShadowDrawable(drawable, 0, 0, 0, 0); } + /** + * Set the shadow drawable with some additional offset. + * + * @param drawable The drawable shown as shadow + * @param left The left offset margin + * @param top The top offset margin + * @param right The right offset margin + * @param bottom The bottom offset margin + */ public final void setShadowDrawable(Drawable drawable, int left, int top, int right, int bottom) { if (drawable != null) { backgroundDrawable = drawable; @@ -202,14 +242,24 @@ public class MyLocationView extends View { invalidateBounds(); } + /** + * Set the shadow drawable tint color, for internal use only. + * + * @param color The tint color to apply + */ public final void setShadowDrawableTint(@ColorInt int color) { if (backgroundDrawable == null) { return; } - backgroundDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN); + applyDrawableTint(backgroundDrawable, color); invalidate(); } + /** + * Set the accuracy tint color, for internal use only. + * + * @param color The tint color to apply + */ public final void setAccuracyTint(@ColorInt int color) { int alpha = accuracyPaint.getAlpha(); accuracyPaint.setColor(color); @@ -217,6 +267,11 @@ public class MyLocationView extends View { invalidate(); } + /** + * Set the accuracy alpha value, for internal use only. + * + * @param alpha The alpha accuracy value to apply + */ public final void setAccuracyAlpha(@IntRange(from = 0, to = 255) int alpha) { accuracyPaint.setAlpha(alpha); invalidate(); @@ -297,15 +352,25 @@ public class MyLocationView extends View { } // draw foreground - if (myBearingTrackingMode == MyBearingTracking.NONE || !compassListener.isSensorAvailable()) { + if (myBearingTrackingMode == MyBearingTracking.NONE) { if (foregroundDrawable != null) { foregroundDrawable.draw(canvas); } } else if (foregroundBearingDrawable != null && foregroundBounds != null) { - foregroundBearingDrawable.draw(canvas); + if (myBearingTrackingMode == MyBearingTracking.GPS || compassListener.isSensorAvailable()) { + foregroundBearingDrawable.draw(canvas); + } else { + // We are tracking MyBearingTracking.COMPASS, but sensor is not available. + foregroundDrawable.draw(canvas); + } } } + /** + * Set the tilt value, for internal use only. + * + * @param tilt The tilt to apply + */ public void setTilt(@FloatRange(from = 0, to = 60.0f) double tilt) { this.tilt = tilt; if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { @@ -314,6 +379,11 @@ public class MyLocationView extends View { invalidate(); } + /** + * Set the bearing value, for internal use only. + * + * @param bearing The bearing to apply + */ public void setBearing(double bearing) { this.bearing = bearing; if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { @@ -327,6 +397,11 @@ public class MyLocationView extends View { } } + /** + * Set the bearing and tilt from a camera position, for internal use only. + * + * @param position The camera position to extract bearing and tilt from + */ public void setCameraPosition(CameraPosition position) { if (position != null) { setBearing(position.bearing); @@ -334,6 +409,9 @@ public class MyLocationView extends View { } } + /** + * Called when the hosting activity is starting, for internal use only. + */ public void onStart() { if (myBearingTrackingMode == MyBearingTracking.COMPASS && compassListener.isSensorAvailable()) { compassListener.onResume(); @@ -343,6 +421,9 @@ public class MyLocationView extends View { } } + /** + * Called when the hosting activity is stopping, for internal use only. + */ public void onStop() { compassListener.onPause(); toggleGps(false); @@ -368,12 +449,15 @@ public class MyLocationView extends View { } if (userLocationListener != null) { - locationSource.removeLocationEngineListener(userLocationListener); - locationSource = null; + locationEngine.removeLocationEngineListener(userLocationListener); + locationEngine = null; userLocationListener = null; } } + /** + * Update current locationstate. + */ public void update() { if (isEnabled()) { myLocationBehavior.invalidate(); @@ -388,13 +472,33 @@ public class MyLocationView extends View { this.projection = mapboxMap.getProjection(); } + /** + * Set the enabled state, for internal use only. + * + * @param enabled The value to set the state to + */ @Override public void setEnabled(boolean enabled) { + setEnabled(enabled, false); + } + + /** + * Set the enabled state, for internal use only. + * + * @param enabled The value to set the state to + * @param isCustomLocationEngine Flag handling for handling user provided custom location engine + */ + public void setEnabled(boolean enabled, boolean isCustomLocationEngine) { super.setEnabled(enabled); setVisibility(enabled ? View.VISIBLE : View.INVISIBLE); - toggleGps(enabled); + toggleGps(enabled, isCustomLocationEngine); } + /** + * Save the view instance state, for internal use only. + * + * @return the marshaled representation of the view state + */ @Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); @@ -403,6 +507,11 @@ public class MyLocationView extends View { return bundle; } + /** + * Restore the view instance state, for internal use only. + * + * @param state the marshalled representation of the state to restore + */ @Override public void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { @@ -413,45 +522,59 @@ public class MyLocationView extends View { super.onRestoreInstanceState(state); } + private void toggleGps(boolean enableGps) { + toggleGps(enableGps, mapboxMap != null && mapboxMap.getTrackingSettings().isCustomLocationSource()); + } + /** - * Enabled / Disable GPS location updates along with updating the UI + * Enabled / Disable GPS location updates along with updating the UI, for internal use only. * * @param enableGps true if GPS is to be enabled, false if GPS is to be disabled */ - private void toggleGps(boolean enableGps) { - if (locationSource == null) { - locationSource = LocationSource.getLocationEngine(this.getContext()); - } - + private void toggleGps(boolean enableGps, boolean isCustomLocationEngine) { if (enableGps) { - // Set an initial location if one available - Location lastLocation = locationSource.getLastLocation(); - - if (lastLocation != null) { - setLocation(lastLocation); + if (locationEngine == null) { + if (!isCustomLocationEngine) { + locationEngine = Mapbox.getLocationEngine(); + } else { + return; + } } if (userLocationListener == null) { - userLocationListener = new GpsLocationListener(this, locationSource); + userLocationListener = new GpsLocationListener(this, locationEngine); } - locationSource.addLocationEngineListener(userLocationListener); - locationSource.activate(); + locationEngine.addLocationEngineListener(userLocationListener); + locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY); + locationEngine.activate(); } else { + if (locationEngine == null) { + return; + } // Disable location and user dot location = null; - locationSource.removeLocationUpdates(); - locationSource.removeLocationEngineListener(userLocationListener); - locationSource.deactivate(); + locationEngine.removeLocationEngineListener(userLocationListener); + locationEngine.removeLocationUpdates(); + locationEngine.deactivate(); + restoreLocationEngine(); } - - locationSource.setPriority(LocationEnginePriority.HIGH_ACCURACY); } + /** + * Get the current location. + * + * @return the current location + */ public Location getLocation() { return location; } + /** + * Set the current location, for internal use only. + * + * @param location The current location + */ public void setLocation(Location location) { if (location == null) { this.location = null; @@ -460,8 +583,37 @@ public class MyLocationView extends View { this.location = location; myLocationBehavior.updateLatLng(location); + + if (mapboxMap != null && myBearingTrackingMode == MyBearingTracking.GPS + && myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { + setBearing(mapboxMap.getCameraPosition().bearing); + } + } + + /** + * Set location change animation enabled, for internal use only. + * + * @param locationChangeAnimationEnabled True if location changes are animated + */ + public void setLocationChangeAnimationEnabled(boolean locationChangeAnimationEnabled) { + this.locationChangeAnimationEnabled = locationChangeAnimationEnabled; } + /** + * Set accuracy circle threshold. Circle won't be displayed if accuracy is below set value. + * For internal use only. + * + * @param accuracyThreshold Value below which circle won't be displayed + */ + public void setAccuracyThreshold(float accuracyThreshold) { + this.accuracyThreshold = accuracyThreshold; + } + + /** + * Set the bearing tracking mode, for internal use only. + * + * @param myBearingTrackingMode The bearing tracking mode + */ public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) { this.myBearingTrackingMode = myBearingTrackingMode; if (myBearingTrackingMode == MyBearingTracking.COMPASS && compassListener.isSensorAvailable()) { @@ -478,6 +630,11 @@ public class MyLocationView extends View { invalidate(); } + /** + * Set the location tracking mode, for internla use only. + * + * @param myLocationTrackingMode The location tracking mode + */ public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) { MyLocationBehaviorFactory factory = new MyLocationBehaviorFactory(); myLocationBehavior = factory.getBehavioralModel(myLocationTrackingMode); @@ -485,8 +642,7 @@ public class MyLocationView extends View { if (location != null) { if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { // center map directly - mapboxMap.easeCamera(CameraUpdateFactory.newLatLng(new LatLng(location)), 0, false /*linear interpolator*/, - null, true); + mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(location))); } else { // do not use interpolated location from tracking mode latLng = null; @@ -498,17 +654,32 @@ public class MyLocationView extends View { invalidate(); } + /** + * Get the location tracking mode, for internal use only. + * + * @return The location tracking mode + */ @MyLocationTracking.Mode public int getMyLocationTrackingMode() { return myLocationTrackingMode; } + /** + * Get the bearing tracking mode, for internal use only. + * + * @return the bearing tracking mode + */ @MyBearingTracking.Mode public int getMyBearingTrackingMode() { return myBearingTrackingMode; } + /** + * Set the compass bearing value, for internal use only. + * + * @param bearing The compas bearing value + */ private void setCompass(double bearing) { setCompass(bearing, 0 /* no animation */); } @@ -536,14 +707,29 @@ public class MyLocationView extends View { directionAnimator.start(); } + /** + * Get the center of this view in screen coordinates. + * + * @return the center of the view + */ public PointF getCenter() { return new PointF(getCenterX(), getCenterY()); } + /** + * Get the x value of the center of this view. + * + * @return the x value of the center of the view + */ private float getCenterX() { return (getX() + getMeasuredWidth()) / 2 + contentPaddingX - projectedX; } + /** + * Get the y value of the center of this view. + * + * @return the y value of the center of the view + */ private float getCenterY() { return (getY() + getMeasuredHeight()) / 2 + contentPaddingY - projectedY; } @@ -553,8 +739,41 @@ public class MyLocationView extends View { contentPaddingY = (padding[1] - padding[3]) / 2; } - public void setLocationSource(LocationEngine locationSource) { - this.locationSource = locationSource; + /** + * Set the location source from which location updates are received, for internal use only. + * + * @param locationEngine The location engine to receive updates from + */ + public void setLocationSource(LocationEngine locationEngine) { + toggleGps(false); + this.locationEngine = locationEngine; + this.userLocationListener = null; + setEnabled(isEnabled(), locationEngine != null); + } + + private void applyDrawableTint(Drawable drawable, @ColorInt int color) { + if (color == UNDEFINED_TINT_COLOR) { + removeTintColorFilter(drawable); + } else { + applyTintColorFilter(drawable, color); + } + } + + private void removeTintColorFilter(Drawable drawable) { + if (drawable != null) { + drawable.mutate().setColorFilter(null); + } + } + + private void applyTintColorFilter(Drawable drawable, @ColorInt int color) { + if (drawable != null) { + drawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN); + } + } + + private void restoreLocationEngine() { + locationEngine.setPriority(LocationEnginePriority.LOW_POWER); + locationEngine.activate(); } private static class GpsLocationListener implements LocationEngineListener { @@ -567,13 +786,16 @@ public class MyLocationView extends View { locationSource = new WeakReference<>(locationEngine); } + @SuppressLint("MissingPermission") @Override public void onConnected() { MyLocationView locationView = userLocationView.get(); - if (locationView != null) { - LocationEngine locationEngine = locationSource.get(); - Location location = locationEngine.getLastLocation(); - locationView.setLocation(location); + LocationEngine locationEngine = locationSource.get(); + if (locationView != null && locationEngine != null) { + Location lastKnownLocation = locationEngine.getLastLocation(); + if (lastKnownLocation != null) { + locationView.setLocation(lastKnownLocation); + } locationEngine.requestLocationUpdates(); } } @@ -597,9 +819,12 @@ public class MyLocationView extends View { private final SensorManager sensorManager; private Sensor rotationVectorSensor; - float[] matrix = new float[9]; - float[] orientation = new float[3]; + private float[] matrix = new float[9]; + private float[] rotationVectorValue; + private float[] truncatedRotationVectorValue = new float[4]; + private float[] orientation = new float[3]; + private boolean reportMissingSensor = true; // Compass data private long compassUpdateNextTimestamp = 0; @@ -617,6 +842,10 @@ public class MyLocationView extends View { } public boolean isSensorAvailable() { + if (rotationVectorSensor == null && reportMissingSensor) { + reportMissingSensor = false; + Timber.e("Sensor.TYPE_ROTATION_VECTOR is missing from this device. Unable to use MyBearingTracking.COMPASS."); + } return rotationVectorSensor != null; } @@ -630,9 +859,8 @@ public class MyLocationView extends View { } if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) { - - // calculate the rotation matrix - SensorManager.getRotationMatrixFromVector(matrix, event.values); + rotationVectorValue = getRotationVectorFromSensorEvent(event); + SensorManager.getRotationMatrixFromVector(matrix, rotationVectorValue); SensorManager.getOrientation(matrix, orientation); magneticHeading = (float) Math.toDegrees(SensorManager.getOrientation(matrix, orientation)[0]); @@ -649,6 +877,28 @@ public class MyLocationView extends View { } } + /** + * Pulls out the rotation vector from a SensorEvent, with a maximum length + * vector of four elements to avoid potential compatibility issues. + * + * @param event the sensor event + * @return the events rotation vector, potentially truncated + */ + @NonNull + float[] getRotationVectorFromSensorEvent(@NonNull SensorEvent event) { + if (event.values.length > 4) { + // On some Samsung devices SensorManager.getRotationMatrixFromVector + // appears to throw an exception if rotation vector has length > 4. + // For the purposes of this class the first 4 values of the + // rotation vector are sufficient (see crbug.com/335298 for details). + // Only affects Android 4.3 + System.arraycopy(event.values, 0, truncatedRotationVectorValue, 0, 4); + return truncatedRotationVectorValue; + } else { + return event.values; + } + } + private void rotateCamera(float rotation) { CameraPosition.Builder builder = new CameraPosition.Builder(); builder.bearing(rotation); @@ -701,6 +951,12 @@ public class MyLocationView extends View { private abstract class MyLocationBehavior { + MyLocationBehavior() { + if (latLng != null) { + locationUpdateTimestamp = SystemClock.elapsedRealtime(); + } + } + void updateLatLng(@NonNull Location newLocation) { location = newLocation; } @@ -719,10 +975,11 @@ public class MyLocationView extends View { accuracyAnimator.end(); } - accuracyAnimator = ValueAnimator.ofFloat(accuracy, location.getAccuracy()); + float newAccuracy = location.getAccuracy() >= accuracyThreshold ? location.getAccuracy() : 0f; + accuracyAnimator = ValueAnimator.ofFloat(accuracy, newAccuracy); accuracyAnimator.setDuration(750); accuracyAnimator.start(); - accuracy = location.getAccuracy(); + accuracy = newAccuracy; } abstract void invalidate(); @@ -767,9 +1024,13 @@ public class MyLocationView extends View { // accuracy updateAccuracy(location); - // ease to new camera position with a linear interpolator - mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), animationDuration, false, null, - true); + if (locationChangeAnimationEnabled && animationDuration > 0) { + // ease to new camera position with a linear interpolator + mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), animationDuration, false, null, + true); + } else { + mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(builder.build())); + } } @Override @@ -811,7 +1072,11 @@ public class MyLocationView extends View { } locationChangeAnimator = ValueAnimator.ofFloat(0.0f, 1.0f); - locationChangeAnimator.setDuration(locationUpdateDuration); + if (locationChangeAnimationEnabled) { + locationChangeAnimator.setDuration(locationUpdateDuration); + } else { + locationChangeAnimator.setDuration(0); + } locationChangeAnimator.addUpdateListener(new MarkerCoordinateAnimatorListener(this, latLng, newLocation )); |