From 8b79a8d6e63c6ae927dc32770ffeadd58be023e7 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 20 Oct 2015 22:37:50 +1100 Subject: [PATCH] [android] Implement UserLocationView Draws animated and synced GPS marker. Inlcudes direction arrow and accuracy ring. Fade out the compass after a delay when reset to north. Lots of other performance and memory improvements. add OnMyLocationChangeListener Move FAB with snackbar Fix getMetersPerPixelAtLatitude Fix some incorrect annotations Fixes #2668 Fixes #2411 Fixes #2678 Fixes #2675 Fixes #2566 Fixes #2549 Fixes #2692 Fixes #2572 --- android/cpp/jni.cpp | 2 +- android/cpp/native_map_view.cpp | 33 +- .../mapboxsdk/annotations/Annotation.java | 17 +- .../mapboxsdk/annotations/InfoWindow.java | 35 +- .../com/mapbox/mapboxsdk/geometry/LatLng.java | 12 + .../mapbox/mapboxsdk/views/CompassView.java | 96 +++- .../com/mapbox/mapboxsdk/views/MapView.java | 299 ++++-------- .../mapbox/mapboxsdk/views/NativeMapView.java | 32 +- .../mapboxsdk/views/UserLocationView.java | 462 ++++++++++++++++++ .../res/drawable-hdpi/location_marker.png | Bin 4788 -> 0 bytes .../res/drawable-ldpi/location_marker.png | Bin 4046 -> 0 bytes .../res/drawable-mdpi/location_marker.png | Bin 4379 -> 0 bytes .../res/drawable-xhdpi/location_marker.png | Bin 5720 -> 0 bytes .../res/drawable-xxhdpi/location_marker.png | Bin 7175 -> 0 bytes .../res/drawable-xxxhdpi/location_marker.png | Bin 8809 -> 0 bytes .../testapp/InfoWindowAdapterActivity.java | 3 +- .../mapboxsdk/testapp/MainActivity.java | 37 +- .../src/main/res/layout/activity_main.xml | 44 +- include/mbgl/android/native_map_view.hpp | 5 +- 19 files changed, 796 insertions(+), 281 deletions(-) create mode 100644 android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/UserLocationView.java delete mode 100644 android/java/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/location_marker.png delete mode 100644 android/java/MapboxGLAndroidSDK/src/main/res/drawable-ldpi/location_marker.png delete mode 100644 android/java/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/location_marker.png delete mode 100644 android/java/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/location_marker.png delete mode 100644 android/java/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/location_marker.png delete mode 100644 android/java/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/location_marker.png diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp index bf16a3dbf..c242f94e3 100644 --- a/android/cpp/jni.cpp +++ b/android/cpp/jni.cpp @@ -481,7 +481,7 @@ void JNICALL nativeRenderSync(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) mbgl::Log::Debug(mbgl::Event::JNI, "nativeRenderSync"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); - nativeMapView->renderSync(); + nativeMapView->getMap().renderSync(); } void JNICALL nativeViewResize(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jint width, jint height) { diff --git a/android/cpp/native_map_view.cpp b/android/cpp/native_map_view.cpp index 31fb5453a..9c4d4b1d1 100644 --- a/android/cpp/native_map_view.cpp +++ b/android/cpp/native_map_view.cpp @@ -131,12 +131,22 @@ std::array NativeMapView::getFramebufferSize() const { void NativeMapView::activate() { mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::activate"); + + assert(vm != nullptr); + + renderDetach = attach_jni_thread(vm, &renderEnv, "Map Thread"); + if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) && (context != EGL_NO_CONTEXT)) { if (!eglMakeCurrent(display, surface, surface, context)) { mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d", eglGetError()); throw new std::runtime_error("eglMakeCurrent() failed"); } + + if (!eglSwapInterval(display, 0)) { + mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapInterval() returned error %d", eglGetError()); + throw new std::runtime_error("eglSwapInterval() failed"); + } } else { mbgl::Log::Info(mbgl::Event::Android, "Not activating as we are not ready"); } @@ -144,6 +154,9 @@ void NativeMapView::activate() { void NativeMapView::deactivate() { mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::deactivate"); + + assert(vm != nullptr); + if (display != EGL_NO_DISPLAY) { if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", @@ -153,6 +166,8 @@ void NativeMapView::deactivate() { } else { mbgl::Log::Info(mbgl::Event::Android, "Not deactivating as we are not ready"); } + + detach_jni_thread(vm, &renderEnv, renderDetach); } void NativeMapView::invalidate() { @@ -173,11 +188,15 @@ void NativeMapView::invalidate() { } void NativeMapView::beforeRender() { + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::beforeRender()"); // no-op } void NativeMapView::afterRender() { - mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::afterRender"); + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::afterRender()"); + + assert(vm != nullptr); + assert(obj != nullptr); if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) { if (!eglSwapBuffers(display, surface)) { @@ -185,6 +204,7 @@ void NativeMapView::afterRender() { eglGetError()); throw new std::runtime_error("eglSwapBuffers() failed"); } + updateFps(); } else { mbgl::Log::Info(mbgl::Event::Android, "Not swapping as we are not ready"); @@ -709,17 +729,6 @@ void NativeMapView::updateFps() { detach_jni_thread(vm, &env, detach); } -void NativeMapView::renderSync() { - mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::renderSync()"); - - if (map->isPaused()) { - mbgl::Log::Debug(mbgl::Event::Android, "Not rendering as map is paused"); - return; - } - - map->renderSync(); -} - void NativeMapView::resizeView(int w, int h) { width = w; height = h; diff --git a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Annotation.java b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Annotation.java index f5f748ad0..1b8f6491e 100644 --- a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Annotation.java +++ b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Annotation.java @@ -2,6 +2,8 @@ package com.mapbox.mapboxsdk.annotations; import com.mapbox.mapboxsdk.views.MapView; +import java.lang.ref.WeakReference; + public abstract class Annotation implements Comparable { /** @@ -10,7 +12,7 @@ public abstract class Annotation implements Comparable { * Internal C++ id is stored as unsigned int. */ private long id = -1; // -1 unless added to a MapView - private MapView mapView; + private WeakReference mapView; private float alpha = 1.0f; private boolean visible = true; @@ -33,8 +35,10 @@ public abstract class Annotation implements Comparable { } public void remove() { - if (mapView == null) return; - mapView.removeAnnotation(this); + if ((mapView == null) || (mapView.get() == null)) { + return; + } + mapView.get().removeAnnotation(this); } void setAlpha(float alpha) { @@ -52,11 +56,14 @@ public abstract class Annotation implements Comparable { * Do not use this method. Used internally by the SDK. */ public void setMapView(MapView mapView) { - this.mapView = mapView; + this.mapView = new WeakReference(mapView); } protected MapView getMapView() { - return mapView; + if (mapView == null) { + return null; + } + return mapView.get(); } void setVisible(boolean visible) { diff --git a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java index 0e60c2510..4b4fdb8c1 100644 --- a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java +++ b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java @@ -13,13 +13,15 @@ import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.views.MapView; +import java.lang.ref.WeakReference; + /** * A tooltip view */ final class InfoWindow { - private Marker boundMarker; - private MapView mMapView; + private WeakReference mBoundMarker; + private WeakReference mMapView; private boolean mIsVisible; protected View mView; @@ -29,7 +31,7 @@ final class InfoWindow { static int mImageId = 0; public InfoWindow(int layoutResId, MapView mapView) { - mMapView = mapView; + mMapView = new WeakReference(mapView); mIsVisible = false; mView = LayoutInflater.from(mapView.getContext()).inflate(layoutResId, mapView, false); @@ -50,7 +52,7 @@ final class InfoWindow { } public InfoWindow(View view, MapView mapView) { - mMapView = mapView; + mMapView = new WeakReference(mapView); mIsVisible = false; mView = view; @@ -80,7 +82,7 @@ final class InfoWindow { mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); // Calculate default Android x,y coordinate - PointF coords = mMapView.toScreenLocation(position); + PointF coords = mMapView.get().toScreenLocation(position); float x = coords.x - (mView.getMeasuredWidth() / 2) + offsetX; float y = coords.y - mView.getMeasuredHeight() + offsetY; @@ -89,25 +91,25 @@ final class InfoWindow { float left = x; // get right/left map view - float mapRight = mMapView.getRight(); - float mapLeft = mMapView.getLeft(); + float mapRight = mMapView.get().getRight(); + float mapLeft = mMapView.get().getLeft(); if (mView instanceof InfoWindowView) { // only apply repositioning/margin for InfoWindowView - Resources resources = mMapView.getContext().getResources(); + Resources resources = mMapView.get().getContext().getResources(); float margin = resources.getDimension(R.dimen.infowindow_margin); float tipViewOffset = resources.getDimension(R.dimen.infowindow_tipview_width) / 2; float tipViewMarginLeft = mView.getMeasuredWidth() / 2 - tipViewOffset; // fit screen on right - if (right > mMapView.getRight()) { + if (right > mMapView.get().getRight()) { x -= right - mapRight; tipViewMarginLeft += right - mapRight + tipViewOffset; right = x + mView.getMeasuredWidth(); } // fit screen left - if (left < mMapView.getLeft()) { + if (left < mMapView.get().getLeft()) { x += mapLeft - left; tipViewMarginLeft -= mapLeft - left + tipViewOffset; left = x; @@ -136,7 +138,7 @@ final class InfoWindow { mView.setY(y); close(); //if it was already opened - mMapView.addView(mView, lp); + mMapView.get().addView(mView, lp); mIsVisible = true; return this; } @@ -172,7 +174,7 @@ final class InfoWindow { * @return the mapView */ public MapView getMapView() { - return mMapView; + return mMapView.get(); } /** @@ -204,13 +206,16 @@ final class InfoWindow { //by default, do nothing } - public InfoWindow setBoundMarker(Marker aBoundMarker) { - this.boundMarker = aBoundMarker; + public InfoWindow setBoundMarker(Marker boundMarker) { + mBoundMarker = new WeakReference(boundMarker); return this; } public Marker getBoundMarker() { - return boundMarker; + if (mBoundMarker == null) { + return null; + } + return mBoundMarker.get(); } /** diff --git a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLng.java b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLng.java index 71480e958..7f036aca4 100644 --- a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLng.java +++ b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLng.java @@ -71,16 +71,28 @@ public class LatLng implements ILatLng, Parcelable, Serializable { altitude = in.readDouble(); } + public void setLatitude(double latitude) { + this.latitude = latitude; + } + @Override public double getLatitude() { return latitude; } + public void setLongitude(double longitude) { + this.longitude = longitude; + } + @Override public double getLongitude() { return longitude; } + public void setAltitude(double altitude) { + this.altitude = altitude; + } + @Override public double getAltitude() { return altitude; diff --git a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/CompassView.java b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/CompassView.java index e1d072358..715b57732 100644 --- a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/CompassView.java +++ b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/CompassView.java @@ -1,23 +1,27 @@ package com.mapbox.mapboxsdk.views; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.content.Context; -import android.hardware.GeomagneticField; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.location.Location; import android.support.v4.content.ContextCompat; import android.util.AttributeSet; import android.view.View; -import android.widget.FrameLayout; +import android.view.ViewGroup; +import android.view.ViewPropertyAnimator; import android.widget.ImageView; + import com.mapbox.mapboxsdk.R; import java.lang.ref.WeakReference; +import java.util.Timer; +import java.util.TimerTask; final class CompassView extends ImageView { + private Timer mNorthTimer; + private double mDirection = 0.0f; + private ViewPropertyAnimator mFadeAnimator; + public CompassView(Context context) { super(context); initialize(context); @@ -34,20 +38,94 @@ final class CompassView extends ImageView { } private void initialize(Context context) { + // View configuration setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.compass)); setContentDescription(getResources().getString(R.string.compassContentDescription)); + setEnabled(false); // Layout params float mScreenDensity = context.getResources().getDisplayMetrics().density; - FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams((int) (48 * mScreenDensity), (int) (48 * mScreenDensity)); + ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams((int) (48 * mScreenDensity), (int) (48 * mScreenDensity)); setLayoutParams(lp); } @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); - setVisibility(enabled ? View.VISIBLE : View.INVISIBLE); + if (enabled) { + if (mDirection != 0.0) { + if (mNorthTimer != null){ + mNorthTimer.cancel(); + mNorthTimer = null; + } + if (mFadeAnimator != null) { + mFadeAnimator.cancel(); + } + mFadeAnimator = null; + setAlpha(1.0f); + setVisibility(View.VISIBLE); + } + } else { + if (mNorthTimer != null){ + mNorthTimer.cancel(); + mNorthTimer = null; + } + if (mFadeAnimator != null) { + mFadeAnimator.cancel(); + } + mFadeAnimator = null; + setVisibility(View.INVISIBLE); + } + } + + public void update(double direction) { + mDirection = direction; + setRotation((float) direction); + + if (direction == 0.0) { + if (getVisibility() == View.INVISIBLE) { + return; + } + + if (mNorthTimer == null) { + if (mFadeAnimator != null) { + mFadeAnimator.cancel(); + } + mFadeAnimator = null; + + mNorthTimer = new Timer("CompassView North timer"); + mNorthTimer.schedule(new TimerTask() { + @Override + public void run() { + post(new Runnable() { + @Override + public void run() { + setAlpha(1.0f); + mFadeAnimator = animate().alpha(0.0f).setDuration(1000); + mFadeAnimator.setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + setVisibility(View.INVISIBLE); + mNorthTimer = null; + } + }); + } + }); + } + }, 1000); + } + } else { + if (mNorthTimer != null){ + mNorthTimer.cancel(); + mNorthTimer = null; + } + if (mFadeAnimator != null) { + mFadeAnimator.cancel(); + } + setAlpha(1.0f); + setVisibility(View.VISIBLE); + } } public static class CompassClickListener implements View.OnClickListener { diff --git a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/MapView.java b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/MapView.java index dd757082c..b4e4e7b77 100644 --- a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/MapView.java +++ b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/MapView.java @@ -1,6 +1,5 @@ package com.mapbox.mapboxsdk.views; -import android.animation.Animator; import android.app.Activity; import android.app.ActivityManager; import android.app.Dialog; @@ -13,7 +12,7 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.TypedArray; import android.graphics.Bitmap; -import android.graphics.Matrix; +import android.graphics.Canvas; import android.graphics.PointF; import android.graphics.RectF; import android.graphics.SurfaceTexture; @@ -48,7 +47,6 @@ import android.view.TextureView; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; -import android.view.ViewPropertyAnimator; import android.widget.ArrayAdapter; import android.widget.FrameLayout; import android.widget.ImageView; @@ -73,10 +71,6 @@ import com.mapbox.mapboxsdk.geometry.BoundingBox; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngZoom; import com.mapbox.mapboxsdk.utils.ApiAccess; -import com.mapzen.android.lost.api.LocationListener; -import com.mapzen.android.lost.api.LocationRequest; -import com.mapzen.android.lost.api.LocationServices; -import com.mapzen.android.lost.api.LostApiClient; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -170,7 +164,7 @@ public final class MapView extends FrameLayout { private NativeMapView mNativeMapView; // Used to track rendering - private Boolean mDirty = false; + private TextureView mTextureView; // Used to handle DPI scaling private float mScreenDensity = 1.0f; @@ -194,14 +188,7 @@ public final class MapView extends FrameLayout { private Context mContext; // Used for user location - private LostApiClient mLocationClient; - private LocationRequest mLocationRequest; - private ImageView mGpsMarker; - private float mGpsMarkerOffset; - private Location mGpsLocation; - private MyLocationListener mLocationListener; - private ViewPropertyAnimator mGpsMarkerAnimatorX; - private ViewPropertyAnimator mGpsMarkerAnimatorY; + private UserLocationView mUserLocationView; // Used for the compass private CompassView mCompassView; @@ -222,7 +209,7 @@ public final class MapView extends FrameLayout { private ImageView mAttributionsView; // Used to manage MapChange event listeners - private ArrayList mOnMapChangedListener; + private ArrayList mOnMapChangedListener = new ArrayList<>(); // Used to manage map click event listeners private OnMapClickListener mOnMapClickListener; @@ -247,7 +234,6 @@ public final class MapView extends FrameLayout { private boolean mScrollEnabled = true; private boolean mRotateEnabled = true; private String mStyleUrl; - private boolean mIsMyLocationEnabled = false; // // Inner classes @@ -479,10 +465,26 @@ public final class MapView extends FrameLayout { * @return View to be shown as a {@code InfoWindow}. If null is returned the default * {@code InfoWindow} will be shown. */ - @NonNull + @Nullable View getInfoWindow(@NonNull Marker marker); } + /** + * Interface definition for a callback to be invoked when the the My Location dot + * (which signifies the user's location) changes location. + * + * @see MapView#setOnMyLocationChangeListener(OnMyLocationChangeListener) + */ + public interface OnMyLocationChangeListener { + /** + * Called when the location of the My Location dot has changed + * (be it latitude/longitude, bearing or accuracy). + * + * @param location The current location of the My Location dot The type of map change event. + */ + void onMyLocationChange(@Nullable Location location); + } + // // Constructors // @@ -563,9 +565,12 @@ public final class MapView extends FrameLayout { // Save the context mContext = context; + setWillNotDraw(false); + // Create the TextureView - TextureView textureView = new TextureView(mContext); - addView(textureView); + mTextureView = new TextureView(mContext); + addView(mTextureView); + mTextureView.setSurfaceTextureListener(new SurfaceTextureListener()); // Check if we are in Android Studio UI editor to avoid error in layout preview if (isInEditMode()) { @@ -600,9 +605,6 @@ public final class MapView extends FrameLayout { setFocusableInTouchMode(true); requestFocus(); - // Register the TextureView callbacks - textureView.setSurfaceTextureListener(new SurfaceTextureListener()); - // Touch gesture detectors mGestureDetector = new GestureDetectorCompat(context, new GestureListener()); mGestureDetector.setIsLongpressEnabled(true); @@ -628,26 +630,12 @@ public final class MapView extends FrameLayout { onConnectivityChanged(isConnected); } - // Setup location services - mLocationClient = new LostApiClient.Builder(getContext()).build(); - mLocationRequest = LocationRequest.create() - .setFastestInterval(1250l) - .setSmallestDisplacement(2.0f) - .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); - // Setup user location UI - mGpsMarker = new ImageView(getContext()); - mGpsMarker.setImageResource(R.drawable.location_marker); - mGpsMarker.setVisibility(View.INVISIBLE); - float iconSize = 27.0f * mScreenDensity; - mGpsMarkerOffset = iconSize/2; - FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams((int) iconSize, (int) iconSize); - mGpsMarker.setLayoutParams(lp); - addView(mGpsMarker); + mUserLocationView = new UserLocationView(this, getContext()); + addView(mUserLocationView); // Setup compass mCompassView = new CompassView(mContext); - mCompassView.setVisibility(View.INVISIBLE); mCompassView.setOnClickListener(new CompassView.CompassClickListener(this)); addView(mCompassView); @@ -655,7 +643,7 @@ public final class MapView extends FrameLayout { mLogoView = new ImageView(mContext); mLogoView.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.ic_logo_mapbox)); mLogoView.setContentDescription(getResources().getString(R.string.mapboxIconContentDescription)); - LayoutParams logoParams = new FrameLayout.LayoutParams( + ViewGroup.LayoutParams logoParams = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); mLogoView.setLayoutParams(logoParams); addView(mLogoView); @@ -675,10 +663,6 @@ public final class MapView extends FrameLayout { addView(mAttributionsView); mAttributionsView.setOnClickListener(new AttributionOnClickListener(this)); - // Setup Support For Listener Tracking - // MapView's internal listener is setup in onCreate() - mOnMapChangedListener = new ArrayList<>(); - // Load the attributes TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MapView, 0, 0); try { @@ -887,6 +871,9 @@ public final class MapView extends FrameLayout { public void onDestroy() { mNativeMapView.terminateContext(); mNativeMapView.terminateDisplay(); + mNativeMapView.destroySurface(); + mNativeMapView.destroy(); + mNativeMapView = null; } /** @@ -901,6 +888,7 @@ public final class MapView extends FrameLayout { */ @UiThread public void onStop() { + mUserLocationView.cancelAnimations(); } /** @@ -912,10 +900,7 @@ public final class MapView extends FrameLayout { getContext().unregisterReceiver(mConnectivityReceiver); mConnectivityReceiver = null; - if (mIsMyLocationEnabled) { - toggleGps(false); - } - + mUserLocationView.pause(); mNativeMapView.pause(); } @@ -928,11 +913,9 @@ public final class MapView extends FrameLayout { mConnectivityReceiver = new ConnectivityReceiver(); mContext.registerReceiver(mConnectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); - if (mIsMyLocationEnabled) { - toggleGps(true); - } - + mUserLocationView.resume(); mNativeMapView.resume(); + mNativeMapView.update(); } /** @@ -1360,7 +1343,6 @@ public final class MapView extends FrameLayout { * @param style The bundled style. Accepts one of the values from {@link Style}. */ @UiThread - @NonNull public void setStyle(@Style.StyleUrl String style) { setStyleUrl(style); } @@ -1694,8 +1676,10 @@ public final class MapView extends FrameLayout { throw new NullPointerException("markerOptionsList is null"); } - List markers = new ArrayList<>(markerOptionsList.size()); - for (MarkerOptions markerOptions : markerOptionsList) { + int count = markerOptionsList.size(); + List markers = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + MarkerOptions markerOptions = markerOptionsList.get(i); Marker marker = prepareMarker(markerOptions); markers.add(marker); } @@ -1703,7 +1687,6 @@ public final class MapView extends FrameLayout { long[] ids = mNativeMapView.addMarkers(markers); Marker m; - int count = markers.size(); for (int i = 0; i < count; i++) { m = markers.get(i); m.setId(ids[i]); @@ -1749,8 +1732,10 @@ public final class MapView extends FrameLayout { } // TODO make faster in JNI - List polylines = new ArrayList<>(polylineOptionsList.size()); - for (PolylineOptions polylineOptions : polylineOptionsList) { + int count = polylineOptionsList.size(); + List polylines = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + PolylineOptions polylineOptions = polylineOptionsList.get(i); polylines.add(addPolyline(polylineOptions)); } @@ -1842,7 +1827,9 @@ public final class MapView extends FrameLayout { } // TODO make faster in JNI - for (Annotation annotation : annotationList) { + int count = annotationList.size(); + for (int i = 0; i < count; i++) { + Annotation annotation = annotationList.get(i); removeAnnotation(annotation); } } @@ -1887,8 +1874,8 @@ public final class MapView extends FrameLayout { long[] ids = mNativeMapView.getAnnotationsInBounds(bbox); List idsList = new ArrayList<>(ids.length); - for (long id : ids) { - idsList.add(id); + for (int i = 0; i < ids.length; i++) { + idsList.add(ids[i]); } List annotations = new ArrayList<>(ids.length); @@ -1924,7 +1911,7 @@ public final class MapView extends FrameLayout { */ @UiThread public double getMetersPerPixelAtLatitude(@FloatRange(from = -180, to = 180) double latitude) { - return mNativeMapView.getMetersPerPixelAtLatitude(latitude, getZoomLevel()); + return mNativeMapView.getMetersPerPixelAtLatitude(latitude, getZoomLevel()) / mScreenDensity; } private void selectMarker(Marker marker) { @@ -1977,7 +1964,9 @@ public final class MapView extends FrameLayout { } private void adjustTopOffsetPixels() { - for (Annotation annotation : mAnnotations) { + int count = mAnnotations.size(); + for (int i = 0; i < count; i++) { + Annotation annotation = mAnnotations.get(i); if (annotation instanceof Marker) { Marker marker = (Marker) annotation; marker.setTopOffsetPixels( @@ -2012,29 +2001,16 @@ public final class MapView extends FrameLayout { // Called when the map needs to be rerendered // Called via JNI from NativeMapView - synchronized protected void onInvalidate() { - if (!mDirty) { - mDirty = true; - postRender(); - } + protected void onInvalidate() { + postInvalidate(); } - private void postRender() { - Runnable mRunnable = new Runnable() { - @Override - public void run() { - updateCompass(); - updateGpsMarker(); - mNativeMapView.renderSync(); - mDirty = false; - } - }; - - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - postOnAnimation(mRunnable); - } else { - postDelayed(mRunnable, 1000 / 60); + @Override + public void onDraw(Canvas canvas) { + if (!mNativeMapView.isPaused()) { + mNativeMapView.renderSync(); } + super.onDraw(canvas); } @Override @@ -2059,7 +2035,9 @@ public final class MapView extends FrameLayout { // Must do all EGL/GL ES destruction here @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - mNativeMapView.destroySurface(); + if (mNativeMapView != null) { + mNativeMapView.destroySurface(); + } return true; } @@ -2070,13 +2048,20 @@ public final class MapView extends FrameLayout { mNativeMapView.resizeFramebuffer(width, height); } - // Not used + // Called when the SurfaceTexure frame is drawn to screen + // Must sync with UI here @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { - // Do nothing + mCompassView.update(getDirection()); + mUserLocationView.update(); } } + // Used by UserLocationView + void update() { + mNativeMapView.update(); + } + // // View events // @@ -2111,7 +2096,7 @@ public final class MapView extends FrameLayout { // Called when user touches the screen, all positions are absolute @Override - public boolean onTouchEvent(MotionEvent event) { + public boolean onTouchEvent(@NonNull MotionEvent event) { // Check and ignore non touch or left clicks if ((event.getButtonState() != 0) && (event.getButtonState() != MotionEvent.BUTTON_PRIMARY)) { @@ -2260,7 +2245,9 @@ public final class MapView extends FrameLayout { if (newSelectedMarkerId >= 0) { - for (Annotation annotation : mAnnotations) { + int count = mAnnotations.size(); + for (int i = 0; i < count; i++) { + Annotation annotation = mAnnotations.get(i); if (annotation instanceof Marker) { if (annotation.getId() == newSelectedMarkerId) { if (mSelectedMarker == null || annotation.getId() != mSelectedMarker.getId()) { @@ -2512,7 +2499,7 @@ public final class MapView extends FrameLayout { // Called when the user presses a key, also called for repeating keys held // down @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { + public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) { // If the user has held the scroll key down for a while then accelerate // the scroll speed double scrollDist = event.getRepeatCount() >= 5 ? 50.0 : 10.0; @@ -2761,7 +2748,7 @@ public final class MapView extends FrameLayout { // Called when the mouse pointer enters or exits the view // or when it fades in or out due to movement @Override - public boolean onHoverEvent(MotionEvent event) { + public boolean onHoverEvent(@NonNull MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_HOVER_ENTER: case MotionEvent.ACTION_HOVER_MOVE: @@ -2840,18 +2827,12 @@ public final class MapView extends FrameLayout { // Called when the map view transformation has changed // Called via JNI from NativeMapView // Forward to any listeners - protected void onMapChanged(int rawChange) { - final int mapChange = rawChange; + protected void onMapChanged(int mapChange) { if (mOnMapChangedListener != null) { - post(new Runnable() { - @Override - public void run() { - for (OnMapChangedListener listener : mOnMapChangedListener) { - //noinspection ResourceType - listener.onMapChanged(mapChange); - } - } - }); + int count = mOnMapChangedListener.size(); + for (int i = 0; i < count; i++) { + mOnMapChangedListener.get(i).onMapChanged(mapChange); + } } } @@ -2883,14 +2864,14 @@ public final class MapView extends FrameLayout { // Called via JNI from NativeMapView // Forward to any listener protected void onFpsChanged(final double fps) { - if (mOnFpsChangedListener != null) { - post(new Runnable() { - @Override - public void run() { + post(new Runnable() { + @Override + public void run() { + if (mOnFpsChangedListener != null) { mOnFpsChangedListener.onFpsChanged(fps); } - }); - } + } + }); } /** @@ -2959,13 +2940,13 @@ public final class MapView extends FrameLayout { */ @UiThread public boolean isMyLocationEnabled() { - return mIsMyLocationEnabled; + return mUserLocationView.isEnabled(); } /** * Enables or disables the my-location layer. * While enabled, the my-location layer continuously draws an indication of a user's current - * location. + * location and bearing. *

* In order to use the my-location-layer feature you need to request permission for either * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} @@ -2975,8 +2956,7 @@ public final class MapView extends FrameLayout { */ @UiThread public void setMyLocationEnabled(boolean enabled) { - mIsMyLocationEnabled = enabled; - toggleGps(enabled); + mUserLocationView.setEnabled(enabled); } /** @@ -2987,74 +2967,19 @@ public final class MapView extends FrameLayout { @UiThread @Nullable public Location getMyLocation() { - return mGpsLocation; + return mUserLocationView.getLocation(); } /** - * Enabled / Disable GPS location updates along with updating the UI + * Sets a callback that's invoked when the the My Location dot + * (which signifies the user's location) changes location. * - * @param enableGps true if GPS is to be enabled, false if GPS is to be disabled + * @param listener The callback that's invoked when the user clicks on a marker. + * To unset the callback, use null. */ - private void toggleGps(boolean enableGps) { - if (enableGps) { - if (!mLocationClient.isConnected()) { - mGpsLocation = null; - mLocationClient.connect(); - updateLocation(LocationServices.FusedLocationApi.getLastLocation()); - mLocationListener = new MyLocationListener(); - LocationServices.FusedLocationApi.requestLocationUpdates(mLocationRequest, mLocationListener); - } - } else { - if (mLocationClient.isConnected()) { - LocationServices.FusedLocationApi.removeLocationUpdates(mLocationListener); - mLocationListener = null; - mLocationClient.disconnect(); - mGpsLocation = null; - } - } - - onInvalidate(); - } - - private class MyLocationListener implements LocationListener { - @Override - public void onLocationChanged(Location location) { - updateLocation(location); - } - } - - // Handles location updates from GPS - private void updateLocation(Location location) { - if (location != null) { - mGpsLocation = location; - updateGpsMarker(); - } - } - - private void updateGpsMarker() { - if (mIsMyLocationEnabled && mGpsLocation != null) { - mGpsMarker.setVisibility(View.VISIBLE); - LatLng coordinate = new LatLng(mGpsLocation); - PointF screenLocation = toScreenLocation(coordinate); - if (!mDirty) { - // Map is idle, animate change of location - mGpsMarkerAnimatorX = mGpsMarker.animate().x(screenLocation.x - mGpsMarkerOffset); - mGpsMarkerAnimatorY = mGpsMarker.animate().y(screenLocation.y - mGpsMarkerOffset); - } else { - // Map is not idle, set value, don't animate - if (mGpsMarkerAnimatorX != null) { - mGpsMarkerAnimatorX.cancel(); - mGpsMarkerAnimatorY.cancel(); - } - // Reposition correctly - mGpsMarker.setX(screenLocation.x - mGpsMarkerOffset); - mGpsMarker.setY(screenLocation.y - mGpsMarkerOffset); - } - } else { - if (mGpsMarker != null) { - mGpsMarker.setVisibility(View.INVISIBLE); - } - } + @UiThread + public void setOnMyLocationChangeListener(@Nullable OnMyLocationChangeListener listener) { + mUserLocationView.setOnMyLocationChangeListener(listener); } // @@ -3084,7 +3009,6 @@ public final class MapView extends FrameLayout { @UiThread public void setCompassEnabled(boolean compassEnabled) { mCompassView.setEnabled(compassEnabled); - onInvalidate(); } /** @@ -3096,8 +3020,8 @@ public final class MapView extends FrameLayout { * @param gravity One of the values from {@link Gravity}. * @see Gravity */ - @UiThread - public void setCompassGravity(int gravity) { + @UiThread + public void setCompassGravity(int gravity) { setWidgetGravity(mCompassView, gravity); } @@ -3115,25 +3039,6 @@ public final class MapView extends FrameLayout { setWidgetMargins(mCompassView, left, top, right, bottom); } - // Rotates an ImageView - does not work if the ImageView has padding, use margins - private void rotateImageView(ImageView imageView, float angle) { - Matrix matrix = new Matrix(); - matrix.setScale((float) imageView.getWidth() / (float) imageView.getDrawable().getIntrinsicWidth(), (float) imageView.getHeight() / (float) imageView.getDrawable().getIntrinsicHeight()); - matrix.postRotate(angle, (float) imageView.getWidth() / 2.0f, (float) imageView.getHeight() / 2.0f); - imageView.setImageMatrix(matrix); - imageView.setScaleType(ImageView.ScaleType.MATRIX); - } - - // Updates the UI to match the current map's position - private void updateCompass() { - if (isCompassEnabled()) { - mCompassView.setVisibility(VISIBLE); - rotateImageView(mCompassView, (float) getDirection()); - } else { - mCompassView.setVisibility(INVISIBLE); - } - } - // // Logo // diff --git a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/NativeMapView.java b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/NativeMapView.java index 29542f35f..50b242a9f 100644 --- a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/NativeMapView.java +++ b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/NativeMapView.java @@ -10,6 +10,8 @@ import com.mapbox.mapboxsdk.geometry.BoundingBox; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngZoom; import com.mapbox.mapboxsdk.geometry.ProjectedMeters; + +import java.lang.ref.WeakReference; import java.util.List; // Class that wraps the native methods for convenience @@ -23,11 +25,13 @@ final class NativeMapView { // Instance members // + boolean mDestroyed = false; + // Holds the pointer to JNI NativeMapView private long mNativeMapViewPtr = 0; // Used for callbacks - private MapView mMapView; + private WeakReference mMapView; // // Static methods @@ -50,7 +54,7 @@ final class NativeMapView { throw new IllegalArgumentException("totalMemory cannot be negative."); } - mMapView = mapView; + mMapView = new WeakReference<>(mapView); // Create the NativeMapView mNativeMapViewPtr = nativeCreate(cachePath, dataPath, apkPath, pixelRatio, availableProcessors, totalMemory); @@ -60,6 +64,17 @@ final class NativeMapView { // Methods // + public void destroy() { + nativeDestroy(mNativeMapViewPtr); + mNativeMapViewPtr = 0; + mMapView = null; + mDestroyed = true; + } + + public boolean wasDestroyed() { + return mDestroyed; + } + public void initializeDisplay() { nativeInitializeDisplay(mNativeMapViewPtr); } @@ -434,28 +449,21 @@ final class NativeMapView { // protected void onInvalidate() { - mMapView.onInvalidate(); + mMapView.get().onInvalidate(); } protected void onMapChanged(int rawChange) { - mMapView.onMapChanged(rawChange); + mMapView.get().onMapChanged(rawChange); } protected void onFpsChanged(double fps) { - mMapView.onFpsChanged(fps); + mMapView.get().onFpsChanged(fps); } // // JNI methods // - @Override - protected void finalize() throws Throwable { - nativeDestroy(mNativeMapViewPtr); - mNativeMapViewPtr = 0; - super.finalize(); - } - private native long nativeCreate(String cachePath, String dataPath, String apkPath, float pixelRatio, int availableProcessors, long totalMemory); private native void nativeDestroy(long nativeMapViewPtr); diff --git a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/UserLocationView.java b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/UserLocationView.java new file mode 100644 index 000000000..3d2416ecd --- /dev/null +++ b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/UserLocationView.java @@ -0,0 +1,462 @@ +package com.mapbox.mapboxsdk.views; + +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +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.location.Location; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapzen.android.lost.api.LocationListener; +import com.mapzen.android.lost.api.LocationRequest; +import com.mapzen.android.lost.api.LocationServices; +import com.mapzen.android.lost.api.LostApiClient; + +import java.lang.ref.WeakReference; + +final class UserLocationView extends View { + + private WeakReference mMapView; + + private float mDensity; + private PointF mMarkerScreenPoint; + private Matrix mMarkerScreenMatrix; + + private Paint mOuterRingPaint; + private Path mOuterRingPath; + private RectF mOuterRingBounds; + + private Paint mInnerRingPaintFill; + private Paint mInnerRingPaintStroke; + private Path mInnerRingPath; + + private boolean mShowDirection; + private Paint mDirectionArrowPaint; + private Path mDirectionArrowPath; + + private boolean mShowAccuracy; + private Paint mAccuracyPaintFill; + private Paint mAccuracyPaintStroke; + private Path mAccuracyPath; + private RectF mAccuracyBounds; + + private Rect mDirtyRect; + private RectF mDirtyRectF; + + private boolean mShowMarker; + private LatLng mMarkerCoordinate; + private ValueAnimator mMarkerCoordinateAnimator; + private float mMarkerDirection; + private ObjectAnimator mMarkerDirectionAnimator; + private float mMarkerAccuracy; + private ObjectAnimator mMarkerAccuracyAnimator; + + private boolean mPaused = false; + private LostApiClient mLocationClient; + private LocationRequest mLocationRequest; + private Location mUserLocation; + private MyLocationListener mLocationListener; + + MapView.OnMyLocationChangeListener mOnMyLocationChangeListener; + + public UserLocationView(MapView mapView, Context context) { + super(context); + initialize(mapView, context); + } + + public UserLocationView(MapView mapView, Context context, AttributeSet attrs) { + super(context, attrs); + initialize(mapView, context); + } + + public UserLocationView(MapView mapView, Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initialize(mapView, context); + } + + private void initialize(MapView mapView, Context context) { + mMapView = new WeakReference(mapView); + + // View configuration + setEnabled(false); + + // Layout params + ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + setLayoutParams(lp); + + // Setup location services + mLocationClient = new LostApiClient.Builder(getContext()).build(); + mLocationRequest = LocationRequest.create() + .setFastestInterval(1000) + .setSmallestDisplacement(3.0f) + .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); + + // Setup the custom paint + Resources resources = context.getResources(); + mDensity = resources.getDisplayMetrics().density; + mMarkerScreenPoint = new PointF(); + mMarkerScreenMatrix = new Matrix(); + + mOuterRingPaint = new Paint(); + mOuterRingPaint.setAntiAlias(true); + mOuterRingPaint.setStyle(Paint.Style.FILL); + mOuterRingPaint.setColor(Color.BLACK); + mOuterRingPaint.setAlpha((int) (255 * 0.5f)); + + mOuterRingPath = new Path(); + mOuterRingPath.addCircle(0.0f, 0.0f, 15.0f * mDensity, Path.Direction.CW); + + mOuterRingBounds = new RectF(); + mOuterRingPath.computeBounds(mOuterRingBounds, false); + + mInnerRingPaintFill = new Paint(); + mInnerRingPaintFill.setAntiAlias(true); + mInnerRingPaintFill.setStyle(Paint.Style.FILL); + mInnerRingPaintFill.setColor(Color.WHITE); + + mInnerRingPaintStroke = new Paint(); + mInnerRingPaintStroke.setAntiAlias(true); + mInnerRingPaintStroke.setStyle(Paint.Style.STROKE); + mInnerRingPaintStroke.setStrokeWidth(1.0f * mDensity); + mInnerRingPaintStroke.setColor(Color.BLACK); + + mInnerRingPath = new Path(); + mInnerRingPath.addCircle(0.0f, 0.0f, 7.5f * mDensity, Path.Direction.CW); + + mDirectionArrowPaint = mInnerRingPaintFill; + + mDirectionArrowPath = new Path(); + mDirectionArrowPath.moveTo(0.0f, -15.0f * mDensity); + mDirectionArrowPath.arcTo( + new RectF( + -9.0f * mDensity, -9.0f * mDensity, + 9.0f * mDensity, 9.0f * mDensity), + 225.0f, 90.0f, false); + mDirectionArrowPath.close(); + + mAccuracyPaintFill = new Paint(); + mAccuracyPaintFill.setAntiAlias(true); + mAccuracyPaintFill.setStyle(Paint.Style.FILL); + mAccuracyPaintFill.setColor(Color.BLACK); + mAccuracyPaintFill.setAlpha((int) (255 * 0.25f)); + + mAccuracyPaintStroke = new Paint(); + mAccuracyPaintStroke.setAntiAlias(true); + mAccuracyPaintStroke.setStyle(Paint.Style.STROKE); + mAccuracyPaintStroke.setStrokeWidth(0.5f * mDensity); + mAccuracyPaintStroke.setColor(Color.BLACK); + mAccuracyPaintStroke.setAlpha((int) (255 * 0.5f)); + + mAccuracyPath = new Path(); + mAccuracyBounds = new RectF(); + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (!mShowMarker) { + return; + } + + canvas.concat(mMarkerScreenMatrix); + + if (!canvas.quickReject(mOuterRingPath, Canvas.EdgeType.AA)) { + if (mShowAccuracy) { + canvas.drawPath(mAccuracyPath, mAccuracyPaintFill); + canvas.drawPath(mAccuracyPath, mAccuracyPaintStroke); + } + canvas.drawPath(mOuterRingPath, mOuterRingPaint); + canvas.drawPath(mInnerRingPath, mInnerRingPaintFill); + canvas.drawPath(mInnerRingPath, mInnerRingPaintStroke); + if (mShowDirection) { + canvas.drawPath(mDirectionArrowPath, mDirectionArrowPaint); + } + } + } + + @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); + + // compute new marker position + // TODO add JNI method that takes existing pointf + mMarkerScreenPoint = mMapView.get().toScreenLocation(mMarkerCoordinate); + mMarkerScreenMatrix.reset(); + mMarkerScreenMatrix.setTranslate( + mMarkerScreenPoint.x, + mMarkerScreenPoint.y); + + // rotate so arrow in points to bearing + if (mShowDirection) { + mMarkerScreenMatrix.preRotate(mMarkerDirection + + (float) mMapView.get().getDirection()); + } + + // adjust accuracy circle + if (mShowAccuracy) { + mAccuracyPath.reset(); + mAccuracyPath.addCircle(0.0f, 0.0f, + (float) (mMarkerAccuracy / mMapView.get().getMetersPerPixelAtLatitude( + mMarkerCoordinate.getLatitude())), + Path.Direction.CW); + + mAccuracyPath.computeBounds(mAccuracyBounds, false); + } + + // invalidate changed pixels + if (mDirtyRect == null) { + mDirtyRect = new Rect(); + mDirtyRectF = new RectF(); + } else { + // the old marker location + // TODO need to take into account accuracy stroke + //invalidate(mDirtyRect); + invalidate(); + } + + RectF largerBounds; + if (mAccuracyBounds.contains(mOuterRingBounds)) { + largerBounds = mAccuracyBounds; + } else { + largerBounds = mOuterRingBounds; + } + mMarkerScreenMatrix.mapRect(mDirtyRectF, largerBounds); + mDirtyRectF.roundOut(mDirtyRect); + // TODO need to take into account accuracy stroke + //invalidate(mDirtyRect); // the new marker location + invalidate(); + } 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) { + if (mLocationClient == null) { + return; + } + + if (enableGps) { + if (!mLocationClient.isConnected()) { + mUserLocation = null; + mLocationClient.connect(); + setLocation(LocationServices.FusedLocationApi.getLastLocation()); + mLocationListener = new MyLocationListener(); + LocationServices.FusedLocationApi.requestLocationUpdates(mLocationRequest, mLocationListener); + } + } else { + if (mLocationClient.isConnected()) { + LocationServices.FusedLocationApi.removeLocationUpdates(mLocationListener); + mLocationListener = null; + mLocationClient.disconnect(); + mUserLocation = null; + } + } + } + + private class MyLocationListener implements LocationListener { + @Override + public void onLocationChanged(Location location) { + if (mPaused) { + return; + } + setLocation(location); + } + } + + // 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; + mMarkerCoordinate = 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); + } + + mMarkerCoordinateAnimator = ValueAnimator.ofFloat(0.0f, 1.0f); + mMarkerCoordinateAnimator.setDuration(1000); + mMarkerCoordinateAnimator.addUpdateListener(new MarkerCoordinateAnimatorListener( + previousCoordinate, new LatLng(location) + )); + mMarkerCoordinateAnimator.start(); + + mShowDirection = location.hasBearing(); + if (mShowDirection) { + if (mUserLocation != null && mUserLocation.hasBearing()) { + mMarkerDirection = mUserLocation.getBearing(); + } + float oldDir = mMarkerDirection; + 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(); + } + + 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); + } + } + + void updateOnNextFrame() { + mMapView.get().update(); + } + + public void pause() { + mPaused = true; + toggleGps(false); + } + + public void resume() { + mPaused = false; + if (isEnabled()) { + toggleGps(true); + } + } + + public void setOnMyLocationChangeListener(@Nullable MapView.OnMyLocationChangeListener listener) { + mOnMyLocationChangeListener = listener; + } + + // public for animator only + public float getDirection() { + return mMarkerDirection; + } + + // public for animator only + public void setDirection(float direction) { + mMarkerDirection = 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; + } + } +} diff --git a/android/java/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/location_marker.png b/android/java/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/location_marker.png deleted file mode 100644 index f3a57845b740230b0011f4ace9e13748b132533f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4788 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 diff --git a/android/java/MapboxGLAndroidSDK/src/main/res/drawable-ldpi/location_marker.png b/android/java/MapboxGLAndroidSDK/src/main/res/drawable-ldpi/location_marker.png deleted file mode 100644 index 94a78378178fb95943d0408fcf4028c1b686b1a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4046 zcmZuz2UJtb)(#M)Nt0fbfFbk*X$lg03!_Y&RTP3X3zKSy}vzsopmBLAE;82vyuY<07`_KlJ><+e>q66 zT)bl=uTug5p}`eZIy8;@)HRAp;HF zRbv2ZMIxSP5{899yp)Jl=xO;LTI#+hPz~p@P^PDN8jKz%`$41G)uyGSyfhu-K(<{b z{&ObdtQ$2uxl%ViX|wG=-U!e<4F{k*j` zQ1wuWaO4yRPfssW>#E-W4t-+TG$q)o32`4g4@}^?tq8sXAXgrhkPd(_EfFT`RDWO* zIS2OcrSOS_-4bEUCChumwa{7Nxc_=tpy^{;Dr7O26*a)pES4S9q&btpJ)a)~%r#2m zp0mmKBeBBiA~FC=2fV|>hc7e$!u08;{VR}M$4C)ndY12zKL`P&$(@qTZdB1nlD63Z z??Ri!C}1Q4T}V5gX1BE3V|~0^t#gFFC6L^4e?G!n1HV^u^_ei!lM3hO$^_gfr*zwd ztupqlE!5bu?NRIG?2Q1OGkA~BiQX<`6Q3;~lZ~2n+nj`q&}(BIiBRZs@9>)@m|YNTh93 zsC1BiJk*jrBye;-kXeF2Zbw6mJrNF|s~{{u_ogx5#^1 znr+JxD%|5le15MSb5IyXVdgdDxpbEIuf|!rapwuL;7C-HbmE*1n8CKNQI zWPc*+LNo&cggjBBA$m>(RU@>M1HP4)nYd`dTN4CcPwia*BSAi&fe(TuT&@@aAHAn^ zp}6`)RgOHY?Vd2fOpqOE@D2?_o_t9>V*z!#!Wa#k0qJ8!dCfcYL?09*<7pD*jy^+* znE9WUMzi0Wz?h;VXY$SpaF9+69kH<7U+3290hwOSc z^Uu(42UEAX@1XrSTgU_118>a6adfrv#FH5S9fGMjpM$`TU|kwk(iPIP5ZZPzt)L>L z2Ui%)<4z+qU)8e-`DO$=jurNcH7LCXc6LQ%#{cJf@t1>c0z5>j|~m_y;ZS=ER`Hx@*W{xwXn6?pK_w3d64+57pP)uaWv<^+ou)>g|G);iR<> zdmO8{Ju)k9E5jw;CH5uOrRrYoEA7H&hoagnuVRv7nquO9iY+(b7w}UO6?P`)&3q|G z{1|?}6s{EO6oQnv5)D1)QToxW5~mX6h_F4x-rRoD{$vzd8jF98$7bWROvTO@Ps7(M z8zX({kL`bTti#unSTk5#SXo&0SX0Hi#91;h86Pv2GFHU8YitcDYJmnz1}n8D!YS$B z`*p3ktD_}z%gVQ23k}K?OqasTEQ+f2ZSo&f56~GWLJe=r*BRC6U=@(}VEQoA3a#y2 z8LgbMoPt)>R>Q#F_n$#u%%bHp+lq3da$C)_&GY9iZk8mJ4)W(4)G8Bpq9`WQ*W2t5xuFD1%r*_{`=#xD_)&WozoNW z$+P+s0qT3y398ZE$*+@ESn(7l5vdiA5*}65B_5!+?YCdcNXbOX#LCdMc(s)J!w${Q z2+zEZr+<#`tQ?P@Ac1#)Z^%~33xF!Xha@r-B4pQyJ%Jr<1Z_>h9Izo%u+a!3p~9)6 zSEw|Z_*G+uV|Qk#px7J8=Hq%j?*MNsPzbH8q}bV{7S}V>qZ%E{c#jdQ@=^7J@~G;4 zWnbk~<$J06f~e|6l^BB%YHjeAFH0P0Y?ZrzP<$Ibc zr*WhOq8!11SRh2o*UovqHT@B^SezW6oX;m(JXrk6E~MtLW>|sSJgQQ_SQO5KgLR1I z3oADE+KqnKqsx;UOwizUM_D(eJ^QWkODzVR$^8^XQEmTr#zxgDr&p_=c6o0xV-eGL z{o~J%CjsKt;#g0c7O&r7r)}cj9jMAe$I6YS8XjPc+ZyM8wQR&Wft=JPcluQNVy)O_ zF;O!kS}(OKTg+^$x0kyV8WgnVaY6%zRE{4U863B!t*guIr&76027jUZzD0dQa(_+S zm__+NI-2g^581-K+JAL|o%15aew|)x{HdRgMwv{0E}t_lTk8Lj_oJ@q(Pqm-b3<1% zYP&egxEhh7w>b3*d)3j`>jmpUtZ^C3xOLyUt*kHIQ{%0(PJzP&u|yw8WNy&2HwX3{ zXgZY1JAE+!I{&5vzmd1z*lH7K8mP3Twwr)g4OM;B-5%#TdUs^}cG2x*iT2pUuBrvs zZs)VPUy8nTrE8j^`3;A_JDifc-?|SuwzIEFPd<+FFInR`%h`YT*&xawckJs}W+;lU*Zmo9P3Ag4_ zefE5z>{$!3|H`rP32bJ+(SPaLc@`T{^p)f9&=%iwldteK|CzHvl45ET*?ylpXSQb+ zn+th7ESeg@nJ;B+aKX&do3~r>$s?QX)NFI;vJFQr*E)HYu zwhLcJ&6Kvt?gyRao>=U4C#-EE50NQ3v9ee{veO^l5GU*9*@KXuCn8d)fQx@hG8Z+J zI{?7!aXAREK_BJ-0796ZuD*xh0umfd&Aiy`?UePF5b~Aa5r} zjJuRKoa+yS)W!PJ4C4a*0r7BvbLrpL1SvYZS%Ji%Vo+f&8FCN^B<*HtEv2ob@)!ML z2j{Z&@NkiW!LV2?6e|jKcC&%ql9ZH$35&o)M1(F7Lhg?-9_HRc826ihI{CLBB`bFe zH#-*(J7)~&(yuw%+0z5g#dR6zKhIxzI=TEO5XSv4RTuhT-sUc_TTo%x{~~$VS^qb^ z|HHaG`IGjCsy~BCUzA2l(ap--!`V&O+1XL%Qjs0T!QH~##p+U*H0+;<|7I$E=~4=% zZRPIl=y|C~=9ajmH0=Lb|H(kQ*;!pw=TF98_`lo!iP!(15&mxdiy;lWEa~4${OA4s zVZFFO8S)D?|852u@&{SSvy0X_L@3GY-l5G(!Q0bm(RZy3XrKiZG+AT76m-`}f6GVt zYKds4S!y@aE|W!c^-PACuiB0z8nO`r4mmv&zA@Qi$g&nQl)~s9 zh4iBY84Gp!)9q_nnWB}ue@bIT(wS+^!jg65lnJud=0r`S#oY>NVdV{A2Qk)pGvNr8 zeA?Uc%R>!AkGbZe4zI~U=1KiZiiwHqHP{_3YYiyh8kLTgIAbu+t=xTZGqzalf!e$1 zd6#;1A2Jf77VV+WXcTjKy3+HhnVEn|OkGZD-;!X+Cu{e2RaLI_wY8q0oahuaC;7P_ z$<*Yoh7o)KNs3^Qu4SB5)__2d2Z-&3pUn0n{*)8-w))xaoZjX8uXXrC(>cadVE6B# zi<7HcS_Zx@WC&Q^VT%~#=G)n^qL$zc?qdXe@~q-Cel%mY#sAg)NS=L|YGHTcAqGht z-*36%ad;$kRVpgqGs1aDVM2&0#ivMZqahH(XgUv8pjMrJSOEimoB&jeK3zW_;cA)K`kh&XDSC0y)r%&}1u8MCxR({`{;?rKX)CQ`dC&Ap zK9_pPMbue2d&E@!i+p|UPoJ1sQ>b?PTWp6DjY_ybKD0~X6LH*-F+?Pb?c>*(BIGpZc0RiDkhE*u~_wK3y_(eKFH+4VSfQ8#^GK}x* z>zqmxUV6pJ`W?*d3NV>JavQ&@3As7F$sA|rDfC>DCuc0owTbq0JU!tVqkITdnpV<^ zg+Ex%gKiWI2n*I_qkBvT)uqL00AFjWeLgyZGM{O@A2@pe`a=A=fY(DWd7Lx{-fCp= zU}6qNYA{AL=}Xg$hTs@NSJ}=!*DQ+X%srK+HNeJa#&8d=X?U4~t_>a)&z7jM({0zwEj&It&XP?q@ zHGUb_tKw8+t!(BR=UW9xhGUzP7&P89&~@ve8Ti(;wc^o&H0&2#5+J#>g^2Wc)?B{o z7~O6rU6yO=x=obLgYOD$MnZLoA&Hfz4GJ~p%6o+JF7Rsg=H2V5s<>96JZwKKeGaLZyVsjE z*R~-rOW6R~3)$bE5}o3o;+-OYym7Kw+G<(Yv^a+Pg6fk=&A`eZPX3CV zFRzL6tJ-z`*)p#>pTwKaThGhGYr>l%|4xA?-7Wn?`c(Rie0znH8Pgk}*_7GL8%t?Y z+VUr32O)B_Qg&(C(hI3>wcL?n)l%C6GTJfcI=SqsCS>)CwxI%e0fQe6d1p`}6Xz7)*)Iz%!*5mG}2^?mgL& zU{Z}nM`v)3aAGBwByUPBND5%YFelLXiuv;0mf_ZP7Yo~f_6X+*TXJXq$n=}iLa8zD zhNXZj=@#W9oVC|$shg~ukUil&8G6-7-$)AU)@OpZXl+cvGP*taMbZo}mC5p9%F9~`w_a8zZrQ9jue?xGR*O=LQ9D!bU0)mk z+qT}P-S^%dSsz@T*&W<70A2=`fMywUfjU4_dNn3l(CHI*fh|omO|_u{upX?Sc^@aO z)-SksxC%&t+2ZW37dKQ=zC?KOUX_V&psx>5$_AkgZ>`mhd*AaO86Cr^&*`J{0ojJ= zM_xntBT^9hDQHOyc~XaHMg-@patiRB^%wN^EZpiD(T|*1{LVPB43#r~OkN!Jd^1(p zK`o2NHev3FpXU=eH^z1tb*8y+>T1~rw%TV3IG9u$xtBQePGTlz&0^dBR{}64>~&bT zc1o*7lr5?Zbrv;2YoPh&+uhgLA0d;4$??fKVseGug@53}Dz++fdVpafJHH-vMA~P4H2TCoy)l_S z>DF=X!}^E4Km`W{pSzCr-UkuCniQ5@SWCkP%FMr1U-z+Ssu}-TzYvE9<8_BtJ9Ii? z?D@vr9*y=HJu@n=w{jw{Ouy5r)-oEWNOj(3b!~G!>$)`JKrVIuk^-^p{%O z_3b-Wr*Lt0aTT%!uPJ&joss=7=5yymc;nKiDf9l7OBuuMUj`e-@Ik#aF?4P8YUerl z>w?~I8M>5e;@hmhj+eAh7oN}8G&lxfgS3})*Al46aAaP4bKKqjD}95C1&Ya+nqv~* zy_&ew?!N!+C*1!`@tmPtPW3kM@&%=}@9oTT{Noc~q-vAN*hjB}mmq|~1r-rpc@rlpfVI_gssI?q0RqLk$m_(SVm_*!%nSETYS zf?7?*WKTv}dD&=3NXkpgwQA48FO!e}6#S)^XN-@iTRP)ONWd^TuBB|mhGi>QLb4oN#N4O&i63#k%16*}|C zQY4kQLmcsYwb8cPK}_kL2yYNOc)LaFF3t+{*`qkxd}v*D>0U@v*BdRUSK1xe#Lw8x zPWJlXR`Opw8ZE9@-wfH$-m~3kPncUY*ft<##i;w-2mSitD~xxT9@`3A-;-6w1RniU zf;@CFUH||$;ds#agtUDF0BB)2V>AJM?W(e^J05Cn=Wb&U^~HM}u>k-TU*)4I-kx9$ z_Qkupc`5sZj!`(gY!3t1$s5C^45ex>ac-lEA-_X|i zOMdjG3UMM3Jd|NDA0Hp6j~vw9(-9`4q@)CsmW9d6N*ytzyzaRXtbL{2ye|F@^6xm> z_FlH0I1d8O-3@#k*V@MYE#sWT9{)+?=Jl7WBY!YoYY&(VR2ufbL|1-ni(Z3Wbu;Z5gt;N6h z?@#n-1JxLh-2D46s4-s8FxUqGfC!Yfrm-#yE()s+VBx=%bNrSD_6Ykue4zc zO8dNX*OVL`I_u=Trk}ce;k_Q)x}>4NPPV~Of7Y)D1nIm+-7&u}rdElso%LIejOgFa zy=%gJcV$I)!v#rR0LfyXa`W)))Q`B0_5OOyO`n+xwi+nsE0KtusKdW8Yrt8Yi42rq zGE_w865DJrct1!|Zk5={FhKd-=mW`QI%RRF*n2E*%)5|j!%40O=M%@_)b&ig%fe;R zaL0X(MJ9WqoxT;w9>GH8lG2TlBDkJL#Ad{hpgB^>}e}GfV>(Y zXIU*Tsp+PShV`ZN1KVbqJyit*9T0~}Vdwy>mFQ{Od&2nyZ3jsAXM@e{?JAP4prq1k zLYd+)F6Zk`tn8lY#FbLMe4`h_(2cNh_bDU@(yVcc7E8E+?+PlfAtW#wf(fZ%( zHl*e(PAo~BiGt8(gcT*K`w&cVQdLN8b{gC$i#B+x^6lI60_V={dw6?qK1@*Xq`Ah{ z75`diD8yExO@MSjhyRY46&vsCOU8;9#)7zyE1i?FYLa ziIfgi8vW}0lg_H=AXJ%$(u8KWy1qgbi+1-ZGvji&Wr= zYsaxVq5DjqWwmAoz!?tjG@k5aw58w&78fOZ*N3OVGCv8D0jhT6^&0pP7#9d*UIhEiG{3 z4?aFVdP2rD|5-cXlHY}c%sdCmHaSL&5};pS1L>|ccQo!-S5=9+ySWXJhZa*UV~r@H z^~+`f{{DM$KY#wTSW=C*pr9qc8+Xun62`GPJ7bn?9|!x|GP^L6vRugQ?%}$7B)zuo zaJ-`|j~=GLv>4mR98OTS=q|QS=#GD(m!BVI>$f>t;I{&A2Y~_;6CI;FnWoY=h`^Gf!oOLqE?ume_6 zuyl~0?mX%Bp~?EgnkFI~VA69Y#>cLW=X-FEtky{51%T)MwLDlc#_}Z3g}B8WP)~6D q_@w&&7d=iuZ diff --git a/android/java/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/location_marker.png b/android/java/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/location_marker.png deleted file mode 100644 index 157c48adeaa82f9f93cc2cc6604bffaab587543e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5720 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 a^A0095FSw&TQ~pbxUM9pCR-(K67oL;griyj diff --git a/android/java/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/location_marker.png b/android/java/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/location_marker.png deleted file mode 100644 index ccc56f6601908a218659359e3a43097987c06442..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7175 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 Xa$)_SQwe^=zCm8EJVOhW$$gwmIe diff --git a/android/java/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/location_marker.png b/android/java/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/location_marker.png deleted file mode 100644 index 2318f1b7cc098173eda4e2ddde96711e4fec8b49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8809 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 diff --git a/android/java/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowAdapterActivity.java b/android/java/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowAdapterActivity.java index a5e08f6cd..62292811d 100644 --- a/android/java/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowAdapterActivity.java +++ b/android/java/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowAdapterActivity.java @@ -2,6 +2,7 @@ package com.mapbox.mapboxsdk.testapp; import android.graphics.Color; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; @@ -42,7 +43,7 @@ public class InfoWindowAdapterActivity extends AppCompatActivity { private int tenDp = (int) getResources().getDimension(R.dimen.attr_margin); @Override - public View getInfoWindow(Marker marker) { + public View getInfoWindow(@NonNull Marker marker) { TextView textView = new TextView(InfoWindowAdapterActivity.this); textView.setText(marker.getTitle()); textView.setTextColor(Color.WHITE); diff --git a/android/java/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java b/android/java/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java index 019d4c763..361e6fd44 100644 --- a/android/java/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java +++ b/android/java/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java @@ -7,6 +7,8 @@ import android.graphics.Color; import android.location.Location; import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.NavigationView; import android.support.design.widget.Snackbar; @@ -63,6 +65,7 @@ public class MainActivity extends AppCompatActivity { private TextView mFpsTextView; private int mSelectedStyle = R.id.actionStyleMapboxStreets; private NavigationView mNavigationView; + private CoordinatorLayout mCoordinatorLayout; // Used for GPS private FloatingActionButton mLocationFAB; @@ -102,6 +105,8 @@ public class MainActivity extends AppCompatActivity { setupDrawerContent(mNavigationView); } + mCoordinatorLayout = (CoordinatorLayout) findViewById(R.id.coordinator_layout); + mMapView = (MapView) findViewById(R.id.mainMapView); mMapView.setAccessToken(ApiAccess.getToken(this)); mMapView.onCreate(savedInstanceState); @@ -110,7 +115,7 @@ public class MainActivity extends AppCompatActivity { mMapView.setOnMapLongClickListener(new MapView.OnMapLongClickListener() { @Override - public void onMapLongClick(LatLng point) { + public void onMapLongClick(@NonNull LatLng point) { mMapView.addMarker(new MarkerOptions() .position(point) .title("Dropped Pin") @@ -122,21 +127,41 @@ public class MainActivity extends AppCompatActivity { mMapView.setOnMapClickListener(new MapView.OnMapClickListener() { @Override - public void onMapClick(LatLng point) { + public void onMapClick(@NonNull LatLng point) { String location = latLngFormatter.format(point.getLatitude()) + ", " + latLngFormatter.format(point.getLongitude()); - Snackbar.make(findViewById(android.R.id.content), "Map Click Listener " + location, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(mCoordinatorLayout, "Map Click Listener " + location, Snackbar.LENGTH_SHORT).show(); } }); mMapView.setOnMarkerClickListener(new MapView.OnMarkerClickListener() { @Override - public boolean onMarkerClick(Marker marker) { - Snackbar.make(findViewById(android.R.id.content), "Custom Marker Click Listener", Snackbar.LENGTH_SHORT).show(); + public boolean onMarkerClick(@NonNull Marker marker) { + Snackbar.make(mCoordinatorLayout, "Custom Marker Click Listener", Snackbar.LENGTH_SHORT).show(); return false; } }); + mMapView.setOnMyLocationChangeListener(new MapView.OnMyLocationChangeListener() { + @Override + public void onMyLocationChange(@Nullable Location location) { + String desc = "Loc Chg: "; + boolean noInfo = true; + if (location.hasSpeed()) { + desc += String.format("Spd = %.1f km/h ", location.getSpeed() * 3.6f); + noInfo = false; + } + if (location.hasAltitude()) { + desc += String.format("Alt = %.0f m ", location.getAltitude()); + noInfo = false; + } + if (noInfo) { + desc += "No extra info"; + } + Snackbar.make(mCoordinatorLayout, desc, Snackbar.LENGTH_SHORT).show(); + } + }); + mFpsTextView = (TextView) findViewById(R.id.view_fps); mFpsTextView.setText(""); @@ -149,7 +174,7 @@ public class MainActivity extends AppCompatActivity { if (mMapView.isMyLocationEnabled()) { Location location = mMapView.getMyLocation(); if (location != null) { - mMapView.setZoomLevel(8); + mMapView.setZoomLevel(18); mMapView.setCenterCoordinate(new LatLng(location)); } } diff --git a/android/java/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_main.xml b/android/java/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_main.xml index d62d6423e..b97fb4fca 100644 --- a/android/java/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_main.xml +++ b/android/java/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_main.xml @@ -27,33 +27,35 @@ android:layout_below="@+id/toolbar"> - + - + - + + diff --git a/include/mbgl/android/native_map_view.hpp b/include/mbgl/android/native_map_view.hpp index 0908f7563..7b5473d48 100644 --- a/include/mbgl/android/native_map_view.hpp +++ b/include/mbgl/android/native_map_view.hpp @@ -50,8 +50,6 @@ public: void enableFps(bool enable); void updateFps(); - void renderSync(); - void resizeView(int width, int height); void resizeFramebuffer(int width, int height); @@ -89,6 +87,9 @@ private: int availableProcessors = 0; size_t totalMemory = 0; + jboolean renderDetach = false; + JNIEnv *renderEnv = nullptr; + // Ensure these are initialised last std::shared_ptr fileCache; std::unique_ptr fileSource;