diff options
author | Leith Bade <leith@mapbox.com> | 2015-10-21 23:07:43 +1100 |
---|---|---|
committer | Leith Bade <leith@mapbox.com> | 2015-10-22 16:18:13 +1100 |
commit | 2daa5f14a8902ef0fe623558097f18644a2a8bf6 (patch) | |
tree | 6ba2908b03843219a742e30dc0db1074f876f847 /android | |
parent | e5289d685fde4255414e5bf784f8ffb24008f681 (diff) | |
download | qtlocation-mapboxgl-2daa5f14a8902ef0fe623558097f18644a2a8bf6.tar.gz |
[android] Fix bug in setSprite when calculating pixel data size
Change Marker sprite() to icon() and use Sprite class
Load a Maki dog icon in test app
Fixes #2506
Diffstat (limited to 'android')
10 files changed, 350 insertions, 117 deletions
diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp index 7397c6f5df..bf16a3dbfb 100644 --- a/android/cpp/jni.cpp +++ b/android/cpp/jni.cpp @@ -57,13 +57,14 @@ jfieldID bboxLatSouthId = nullptr; jfieldID bboxLonEastId = nullptr; jfieldID bboxLonWestId = nullptr; +jclass spriteClass = nullptr; +jfieldID spriteIdId = nullptr; + jclass markerClass = nullptr; -jmethodID markerConstructorId = nullptr; jfieldID markerPositionId = nullptr; -jfieldID markerSpriteId = nullptr; +jfieldID markerIconId = nullptr; jclass polylineClass = nullptr; -jmethodID polylineConstructorId = nullptr; jfieldID polylineAlphaId = nullptr; jfieldID polylineVisibleId = nullptr; jfieldID polylineColorId = nullptr; @@ -71,7 +72,6 @@ jfieldID polylineWidthId = nullptr; jfieldID polylinePointsId = nullptr; jclass polygonClass = nullptr; -jmethodID polygonConstructorId = nullptr; jfieldID polygonAlphaId = nullptr; jfieldID polygonVisibleId = nullptr; jfieldID polygonFillColorId = nullptr; @@ -809,8 +809,14 @@ jlong JNICALL nativeAddMarker(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, return -1; } - jstring jsprite = reinterpret_cast<jstring>(env->GetObjectField(marker, markerSpriteId)); - std::string sprite = std_string_from_jstring(env, jsprite); + jobject icon = env->GetObjectField(marker, markerIconId); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + return -1; + } + + jstring jid = reinterpret_cast<jstring>(env->GetObjectField(icon, spriteIdId)); + std::string id = std_string_from_jstring(env, jid); jdouble latitude = env->GetDoubleField(position, latLngLatitudeId); if (env->ExceptionCheck()) { @@ -825,7 +831,7 @@ jlong JNICALL nativeAddMarker(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, } // Because Java only has int, not unsigned int, we need to bump the annotation id up to a long. - return nativeMapView->getMap().addPointAnnotation(mbgl::PointAnnotation(mbgl::LatLng(latitude, longitude), sprite)); + return nativeMapView->getMap().addPointAnnotation(mbgl::PointAnnotation(mbgl::LatLng(latitude, longitude), id)); } jlongArray JNICALL nativeAddMarkers(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject jlist) { @@ -867,8 +873,14 @@ jlongArray JNICALL nativeAddMarkers(JNIEnv *env, jobject obj, jlong nativeMapVie return nullptr; } - jstring jsprite = reinterpret_cast<jstring>(env->GetObjectField(marker, markerSpriteId)); - std::string sprite = std_string_from_jstring(env, jsprite); + jobject icon = env->GetObjectField(marker, markerIconId); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + return nullptr; + } + + jstring jid = reinterpret_cast<jstring>(env->GetObjectField(icon, spriteIdId)); + std::string id = std_string_from_jstring(env, jid); jdouble latitude = env->GetDoubleField(position, latLngLatitudeId); if (env->ExceptionCheck()) { @@ -882,7 +894,7 @@ jlongArray JNICALL nativeAddMarkers(JNIEnv *env, jobject obj, jlong nativeMapVie return nullptr; } - markers.emplace_back(mbgl::PointAnnotation(mbgl::LatLng(latitude, longitude), sprite)); + markers.emplace_back(mbgl::PointAnnotation(mbgl::LatLng(latitude, longitude), id)); /* Do I need to delete other LocalRefs? */ env->DeleteLocalRef(marker); @@ -1110,7 +1122,8 @@ void JNICALL nativeSetSprite(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, const std::string symbolName = std_string_from_jstring(env, symbol); jbyte* pixelData = env->GetByteArrayElements(jpixels, nullptr); - std::string pixels(reinterpret_cast<char*>(pixelData), width * height * 4); + jsize size = env->GetArrayLength(jpixels); + std::string pixels(reinterpret_cast<char*>(pixelData), size); env->ReleaseByteArrayElements(jpixels, pixelData, JNI_ABORT); auto spriteImage = std::make_shared<mbgl::SpriteImage>( @@ -1421,38 +1434,38 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { return JNI_ERR; } - markerClass = env->FindClass("com/mapbox/mapboxsdk/annotations/Marker"); - if (markerClass == nullptr) { + spriteClass = env->FindClass("com/mapbox/mapboxsdk/annotations/Sprite"); + if (spriteClass == nullptr) { env->ExceptionDescribe(); return JNI_ERR; } - markerConstructorId = env->GetMethodID(markerClass, "<init>", "()V"); - if (markerConstructorId == nullptr) { + spriteIdId = env->GetFieldID(spriteClass, "mId", "Ljava/lang/String;"); + if (spriteIdId == nullptr) { env->ExceptionDescribe(); return JNI_ERR; } - markerPositionId = env->GetFieldID(markerClass, "position", "Lcom/mapbox/mapboxsdk/geometry/LatLng;"); - if (markerPositionId == nullptr) { + markerClass = env->FindClass("com/mapbox/mapboxsdk/annotations/Marker"); + if (markerClass == nullptr) { env->ExceptionDescribe(); return JNI_ERR; } - markerSpriteId = env->GetFieldID(markerClass, "sprite", "Ljava/lang/String;"); - if (markerSpriteId == nullptr) { + markerPositionId = env->GetFieldID(markerClass, "position", "Lcom/mapbox/mapboxsdk/geometry/LatLng;"); + if (markerPositionId == nullptr) { env->ExceptionDescribe(); return JNI_ERR; } - polylineClass = env->FindClass("com/mapbox/mapboxsdk/annotations/Polyline"); - if (polylineClass == nullptr) { + markerIconId = env->GetFieldID(markerClass, "icon", "Lcom/mapbox/mapboxsdk/annotations/Sprite;"); + if (markerIconId == nullptr) { env->ExceptionDescribe(); return JNI_ERR; } - polylineConstructorId = env->GetMethodID(polylineClass, "<init>", "()V"); - if (polylineConstructorId == nullptr) { + polylineClass = env->FindClass("com/mapbox/mapboxsdk/annotations/Polyline"); + if (polylineClass == nullptr) { env->ExceptionDescribe(); return JNI_ERR; } @@ -1493,12 +1506,6 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { return JNI_ERR; } - polygonConstructorId = env->GetMethodID(polygonClass, "<init>", "()V"); - if (polygonConstructorId == nullptr) { - env->ExceptionDescribe(); - return JNI_ERR; - } - polygonAlphaId = env->GetFieldID(polygonClass, "alpha", "F"); if (polygonAlphaId == nullptr) { env->ExceptionDescribe(); @@ -1828,12 +1835,22 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { return JNI_ERR; } + spriteClass = reinterpret_cast<jclass>(env->NewGlobalRef(spriteClass)); + if (spriteClass == nullptr) { + env->ExceptionDescribe(); + env->DeleteGlobalRef(latLngClass); + env->DeleteGlobalRef(latLngZoomClass); + env->DeleteGlobalRef(bboxClass); + return JNI_ERR; + } + markerClass = reinterpret_cast<jclass>(env->NewGlobalRef(markerClass)); if (markerClass == nullptr) { env->ExceptionDescribe(); env->DeleteGlobalRef(latLngClass); env->DeleteGlobalRef(latLngZoomClass); env->DeleteGlobalRef(bboxClass); + env->DeleteGlobalRef(spriteClass); return JNI_ERR; } @@ -1843,6 +1860,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { env->DeleteGlobalRef(latLngClass); env->DeleteGlobalRef(latLngZoomClass); env->DeleteGlobalRef(bboxClass); + env->DeleteGlobalRef(spriteClass); env->DeleteGlobalRef(markerClass); return JNI_ERR; } @@ -1853,6 +1871,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { env->DeleteGlobalRef(latLngClass); env->DeleteGlobalRef(latLngZoomClass); env->DeleteGlobalRef(bboxClass); + env->DeleteGlobalRef(spriteClass); env->DeleteGlobalRef(markerClass); env->DeleteGlobalRef(polylineClass); return JNI_ERR; @@ -1864,6 +1883,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { env->DeleteGlobalRef(latLngClass); env->DeleteGlobalRef(latLngZoomClass); env->DeleteGlobalRef(bboxClass); + env->DeleteGlobalRef(spriteClass); env->DeleteGlobalRef(markerClass); env->DeleteGlobalRef(polylineClass); env->DeleteGlobalRef(polygonClass); @@ -1877,6 +1897,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { env->DeleteGlobalRef(latLngClass); env->DeleteGlobalRef(latLngZoomClass); env->DeleteGlobalRef(bboxClass); + env->DeleteGlobalRef(spriteClass); env->DeleteGlobalRef(markerClass); env->DeleteGlobalRef(polylineClass); env->DeleteGlobalRef(polygonClass); @@ -1890,6 +1911,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { env->DeleteGlobalRef(latLngClass); env->DeleteGlobalRef(latLngZoomClass); env->DeleteGlobalRef(bboxClass); + env->DeleteGlobalRef(spriteClass); env->DeleteGlobalRef(markerClass); env->DeleteGlobalRef(polylineClass); env->DeleteGlobalRef(polygonClass); @@ -1904,6 +1926,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { env->DeleteGlobalRef(latLngClass); env->DeleteGlobalRef(latLngZoomClass); env->DeleteGlobalRef(bboxClass); + env->DeleteGlobalRef(spriteClass); env->DeleteGlobalRef(markerClass); env->DeleteGlobalRef(polylineClass); env->DeleteGlobalRef(polygonClass); @@ -1917,9 +1940,10 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { if (pointFClass == nullptr) { env->ExceptionDescribe(); env->DeleteGlobalRef(latLngClass); - env->DeleteGlobalRef(markerClass); env->DeleteGlobalRef(latLngZoomClass); env->DeleteGlobalRef(bboxClass); + env->DeleteGlobalRef(markerClass); + env->DeleteGlobalRef(spriteClass); env->DeleteGlobalRef(polylineClass); env->DeleteGlobalRef(polygonClass); env->DeleteGlobalRef(runtimeExceptionClass); @@ -1933,9 +1957,10 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { if (httpContextClass == nullptr) { env->ExceptionDescribe(); env->DeleteGlobalRef(latLngClass); - env->DeleteGlobalRef(markerClass); env->DeleteGlobalRef(latLngZoomClass); env->DeleteGlobalRef(bboxClass); + env->DeleteGlobalRef(spriteClass); + env->DeleteGlobalRef(markerClass); env->DeleteGlobalRef(polylineClass); env->DeleteGlobalRef(polygonClass); env->DeleteGlobalRef(runtimeExceptionClass); @@ -1949,9 +1974,10 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { if (httpRequestClass == nullptr) { env->ExceptionDescribe(); env->DeleteGlobalRef(latLngClass); - env->DeleteGlobalRef(markerClass); env->DeleteGlobalRef(latLngZoomClass); env->DeleteGlobalRef(bboxClass); + env->DeleteGlobalRef(spriteClass); + env->DeleteGlobalRef(markerClass); env->DeleteGlobalRef(polylineClass); env->DeleteGlobalRef(polygonClass); env->DeleteGlobalRef(runtimeExceptionClass); @@ -2002,15 +2028,17 @@ extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) { bboxLonEastId = nullptr; bboxLonWestId = nullptr; + env->DeleteGlobalRef(spriteClass); + spriteClass = nullptr; + spriteIdId = nullptr; + env->DeleteGlobalRef(markerClass); markerClass = nullptr; - markerConstructorId = nullptr; markerPositionId = nullptr; - markerSpriteId = nullptr; + markerIconId = nullptr; env->DeleteGlobalRef(polylineClass); polylineClass = nullptr; - polylineConstructorId = nullptr; polylineAlphaId = nullptr; polylineVisibleId = nullptr; polylineColorId = nullptr; @@ -2019,7 +2047,6 @@ extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) { env->DeleteGlobalRef(polygonClass); polygonClass = nullptr; - polygonConstructorId = nullptr; polygonAlphaId = nullptr; polygonVisibleId = nullptr; polygonFillColorId = nullptr; diff --git a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java index 3567c1a8ef..eed66d2738 100644 --- a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java +++ b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java @@ -2,7 +2,6 @@ package com.mapbox.mapboxsdk.annotations; import android.graphics.Point; import android.support.annotation.Nullable; -import android.text.TextUtils; import android.view.View; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.geometry.LatLng; @@ -18,7 +17,7 @@ public final class Marker extends Annotation { private LatLng position; private float rotation; private String snippet; - private String sprite = "default_marker"; + private Sprite icon; private String title; private InfoWindow infoWindow = null; @@ -140,23 +139,14 @@ public final class Marker extends Annotation { } /** - * You can specify the name of a sprite to get a marker other than the default marker. - * This name can be found in the sprite json file: - * - * https://github.com/mapbox/mapbox-gl-styles/blob/mb-pages/sprites/mapbox-streets.json - * - * If null you will get the default marker. - * - * @param sprite The name of the sprite. + * Do not use this method. Used internally by the SDK. */ - void setSprite(@Nullable String sprite) { - if (!TextUtils.isEmpty(sprite)) { - this.sprite = sprite; - } + public void setIcon(@Nullable Sprite icon) { + this.icon = icon; } - public String getSprite() { - return sprite; + public Sprite getIcon() { + return icon; } void setTitle(String title) { @@ -227,11 +217,6 @@ public final class Marker extends Annotation { // TODO Method in Google Maps Android API // public int hashCode() - // TODO: Implement this method of Google Maps Android API -// void setIcon(BitmapDescriptor icon) { -// -// } - /** * Do not use this method. Used internally by the SDK. */ diff --git a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java index 77a0172cb1..5fa6412c01 100644 --- a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java +++ b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java @@ -71,8 +71,8 @@ public final class MarkerOptions { return marker.getTitle(); } - public String getSprite() { - return marker.getSprite(); + public Sprite getIcon() { + return marker.getIcon(); } private MarkerOptions infoWindowAnchor(float u, float v) { @@ -107,10 +107,8 @@ public final class MarkerOptions { return this; } - public MarkerOptions sprite(@Nullable String sprite) { - if (!TextUtils.isEmpty(sprite)) { - marker.setSprite(sprite); - } + public MarkerOptions icon(@Nullable Sprite icon) { + marker.setIcon(icon); return this; } diff --git a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Sprite.java b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Sprite.java new file mode 100644 index 0000000000..597c196d2a --- /dev/null +++ b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Sprite.java @@ -0,0 +1,40 @@ +package com.mapbox.mapboxsdk.annotations; + +import android.graphics.Bitmap; + +public final class Sprite { + private Bitmap mBitmap; + private String mId; + + Sprite(String id, Bitmap bitmap) { + mId = id; + mBitmap = bitmap; + } + + public String getId() { + return mId; + } + + public Bitmap getBitmap() { + return mBitmap; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Sprite sprite = (Sprite) o; + + if (!mBitmap.equals(sprite.mBitmap)) return false; + return mId.equals(sprite.mId); + + } + + @Override + public int hashCode() { + int result = mBitmap.hashCode(); + result = 31 * result + mId.hashCode(); + return result; + } +} diff --git a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/SpriteFactory.java b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/SpriteFactory.java new file mode 100644 index 0000000000..3178ef1553 --- /dev/null +++ b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/SpriteFactory.java @@ -0,0 +1,109 @@ +package com.mapbox.mapboxsdk.annotations; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.os.Build; +import android.support.v4.content.ContextCompat; +import android.util.DisplayMetrics; +import android.view.WindowManager; + +import com.mapbox.mapboxsdk.R; +import com.mapbox.mapboxsdk.exceptions.TooManySpritesException; +import com.mapbox.mapboxsdk.views.MapView; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.lang.ref.WeakReference; +import java.util.Map; + +public final class SpriteFactory { + + private static final String SPRITE_ID_PREFIX = "com.mapbox.sprites.sprite_"; + + private MapView mMapView; + private Sprite mDefaultMarker; + private BitmapFactory.Options mOptions; + + private int mNextId = 0; + + public SpriteFactory(MapView mapView) { + mMapView = mapView; + DisplayMetrics realMetrics = null; + DisplayMetrics metrics = new DisplayMetrics(); + WindowManager wm = (WindowManager) mMapView.getContext().getSystemService(Context.WINDOW_SERVICE); + + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + realMetrics = new DisplayMetrics(); + wm.getDefaultDisplay().getRealMetrics(realMetrics); + } + wm.getDefaultDisplay().getMetrics(metrics); + + mOptions = new BitmapFactory.Options(); + mOptions.inScaled = true; + mOptions.inDensity = DisplayMetrics.DENSITY_DEFAULT; + mOptions.inTargetDensity = metrics.densityDpi; + if (realMetrics != null) { + mOptions.inScreenDensity = realMetrics.densityDpi; + } + + } + + public Sprite fromBitmap(Bitmap bitmap) { + if (bitmap == null) { + return null; + } + + if (mNextId < 0) { + throw new TooManySpritesException(); + } + String id = SPRITE_ID_PREFIX + ++mNextId; + + return new Sprite(id, bitmap); + } + + public Sprite fromResource(int resourceId) { + Bitmap bitmap = BitmapFactory.decodeResource(mMapView.getResources(), resourceId); + return fromBitmap(bitmap); + } + + public Sprite defaultMarker() { + if (mDefaultMarker == null) { + mDefaultMarker = fromResource(R.drawable.default_marker); + } + return mDefaultMarker; + } + + private Sprite fromInputStream(InputStream is) { + Bitmap bitmap = BitmapFactory.decodeStream(is, null, mOptions); + return fromBitmap(bitmap); + } + + public Sprite fromAsset(String assetName) { + InputStream is; + try { + is = mMapView.getContext().getAssets().open(assetName); + } catch (IOException e) { + return null; + } + return fromInputStream(is); + } + + public Sprite fromPath(String absolutePath) { + Bitmap bitmap = BitmapFactory.decodeFile(absolutePath, mOptions); + return fromBitmap(bitmap); + } + + public Sprite fromFile(String fileName) { + FileInputStream is = null; + try { + is = mMapView.getContext().openFileInput(fileName); + } catch (FileNotFoundException e) { + return null; + } + return fromInputStream(is); + } +} diff --git a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/SpriteBitmapChangedException.java b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/SpriteBitmapChangedException.java new file mode 100644 index 0000000000..15c6d7eec6 --- /dev/null +++ b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/SpriteBitmapChangedException.java @@ -0,0 +1,26 @@ +package com.mapbox.mapboxsdk.exceptions; + +import android.graphics.Bitmap; + +import com.mapbox.mapboxsdk.annotations.Marker; +import com.mapbox.mapboxsdk.annotations.Sprite; +import com.mapbox.mapboxsdk.views.MapView; + +/** + * A {@code SpriteBitmapChangedException} is thrown by {@link MapView} when a {@link Marker} is added + * that has a {@link Sprite} with a {@link Bitmap} that has been modified. + * <p/> + * You cannot modify a {@code Sprite} after it has been added to the map in a {@code Marker} + * + * @see MapView + * @see Sprite + * @see Marker + */ +public class SpriteBitmapChangedException extends RuntimeException { + + public SpriteBitmapChangedException() { + super("The added Marker has a Sprite with a Bitmap that has been modified. You cannot modufy" + + "a Sprite after it has been added in a Marker."); + } + +} diff --git a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/TooManySpritesException.java b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/TooManySpritesException.java new file mode 100644 index 0000000000..02a57ba225 --- /dev/null +++ b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/TooManySpritesException.java @@ -0,0 +1,20 @@ +package com.mapbox.mapboxsdk.exceptions; + +import com.mapbox.mapboxsdk.annotations.Sprite; +import com.mapbox.mapboxsdk.annotations.SpriteFactory; + +/** + * A {@code TooManySpritesException} is thrown by {@link SpriteFactory} when it + * cannot create a {@link Sprite} because there are already too many. + * <p/> + * You should try to reuse Sprite objects whenever possible. + * + * @see SpriteFactory + */ +public class TooManySpritesException extends RuntimeException { + + public TooManySpritesException() { + super("Cannot create a Sprite because there are already too many. Try reusing Sprites."); + } + +} 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 0b10993819..515f1a74d7 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 @@ -17,6 +17,7 @@ import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.RectF; import android.graphics.SurfaceTexture; +import android.graphics.drawable.BitmapDrawable; import android.location.Location; import android.net.ConnectivityManager; import android.net.NetworkInfo; @@ -36,6 +37,7 @@ import android.support.v4.view.ScaleGestureDetectorCompat; import android.support.v7.app.AlertDialog; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.DisplayMetrics; import android.util.Log; import android.view.GestureDetector; import android.view.Gravity; @@ -64,8 +66,11 @@ import com.mapbox.mapboxsdk.annotations.Polygon; import com.mapbox.mapboxsdk.annotations.PolygonOptions; import com.mapbox.mapboxsdk.annotations.Polyline; import com.mapbox.mapboxsdk.annotations.PolylineOptions; +import com.mapbox.mapboxsdk.annotations.Sprite; +import com.mapbox.mapboxsdk.annotations.SpriteFactory; import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.exceptions.InvalidAccessTokenException; +import com.mapbox.mapboxsdk.exceptions.SpriteBitmapChangedException; import com.mapbox.mapboxsdk.geometry.BoundingBox; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngZoom; @@ -152,9 +157,6 @@ public final class MapView extends FrameLayout { // Index into R.arrays.attribution_links private static final int ATTRIBUTION_INDEX_IMPROVE_THIS_MAP = 2; - // Used for loading default marker sprite - private static final String DEFAULT_SPRITE = "com.mapbox.sprites.default"; - /** * The currently supported maximum zoom level. * @@ -174,7 +176,6 @@ public final class MapView extends FrameLayout { // Used to handle DPI scaling private float mScreenDensity = 1.0f; - private float mScreenDensityDpi = 1.0f; // Touch gesture detectors private GestureDetectorCompat mGestureDetector; @@ -213,6 +214,8 @@ public final class MapView extends FrameLayout { private List<Marker> mMarkersNearLastTap = new ArrayList<>(); private Marker mSelectedMarker; private InfoWindowAdapter mInfoWindowAdapter; + private SpriteFactory mSpriteFactory; + private ArrayList<Sprite> mSprites = new ArrayList<>(); // Used for the Mapbox Logo private ImageView mLogoView; @@ -573,7 +576,6 @@ public final class MapView extends FrameLayout { // Get the screen's density mScreenDensity = context.getResources().getDisplayMetrics().density; - mScreenDensityDpi = context.getResources().getDisplayMetrics().densityDpi; // Get the cache path String cachePath = context.getCacheDir().getAbsolutePath(); @@ -818,6 +820,8 @@ public final class MapView extends FrameLayout { } if (change == DID_FINISH_LOADING_MAP) { + reloadSprites(); + reloadMarkers(); adjustTopOffsetPixels(); } } @@ -1595,18 +1599,61 @@ public final class MapView extends FrameLayout { // Annotations // - // Marking this function private until #2506 fixed - private void setSprite(String symbol, Bitmap bitmap) { + public SpriteFactory getSpriteFactory() { + if (mSpriteFactory == null) { + mSpriteFactory = new SpriteFactory(this); + } + return mSpriteFactory; + } + + private void loadSprite(Sprite sprite) { + Bitmap bitmap = sprite.getBitmap(); + String id = sprite.getId(); if (bitmap.getConfig() != Bitmap.Config.ARGB_8888) { bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false); } ByteBuffer buffer = ByteBuffer.allocate(bitmap.getRowBytes() * bitmap.getHeight()); bitmap.copyPixelsToBuffer(buffer); - //float scale = mScreenDensityDpi / bitmap.getDensity() * mScreenDensity; - float scale = 1.0f; + float density = bitmap.getDensity(); + if (density == Bitmap.DENSITY_NONE) { + density = DisplayMetrics.DENSITY_DEFAULT; + } + float scale = density / DisplayMetrics.DENSITY_DEFAULT; + + mNativeMapView.setSprite( + id, + (int) (bitmap.getWidth() / scale), + (int) (bitmap.getHeight() / scale), + scale, buffer.array()); + } - mNativeMapView.setSprite(symbol, bitmap.getWidth(), bitmap.getHeight(), scale, buffer.array()); + private void reloadSprites() { + int count = mSprites.size(); + for (int i = 0; i < count; i++) { + Sprite sprite = mSprites.get(i); + loadSprite(sprite); + } + } + + private Marker prepareMarker(MarkerOptions markerOptions) { + Marker marker = markerOptions.getMarker(); + Sprite icon = marker.getIcon(); + if (icon == null) { + icon = getSpriteFactory().defaultMarker(); + marker.setIcon(icon); + } + if (!mSprites.contains(icon)) { + mSprites.add(icon); + loadSprite(icon); + } else { + Sprite oldSprite = mSprites.get(mSprites.indexOf(icon)); + if (!oldSprite.getBitmap().sameAs(icon.getBitmap())) { + throw new SpriteBitmapChangedException(); + } + } + marker.setTopOffsetPixels(getTopOffsetPixelsForSprite(icon)); + return marker; } /** @@ -1625,21 +1672,7 @@ public final class MapView extends FrameLayout { throw new NullPointerException("markerOptions is null"); } - Marker marker = markerOptions.getMarker(); - - // Load the default marker sprite - if (marker.getSprite() == null) { - //BitmapDrawable bitmapDrawable = (BitmapDrawable) ContextCompat.getDrawable(mContext, R.drawable.default_marker); - //Bitmap bitmap = bitmapDrawable.getBitmap(); - //setSprite(DEFAULT_SPRITE, bitmap); - - // Red default marker is currently broken - //marker.setSprite("default_marker"); - //marker.setSprite(DEFAULT_SPRITE); - } - - marker.setTopOffsetPixels(getTopOffsetPixelsForAnnotationSymbol(marker.getSprite())); - + Marker marker = prepareMarker(markerOptions); long id = mNativeMapView.addMarker(marker); marker.setId(id); // the annotation needs to know its id marker.setMapView(this); // the annotation needs to know which map view it is in @@ -1665,21 +1698,7 @@ public final class MapView extends FrameLayout { List<Marker> markers = new ArrayList<>(markerOptionsList.size()); for (MarkerOptions markerOptions : markerOptionsList) { - Marker marker = markerOptions.getMarker(); - - // Load the default marker sprite - if (marker.getSprite() == null) { - //BitmapDrawable bitmapDrawable = (BitmapDrawable) ContextCompat.getDrawable(mContext, R.drawable.default_marker); - //Bitmap bitmap = bitmapDrawable.getBitmap(); - //setSprite(DEFAULT_SPRITE, bitmap); - - // Red default marker is currently broken - //marker.setSprite("default_marker"); - //marker.setSprite(DEFAULT_SPRITE); - } - - marker.setTopOffsetPixels(getTopOffsetPixelsForAnnotationSymbol(marker.getSprite())); - + Marker marker = prepareMarker(markerOptions); markers.add(marker); } @@ -1885,21 +1904,15 @@ public final class MapView extends FrameLayout { return new ArrayList<>(annotations); } - /** - * Get Top Offset for the annotation symbol. - * Used by InfoWindow - * - * @param symbolName Annotation Symbol - * @return Top Offset in pixels - */ - private int getTopOffsetPixelsForAnnotationSymbol(String symbolName) { + private int getTopOffsetPixelsForSprite(Sprite sprite) { // This method will dead lock if map paused. Causes a freeze if you add a marker in an // activity's onCreate() if (mNativeMapView.isPaused()) { return 0; } - return (int) (mNativeMapView.getTopOffsetPixelsForAnnotationSymbol(symbolName) * mScreenDensity); + return (int) (mNativeMapView.getTopOffsetPixelsForAnnotationSymbol(sprite.getId()) + * mScreenDensity); } /** @@ -1970,7 +1983,7 @@ public final class MapView extends FrameLayout { if (annotation instanceof Marker) { Marker marker = (Marker) annotation; marker.setTopOffsetPixels( - getTopOffsetPixelsForAnnotationSymbol(marker.getSprite())); + getTopOffsetPixelsForSprite(marker.getIcon())); } } @@ -1982,6 +1995,19 @@ public final class MapView extends FrameLayout { } } + private void reloadMarkers() { + int count = mAnnotations.size(); + for (int i = 0; i < count; i++) { + Annotation annotation = mAnnotations.get(i); + if (annotation instanceof Marker) { + Marker marker = (Marker) annotation; + mNativeMapView.removeAnnotation(annotation.getId()); + long newId = mNativeMapView.addMarker(marker); + marker.setId(newId); + } + } + } + // // Rendering // diff --git a/android/java/MapboxGLAndroidSDKTestApp/src/main/assets/dog-park-24.png b/android/java/MapboxGLAndroidSDKTestApp/src/main/assets/dog-park-24.png Binary files differnew file mode 100644 index 0000000000..235f4b4d34 --- /dev/null +++ b/android/java/MapboxGLAndroidSDKTestApp/src/main/assets/dog-park-24.png 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 ab8deffd97..019d4c763d 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 @@ -28,6 +28,7 @@ import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.MarkerOptions; import com.mapbox.mapboxsdk.annotations.PolygonOptions; import com.mapbox.mapboxsdk.annotations.PolylineOptions; +import com.mapbox.mapboxsdk.annotations.Sprite; import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.utils.ApiAccess; @@ -115,7 +116,7 @@ public class MainActivity extends AppCompatActivity { .title("Dropped Pin") .snippet(latLngFormatter.format(point.getLatitude()) + ", " + latLngFormatter.format(point.getLongitude())) - .sprite("default_marker")); + .icon(null)); } }); @@ -400,7 +401,8 @@ public class MainActivity extends AppCompatActivity { final MarkerOptions backLot = generateMarker("Back Lot", "The back lot behind my house", null, 38.649441, -121.369064); markerOptionsList.add(backLot); - final MarkerOptions cheeseRoom = generateMarker("Cheese Room", "The only air conditioned room on the property", "dog-park-15", 38.531577, -122.010646); + final Sprite dogIcon = mMapView.getSpriteFactory().fromAsset("dog-park-24.png"); + final MarkerOptions cheeseRoom = generateMarker("Cheese Room", "The only air conditioned room on the property", dogIcon, 38.531577, -122.010646); markerOptionsList.add(cheeseRoom); List<Marker> markers = mMapView.addMarkers(markerOptionsList); @@ -417,11 +419,11 @@ public class MainActivity extends AppCompatActivity { }); } - private MarkerOptions generateMarker(String title, String snippet, String sprite, double lat, double lng){ + private MarkerOptions generateMarker(String title, String snippet, Sprite icon, double lat, double lng){ return new MarkerOptions() .position(new LatLng(lat, lng)) .title(title) - .sprite(sprite) + .icon(icon) .snippet(snippet); } |