From 00381a2ae0a5afaf788a86764672f0d4156c5510 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Wed, 13 Apr 2016 15:17:31 +0200 Subject: [PATCH] [android] #4396 - UserLocationView refactor --- .../com/mapbox/mapboxsdk/maps/MapView.java | 110 +- .../com/mapbox/mapboxsdk/maps/MapboxMap.java | 92 +- .../maps/widgets/UserLocationView.java | 1084 ++++++++--------- .../ic_userlocationview_bearing.png | Bin 0 -> 8679 bytes .../ic_userlocationview_normal.png | Bin 0 -> 8119 bytes .../drawable/ic_userlocationview_shadow.xml | 12 + .../src/main/res/values/dimens.xml | 2 + 7 files changed, 657 insertions(+), 643 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/ic_userlocationview_bearing.png create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/ic_userlocationview_normal.png create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable/ic_userlocationview_shadow.xml 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 bd954c045..df20871c3 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 @@ -469,11 +469,11 @@ public class MapView extends FrameLayout { 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_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_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_TILT_ENABLED_CHANGE, uiSettings.isTiltGestureChangeAllowed()); outState.putBoolean(MapboxConstants.STATE_ZOOM_CONTROLS_ENABLED, uiSettings.isZoomControlsEnabled()); // UiSettings - Compass @@ -588,6 +588,7 @@ public class MapView extends FrameLayout { if (duration != null) { actualDuration = duration; } + mUserLocationView.setTilt(pitch); mNativeMapView.setPitch(pitch, actualDuration); } @@ -889,58 +890,59 @@ public class MapView extends FrameLayout { * Set the user location view default drawable . */ void setUserLocationDrawable(Drawable drawable) { - mUserLocationView.setUserLocationDrawable(drawable); + mUserLocationView.setForegroundDrawable(drawable); } /** * Set the user location view default drawable and its shadow. */ void setUserLocationDrawable(Drawable drawable, Drawable shadowDrawable) { - mUserLocationView.setUserLocationDrawable(drawable, shadowDrawable); + mUserLocationView.setForegroundDrawable(drawable); + mUserLocationView.setShadowDrawable(shadowDrawable); } +// +// /** +// * Set the user location view bearing drawable. +// */ +// void setUserLocationBearingDrawable(Drawable drawable) { +// mUserLocationView.setUserLocationBearingDrawable(drawable); +// } +// +// /** +// * Set the user location view bearing drawable and its shadow. +// */ +// void setUserLocationBearingDrawable(Drawable drawable, Drawable shadowDrawable) { +// mUserLocationView.setUserLocationBearingDrawable(drawable, shadowDrawable); +// } +// +// /** +// * Set the stale user location view drawable. +// */ +// void setUserLocationStaleDrawable(Drawable drawable) { +// mUserLocationView.setUserLocationStaleDrawable(drawable); +// } +// +// /** +// * Set the stale user location view drawable and its shadow. +// */ +// void setUserLocationStaleDrawable(Drawable drawable, Drawable shadowDrawable) { +// mUserLocationView.setUserLocationStaleDrawable(drawable, shadowDrawable); +// } - /** - * Set the user location view bearing drawable. - */ - void setUserLocationBearingDrawable(Drawable drawable) { - mUserLocationView.setUserLocationBearingDrawable(drawable); - } - - /** - * Set the user location view bearing drawable and its shadow. - */ - void setUserLocationBearingDrawable(Drawable drawable, Drawable shadowDrawable) { - mUserLocationView.setUserLocationBearingDrawable(drawable, shadowDrawable); - } - - /** - * Set the stale user location view drawable. - */ - void setUserLocationStaleDrawable(Drawable drawable) { - mUserLocationView.setUserLocationStaleDrawable(drawable); - } - - /** - * Set the stale user location view drawable and its shadow. - */ - void setUserLocationStaleDrawable(Drawable drawable, Drawable shadowDrawable) { - mUserLocationView.setUserLocationStaleDrawable(drawable, shadowDrawable); - } - - /** - * Set the offset of the user location view shadow - */ - void setUserLocationShadowOffset(int x, int y) { - mUserLocationView.setUserLocationShadowOffset(x, y); - } - - /** - * Change the accuracy color - * @param color int real color (not a @ColorRes) - */ - void setUserLocationAccuracyColor(int color) { - mUserLocationView.setUserLocationAccuracyColor(color); - } +// /** +// * Set the offset of the user location view shadow +// */ +// void setUserLocationShadowOffset(int x, int y) { +// mUserLocationView.setUserLocationShadowOffset(x, y); +// } +// +// /** +// * Change the accuracy color +// * @param color int real color (not a @ColorRes) +// */ +// void setUserLocationAccuracyColor(int color) { +// mUserLocationView.setUserLocationAccuracyColor(color); +// } // // Projection @@ -955,7 +957,7 @@ public class MapView extends FrameLayout { } PointF toScreenLocation(@NonNull LatLng location) { - if (mDestroyed) { + if (mDestroyed || location == null) { return new PointF(); } PointF pointF = mNativeMapView.pixelForLatLng(location); @@ -1125,7 +1127,7 @@ public class MapView extends FrameLayout { } private List getMarkersInBounds(@NonNull LatLngBounds bbox) { - if (mDestroyed || bbox==null) { + if (mDestroyed || bbox == null) { return new ArrayList<>(); } @@ -1838,8 +1840,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 = mUserLocationView.getX() + mUserLocationView.getWidth() / 2; + float y = mUserLocationView.getY() + mUserLocationView.getHeight() / 2; + mNativeMapView.scaleBy(detector.getScaleFactor(), x / mScreenDensity, y / mScreenDensity); } } return true; @@ -1918,8 +1921,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 = mUserLocationView.getX() + mUserLocationView.getWidth() / 2; + float y = mUserLocationView.getY() + mUserLocationView.getHeight() / 2; + mNativeMapView.setBearing(bearing, x / mScreenDensity, y / mScreenDensity); } return true; } 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 0162a98a9..22ff0c345 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 @@ -592,52 +592,52 @@ public class MapboxMap { } /** - * Set the user location view bearing drawable. - */ - public void setUserLocationBearingDrawable(Drawable drawable) { - mMapView.setUserLocationBearingDrawable(drawable); - } - - /** - * Set the user location view bearing drawable and its shadow. - */ - public void setUserLocationBearingDrawable(Drawable drawable, Drawable shadowDrawable) { - mMapView.setUserLocationBearingDrawable(drawable, shadowDrawable); - } - - /** - * Set the stale user location view drawable. - */ - public void setUserLocationStaleDrawable(Drawable drawable) { - mMapView.setUserLocationStaleDrawable(drawable); - } - - /** - * Set the stale user location view drawable and its shadow. - */ - public void setUserLocationStaleDrawable(Drawable drawable, Drawable shadowDrawable) { - mMapView.setUserLocationStaleDrawable(drawable, shadowDrawable); - } - - - /** - * Set the offset of the user location view shadow. - * - * @param x int x offset in pixels - * @param y int y offset in pixels - */ - public void setUserLocationShadowOffset(int x, int y) { - mMapView.setUserLocationShadowOffset(x, y); - } - - /** - * Change the accuracy circle (fill and stroke) color of the user location view - * - * @param color int real color (not a @ColorRes) - */ - public void setUserLocationAccuracyColor(int color) { - mMapView.setUserLocationAccuracyColor(color); - } +// * Set the user location view bearing drawable. +// */ +// public void setUserLocationBearingDrawable(Drawable drawable) { +// mMapView.setUserLocationBearingDrawable(drawable); +// } +// +// /** +// * Set the user location view bearing drawable and its shadow. +// */ +// public void setUserLocationBearingDrawable(Drawable drawable, Drawable shadowDrawable) { +// mMapView.setUserLocationBearingDrawable(drawable, shadowDrawable); +// } +// +// /** +// * Set the stale user location view drawable. +// */ +// public void setUserLocationStaleDrawable(Drawable drawable) { +// mMapView.setUserLocationStaleDrawable(drawable); +// } +// +// /** +// * Set the stale user location view drawable and its shadow. +// */ +// public void setUserLocationStaleDrawable(Drawable drawable, Drawable shadowDrawable) { +// mMapView.setUserLocationStaleDrawable(drawable, shadowDrawable); +// } +// +// +// /** +// * Set the offset of the user location view shadow. +// * +// * @param x int x offset in pixels +// * @param y int y offset in pixels +// */ +// public void setUserLocationShadowOffset(int x, int y) { +// mMapView.setUserLocationShadowOffset(x, y); +// } +// +// /** +// * Change the accuracy circle (fill and stroke) color of the user location view +// * +// * @param color int real color (not a @ColorRes) +// */ +// public void setUserLocationAccuracyColor(int color) { +// mMapView.setUserLocationAccuracyColor(color); +// } /** 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 index 9c2aee5ec..208ab9886 100644 --- 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 @@ -3,28 +3,27 @@ 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.Color; import android.graphics.Paint; -import android.graphics.Path; import android.graphics.PointF; +import android.graphics.PorterDuff; import android.graphics.Rect; -import android.graphics.RectF; import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; 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.NonNull; +import android.support.annotation.ColorInt; +import android.support.annotation.IntRange; import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; import android.util.AttributeSet; import android.view.View; -import android.view.ViewGroup; +import android.widget.Toast; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.camera.CameraPosition; @@ -36,6 +35,7 @@ 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; @@ -44,227 +44,390 @@ import java.lang.ref.WeakReference; */ public final class UserLocationView extends View { - private MapboxMap mMapboxMap; - private Projection mProjection; + // Foreground + private Drawable foregroundRotatedDrawable; + private Drawable foregroundDrawable; - 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 UserLocationViewDrawableHolder mHolder = new UserLocationViewDrawableHolder(); - private UserLocationViewDrawableHolder mBearingHolder = new UserLocationViewDrawableHolder(); - private UserLocationViewDrawableHolder mStaleHolder = new UserLocationViewDrawableHolder(); - - private int mShadowX = 0; - private int mShadowY = 0; - - 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 float compassDirection; + private MyBearingListener mBearingChangeListener; private double mCurrentBearing; - private boolean mPaused = false; - private Location mUserLocation; - private UserLocationListener mUserLocationListener; - private MapboxMap.OnMyLocationChangeListener mOnMyLocationChangeListener; + private float gpsDirection; + + // Shadow + private Drawable backgroundDrawable; + + // Accuracy + private Paint accurancyPaint; + private int maxAccurancy = 50; + private float accurancy = 100; + + private double tilt; + + private Rect viewBounds; + private Rect foregroundBounds; + private Rect backgroundBounds; + + private int backgroundOffsetLeft; + private int backgroundOffsetTop; + private int backgroundOffsetRight; + private int backgroundOffsetBottom; + + private boolean paused; + private boolean showMarker; + private boolean showDirection; + private boolean showAccurancy; @MyLocationTracking.Mode - private int mMyLocationTrackingMode; + private int myLocationTrackingMode; @MyBearingTracking.Mode - private int mMyBearingTrackingMode; + private int myBearingTrackingMode; - // Compass data - private MyBearingListener mBearingChangeListener; + private MapboxMap mapboxMap; + private Projection projection; + + private LatLng markerScreenCoordinate; + private LatLng currentMapViewCoordinate; + private PointF markerScreenPoint; + + private Location location; + private UserLocationListener userLocationListener; + + private ObjectAnimator mMarkerDirectionAnimator; + private ValueAnimator mMarkerCoordinateAnimator; + private ObjectAnimator mMarkerAccuracyAnimator; + + private MapboxMap.OnMyLocationChangeListener onMyLocationChangeListener; public UserLocationView(Context context) { super(context); - initialize(context); + init(context); } public UserLocationView(Context context, AttributeSet attrs) { super(context, attrs); - initialize(context); + init(context); } public UserLocationView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - initialize(context); + init(context); } - private void initialize(Context context) { - // View configuration - setEnabled(false); - setWillNotDraw(false); + private void init(Context context) { + markerScreenCoordinate = new LatLng(); - // Layout params - ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); - setLayoutParams(lp); + viewBounds = new Rect(); + +// setBackgroundColor(ContextCompat.getColor(context,android.R.color.darker_gray)); + + accurancyPaint = new Paint(); + accurancyPaint.setColor(Color.RED); + accurancyPaint.setAlpha(80); + + + setShadowDrawable(ContextCompat.getDrawable(context, R.drawable.ic_userlocationview_shadow)); + setShadowDrawableTint(Color.BLACK); + + setForegroundDrawable(ContextCompat.getDrawable(context, R.drawable.ic_userlocationview_bearing)); + setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(v.getContext(), "Click: " + getMeasuredWidth() + " " + getMeasuredHeight(), Toast.LENGTH_SHORT).show(); + } + }); + + setForegroundDrawableTint(Color.RED); - // 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(Paint.ANTI_ALIAS_FLAG); - mAccuracyPaintFill.setStyle(Paint.Style.FILL); - - mAccuracyPaintStroke = new Paint(Paint.ANTI_ALIAS_FLAG); - mAccuracyPaintStroke.setStyle(Paint.Style.STROKE); - mAccuracyPaintStroke.setStrokeWidth(0.5f * density); - setUserLocationAccuracyColor(accuracyColor); - - mAccuracyPath = new Path(); - mAccuracyBounds = new RectF(); - - setUserLocationDrawable(ContextCompat.getDrawable(getContext(), R.drawable.my_location)); - setUserLocationBearingDrawable(ContextCompat.getDrawable(getContext(), R.drawable.my_location_bearing)); - setUserLocationStaleDrawable(ContextCompat.getDrawable(getContext(), R.drawable.my_location_stale)); } - public void setMapboxMap(MapboxMap mapboxMap) { - mMapboxMap = mapboxMap; - mProjection = mapboxMap.getProjection(); + public void setForegroundDrawable(Drawable drawable) { + foregroundDrawable = drawable; + //TODO remove test only +// foregroundDrawable.setAlpha(80); + invalidateBounds(); + } + + public void setForegroundDrawableTint(@ColorInt int color) { + if (foregroundDrawable == null) { + return; + } + foregroundDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN); + } + + public PointF getForegroundCenter() { + return new PointF(foregroundBounds.centerX(), foregroundBounds.centerY()); + } + + public void setShadowDrawable(Drawable drawable) { + setShadowDrawable(drawable, 0, 0, 0, 0); + } + + public void setShadowDrawable(Drawable drawable, int left, int top, int right, int bottom) { + backgroundDrawable = drawable; + backgroundOffsetLeft = left; + backgroundOffsetTop = top; + backgroundOffsetRight = right; + backgroundOffsetBottom = bottom; + invalidateBounds(); + } + + public void setShadowDrawableTint(@ColorInt int color) { + if (backgroundDrawable == null) { + return; + } + + backgroundDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN); + } + + public void setAccurancyTint(@ColorInt int color, @IntRange(from = 0, to = 100) int alpha) { + accurancyPaint.setColor(color); + accurancyPaint.setAlpha(alpha); + } + + private void invalidateBounds() { + if (backgroundDrawable == null || foregroundDrawable == 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 accurancyWidth = 2 * maxAccurancy; + + backgroundBounds = new Rect(accurancyWidth - (backgroundWidth / 2) + horizontalOffset, accurancyWidth + verticalOffset - (backgroundWidth / 2), accurancyWidth + (backgroundWidth / 2) + horizontalOffset, accurancyWidth + (backgroundHeight / 2) + verticalOffset); + foregroundBounds = new Rect(accurancyWidth - (foregroundWidth / 2), accurancyWidth - (foregroundHeight / 2), accurancyWidth + (foregroundWidth / 2), accurancyWidth + (foregroundHeight / 2)); + + // invoke a new measure + invalidate(); } @Override - public void onDraw(Canvas canvas) { + protected void onDraw(Canvas canvas) { super.onDraw(canvas); - if (!mShowMarker) { - return; + if (foregroundBounds != null) { + canvas.drawCircle(foregroundBounds.centerX(), foregroundBounds.centerY(), foregroundBounds.width() / 2 + (accurancy / 100 * (getWidth() - foregroundBounds.width()) / 2), accurancyPaint); + } + + if (backgroundDrawable != null) { + backgroundDrawable.draw(canvas); + } + + // todo optimise + if (foregroundDrawable != null && foregroundBounds != null) { + getRotateDrawable(foregroundDrawable, compassDirection).draw(canvas); } - UserLocationViewDrawableHolder holder = getCurrentDrawableHolder(); - drawShadow(canvas, holder); - draw(canvas, holder); } - private UserLocationViewDrawableHolder getCurrentDrawableHolder() { - UserLocationViewDrawableHolder holder = mShowDirection ? mBearingHolder : mHolder; - return mStaleMarker ? mStaleHolder : holder; - } - - private void drawShadow(Canvas canvas, UserLocationViewDrawableHolder holder) { - if (!holder.hasShadow()) { - return; - } - canvas.save(); - canvas.translate(mShadowX, mShadowY); - canvas.concat(mMarkerScreenMatrix); - boolean willDraw = !canvas.quickReject(holder.mShadowBounds, Canvas.EdgeType.AA); - - if (willDraw) { - holder.mShadow.draw(canvas); - } - canvas.restore(); - } - - private void draw(Canvas canvas, UserLocationViewDrawableHolder holder) { - canvas.save(); - canvas.concat(mMarkerScreenMatrix); - 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(holder.mBounds, Canvas.EdgeType.AA); - - if (willDraw) { - if (mShowAccuracy && !mStaleMarker) { - canvas.drawPath(mAccuracyPath, mAccuracyPaintFill); - canvas.drawPath(mAccuracyPath, mAccuracyPaintStroke); + private Drawable getRotateDrawable(final Drawable d, final float angle) { + final Drawable[] arD = {d}; + foregroundRotatedDrawable = new LayerDrawable(arD) { + @Override + public void draw(final Canvas canvas) { + canvas.save(); + canvas.rotate(angle, foregroundBounds.centerX(), foregroundBounds.centerY()); + super.draw(canvas); + canvas.restore(); } - holder.mDrawable.draw(canvas); + }; + return foregroundRotatedDrawable; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + // position drawables within view + if (foregroundDrawable != null && foregroundBounds != null) { + foregroundDrawable.setBounds(foregroundBounds); + viewBounds.left = foregroundBounds.left; + viewBounds.top = foregroundBounds.top; + viewBounds.right = foregroundBounds.right; + viewBounds.bottom = foregroundBounds.bottom; } - canvas.restore(); + + if (backgroundDrawable != null && backgroundBounds != null) { + backgroundDrawable.setBounds(backgroundBounds); + viewBounds.union(backgroundBounds); + } + + setMeasuredDimension(4 * maxAccurancy, 4 * maxAccurancy); } - - public void setUserLocationDrawable(@NonNull Drawable drawable) { - setUserLocationDrawable(drawable, null); + //TODO add android annotation for floatrange + public void setTilt(double tilt) { + this.tilt = tilt; + setRotationX((float) tilt); } - public void setUserLocationDrawable(@NonNull Drawable drawable, @Nullable Drawable shadowDrawable) { - mHolder.setDrawable(drawable); - mHolder.setShadow(shadowDrawable); - update(); + public void setDirection(float direction) { + if (myBearingTrackingMode == MyBearingTracking.COMPASS) { + compassDirection = direction % 360.0f; + } else { + gpsDirection = direction % 360.0f; + } + updateOnNextFrame(); + } +// +// public void setMaxAccurancy(int maxAccurancy) { +// this.maxAccurancy = maxAccurancy; +// invalidateBounds(); +// } + + public void setAccurancy(float accurancy) { + this.accurancy = accurancy; + invalidate(); } - public void setUserLocationBearingDrawable(@NonNull Drawable drawable) { - setUserLocationBearingDrawable(drawable, null); + void updateOnNextFrame() { + mapboxMap.invalidate(); } - public void setUserLocationBearingDrawable(@NonNull Drawable drawable, @Nullable Drawable shadowDrawable) { - mBearingHolder.setDrawable(drawable); - mBearingHolder.setShadow(shadowDrawable); - update(); + public void onPause() { + paused = true; +// mBearingChangeListener.onPause(); +// cancelAnimations(); + toggleGps(false); } - public void setUserLocationStaleDrawable(@NonNull Drawable drawable) { - setUserLocationStaleDrawable(drawable, null); - } - - public void setUserLocationStaleDrawable(@NonNull Drawable drawable, @Nullable Drawable shadowDrawable) { - mStaleHolder.setDrawable(drawable); - mStaleHolder.setShadow(shadowDrawable); - update(); - } - - public void setUserLocationShadowOffset(int x, int y) { - mShadowX = x; - mShadowY = y; - update(); - } - - public void setUserLocationAccuracyColor(int color) { - mAccuracyPaintFill.setColor(color); - mAccuracyPaintFill.setAlpha((int) (255 * 0.25f)); - mAccuracyPaintStroke.setColor(color); - mAccuracyPaintStroke.setAlpha((int) (255 * 0.5f)); - update(); - } - - 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); + public void onResume() { + paused = false; + if (myBearingTrackingMode == MyBearingTracking.COMPASS) { +// mBearingChangeListener.onResume(); + } + if (isEnabled()) { + toggleGps(true); } } + public boolean isPaused() { + return paused; + } + + public void update() { + if (isEnabled() && showMarker) { + setVisibility(View.VISIBLE); + +// mStaleMarker = isStale(mUserLocation); + + // compute new marker position + // TODO add JNI method that takes existing pointf + if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { + markerScreenPoint = getMarkerScreenPoint(); + +// markerScreenPoint = new PointF(((uiSettings.getWidth() - getWidth() + contentPadding[0] - contentPadding[2]) / 2) +// , ((uiSettings.getHeight() - getHeight() - contentPadding[3] + contentPadding[1]) / 2)); + + setX(markerScreenPoint.x-getWidth()/2); + setY(markerScreenPoint.y-getHeight()/2); + } else if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { + double bearing = 0; + if (showDirection) { + bearing = myBearingTrackingMode == MyBearingTracking.COMPASS ? mBearingChangeListener.getCompassBearing() : location.getBearing(); + } else { + bearing = mapboxMap.getCameraPosition().bearing; + } + + if (currentMapViewCoordinate == null) { + currentMapViewCoordinate = mapboxMap.getCameraPosition().target; + } + + // only update if there is an actual change + if ((!currentMapViewCoordinate.equals(markerScreenCoordinate)) || (!(mCurrentBearing == bearing))) { + + CameraPosition cameraPosition = new CameraPosition.Builder() + .target(markerScreenCoordinate) + .bearing(bearing) + .build(); + mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 300, null); + +// mMarkerScreenMatrix.reset(); + markerScreenPoint = getMarkerScreenPoint(); +// mMarkerScreenMatrix.setTranslate(mMarkerScreenPoint.x, mMarkerScreenPoint.y); + + // set values for next check for actual change + currentMapViewCoordinate = markerScreenCoordinate; + mCurrentBearing = bearing; + } + } + + // rotate so arrow in points to bearing + if (showDirection) { + if (myBearingTrackingMode == MyBearingTracking.COMPASS && myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { + setDirection(compassDirection); +// mMarkerScreenMatrix.preRotate((float)(mCompassMarkerDirection + mMapboxMap.getCameraPosition().bearing)); + } else if (myBearingTrackingMode == MyBearingTracking.GPS) { + if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { + setDirection(gpsDirection); +// mMarkerScreenMatrix.preRotate((float)(mGpsMarkerDirection + mMapboxMap.getCameraPosition().bearing)); + } else { +// MarkerScreenMatrix.preRotate(gpsDirection); + } + } + } +// +// // 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); +// } + +// UserLocationViewDrawableHolder holder = getCurrentDrawableHolder(); +// +// +// RectF dotBounds = holder.mBounds; +// 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 PointF getMarkerScreenPoint() { + if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { + markerScreenPoint = projection.toScreenLocation(markerScreenCoordinate); + } else { + int[] contentPadding = mapboxMap.getPadding(); + UiSettings uiSettings = mapboxMap.getUiSettings(); + markerScreenPoint = new PointF(((uiSettings.getWidth() - getWidth() + contentPadding[0] - contentPadding[2]) / 2) + , ((uiSettings.getHeight() - getHeight() - contentPadding[3] + contentPadding[1]) / 2)); + } + return markerScreenPoint; + } + + public void setMapboxMap(MapboxMap mapboxMap) { + this.mapboxMap = mapboxMap; + this.projection = mapboxMap.getProjection(); + } + + @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); @@ -272,102 +435,6 @@ public final class UserLocationView extends View { 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); - } - - UserLocationViewDrawableHolder holder = getCurrentDrawableHolder(); - - - RectF dotBounds = holder.mBounds; - 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 * @@ -381,42 +448,219 @@ public final class UserLocationView extends View { // Set an initial location if one available Location lastLocation = locationServices.getLastLocation(); if (lastLocation != null) { - setLocation(lastLocation); + location = lastLocation; } - if (mUserLocationListener == null) { - mUserLocationListener = new UserLocationListener(this); + if (userLocationListener == null) { + userLocationListener = new UserLocationListener(this); } // Register for Location Updates - locationServices.addLocationListener(mUserLocationListener); + locationServices.addLocationListener(userLocationListener); } else { // Disable location and user dot - setLocation(null); + location = null; // Deregister for Location Updates - locationServices.removeLocationListener(mUserLocationListener); + locationServices.removeLocationListener(userLocationListener); } locationServices.toggleGPS(enableGps); } - public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) { - mMyBearingTrackingMode = myBearingTrackingMode; + public Location getLocation() { + return location; + } - if (myBearingTrackingMode == MyBearingTracking.COMPASS) { - mShowAccuracy = false; - mShowDirection = true; - mBearingChangeListener.onResume(); + 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 void setLocation(Location location) { + // if null we should hide the marker + if (location == null) { + showMarker = false; + showDirection = false; + showAccurancy = false; + + cancelAnimations(); + + location = null; + return; + } + + cancelAnimations(); + showMarker = true; + + LatLng previousCoordinate; + if (this.location == null) { + previousCoordinate = new LatLng(location); } else { - mBearingChangeListener.onPause(); - if (myBearingTrackingMode == MyBearingTracking.GPS) { - mShowDirection = (mUserLocation != null) && mUserLocation.hasBearing(); - } else { - mShowDirection = false; + previousCoordinate = new LatLng(this.location); + } + + if (myLocationTrackingMode == 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(); + markerScreenCoordinate = new LatLng(location); + } else { + // moving map under the tracker + markerScreenCoordinate = new LatLng(location); + } + + if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE && myBearingTrackingMode == MyBearingTracking.GPS) { + // show GPS direction + showDirection = location.hasBearing(); + if (showDirection) { + if (this.location != null && this.location.hasBearing()) { + gpsDirection = this.location.getBearing(); + } + float oldDir = gpsDirection; + 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 (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW && myBearingTrackingMode == MyBearingTracking.GPS) { + // always show north & rotate map below + showDirection = true; + gpsDirection = 0; + } + + showAccurancy = location.hasAccuracy(); + if (showAccurancy) { + if (this.location != null && this.location.hasAccuracy()) { + accurancy = this.location.getAccuracy(); + } + mMarkerAccuracyAnimator = ObjectAnimator.ofFloat(this, "accuracy", location.getAccuracy()); + mMarkerAccuracyAnimator.setDuration(1000); + mMarkerAccuracyAnimator.start(); + } + + this.location = location; + updateOnNextFrame(); + + if (onMyLocationChangeListener != null) { + onMyLocationChangeListener.onMyLocationChange(location); + } + } + + public void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener listener) { +// mOnMyLocationChangeListener = listener; + } + + + public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) { + this.myBearingTrackingMode = 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(); + } + + public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) { + this.myLocationTrackingMode = myLocationTrackingMode; + + if (myLocationTrackingMode != MyLocationTracking.TRACKING_NONE && location != null) { + // center map directly if we have a location fix + markerScreenCoordinate = new LatLng(location.getLatitude(), location.getLongitude()); + mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(location))); + + // center view directly +// mMarkerScreenMatrix.reset(); + markerScreenPoint = getMarkerScreenPoint(); + setX(markerScreenPoint.x); + setY(markerScreenPoint.y); +// mMarkerScreenMatrix.setTranslate(mMarkerScreenPoint.x, mMarkerScreenPoint.y); + } + } + + private void setCompass(float bearing) { + if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { + // animate marker + showDirection = true; + float oldDir = compassDirection; + 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(); + compassDirection = bearing; + + } else if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { + cancelAnimations(); + if (myBearingTrackingMode == MyBearingTracking.COMPASS) { + // always show north & change map direction + showDirection = true; + gpsDirection = 0; + compassDirection = 0; + update(); + } + } else { + showDirection = false; + } + } + + private static class UserLocationListener implements LocationListener { + + private WeakReference 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); } } - update(); } private class MyBearingListener implements SensorEventListener { @@ -461,7 +705,7 @@ public final class UserLocationView extends View { @Override public void onSensorChanged(SensorEvent event) { - if (mPaused) { + if (paused) { return; } @@ -503,224 +747,6 @@ public final class UserLocationView extends View { } } - private static class UserLocationListener implements LocationListener { - - private WeakReference 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; @@ -740,41 +766,11 @@ public final class UserLocationView extends View { float frac = animation.getAnimatedFraction(); double latitude = mFromLat + (mToLat - mFromLat) * frac; double longitude = mFromLng + (mToLng - mFromLng) * frac; - mMarkerCoordinate.setLatitude(latitude); - mMarkerCoordinate.setLongitude(longitude); + markerScreenCoordinate.setLatitude(latitude); + markerScreenCoordinate.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; - } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/ic_userlocationview_bearing.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/ic_userlocationview_bearing.png new file mode 100644 index 0000000000000000000000000000000000000000..e5ff0808e23611f4386a64ccf27c5232f660da57 GIT binary patch literal 8679 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 + + + + + + \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml index 5167ecf93..0793acbba 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml @@ -9,4 +9,6 @@ 10dp 16dp 76dp + 19dp + 20dp