diff options
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/UserLocationView.java')
-rw-r--r-- | platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/UserLocationView.java | 771 |
1 files changed, 771 insertions, 0 deletions
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 new file mode 100644 index 0000000000..98d66b9307 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/UserLocationView.java @@ -0,0 +1,771 @@ +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.LocationService; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.Projection; + +import java.lang.ref.WeakReference; + +/** + * This view shows the user's location, as determined from GPS, on the map + * as a dot annotation. + */ + +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(); + } + + public void onStart() { + if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) { + mBearingChangeListener.onStart(getContext()); + } + } + + public void onStop() { + mBearingChangeListener.onStop(); + cancelAnimations(); + } + + @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) { + float 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(mCompassMarkerDirection + mMapboxMap.getCameraPosition().bearing); + } else if (mMyBearingTrackingMode == MyBearingTracking.GPS) { + if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { + mMarkerScreenMatrix.preRotate(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) { + + LocationService locationService = LocationService.getInstance(getContext()); + + if (enableGps) { + // Set an initial location if one available + Location lastLocation = locationService.getLastLocation(); + if (lastLocation != null) { + setLocation(lastLocation); + } + + if (mUserLocationListener == null) { + mUserLocationListener = new UserLocationListener(this); + } + + // Register for Location Updates + locationService.addLocationListener(mUserLocationListener); + } else { + // Disable location and user dot + setLocation(null); + + // Deregister for Location Updates + locationService.removeLocationListener(mUserLocationListener); + } + + locationService.toggleGPS(enableGps); + } + + public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) { + mMyBearingTrackingMode = myBearingTrackingMode; + + if (myBearingTrackingMode == MyBearingTracking.COMPASS) { + mShowAccuracy = false; + mShowDirection = true; + mBearingChangeListener.onStart(getContext()); + } else { + mBearingChangeListener.onStop(); + 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 onStart(Context context) { + mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); + mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME); + } + + public void onStop() { + 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 LocationService. + * + * @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(); + } + + /** + * Called from MapView.onPause() + */ + public void pause() { + mPaused = true; + toggleGps(false); + } + + /** + * Called from MapView.onResume() + */ + public void resume() { + mPaused = false; + 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; + } +} |