From 50018b7574cb1e13a8bbeecd359d83d2a54c6b33 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Mon, 28 Jan 2019 15:06:54 +0100 Subject: Add image support with drawable overload (#13793) * [android] - add addImage support that takes a Drawable as parameter --- .../main/java/com/mapbox/mapboxsdk/maps/Style.java | 56 ++++++++++++++++++++++ .../com/mapbox/mapboxsdk/utils/BitmapUtils.java | 22 +++++++++ .../com/mapbox/mapboxsdk/maps/StyleBuilderTest.kt | 18 +++++++ .../java/com/mapbox/mapboxsdk/maps/StyleTest.kt | 27 ++++++++++- 4 files changed, 121 insertions(+), 2 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java index 14fc69f456..567f0fc768 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java @@ -1,17 +1,20 @@ package com.mapbox.mapboxsdk.maps; import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StringDef; import android.util.DisplayMetrics; + import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.layers.TransitionOptions; import com.mapbox.mapboxsdk.style.light.Light; import com.mapbox.mapboxsdk.style.sources.Source; +import com.mapbox.mapboxsdk.utils.BitmapUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -298,6 +301,20 @@ public class Style { addImage(name, image, false); } + /** + * Adds an drawable to be converted into a bitmap to be used in the map's style + * + * @param name the name of the image + * @param drawable the drawable instance to convert + */ + public void addImage(@NonNull String name, @NonNull Drawable drawable) { + Bitmap bitmap = BitmapUtils.getBitmapFromDrawable(drawable); + if (bitmap == null) { + throw new IllegalArgumentException("Provided drawable couldn't be converted to a Bitmap."); + } + addImage(name, bitmap, false); + } + /** * Adds an image to be used in the map's style * @@ -312,6 +329,8 @@ public class Style { /** * Adds an images to be used in the map's style. + * + * @param images the map of images to add */ public void addImages(@NonNull HashMap images) { addImages(images, false); @@ -319,6 +338,9 @@ public class Style { /** * Adds an images to be used in the map's style. + * + * @param images the map of images to add + * @param sdf the flag indicating image is an SDF or template image */ public void addImages(@NonNull HashMap images, boolean sdf) { validateState("addImages"); @@ -630,6 +652,22 @@ public class Style { return this; } + /** + * Will add the drawable as image when the map style has loaded. + * + * @param id the id for the image + * @param drawable the drawable to be converted and added + * @return this + */ + @NonNull + public Builder withImage(@NonNull String id, @NonNull Drawable drawable) { + Bitmap bitmap = BitmapUtils.getBitmapFromDrawable(drawable); + if (bitmap == null) { + throw new IllegalArgumentException("Provided drawable couldn't be converted to a Bitmap."); + } + return this.withImage(id, bitmap, false); + } + /** * Will add the image when the map style has loaded. * @@ -642,11 +680,29 @@ public class Style { return this.withImage(id, image, false); } + /** + * Will add the drawable as image when the map style has loaded. + * + * @param id the id for the image + * @param drawable the drawable to be converted and added + * @param sdf the flag indicating image is an SDF or template image + * @return this + */ + @NonNull + public Builder withImage(@NonNull String id, @NonNull Drawable drawable, boolean sdf) { + Bitmap bitmap = BitmapUtils.getBitmapFromDrawable(drawable); + if (bitmap == null) { + throw new IllegalArgumentException("Provided drawable couldn't be converted to a Bitmap."); + } + return this.withImage(id, bitmap, sdf); + } + /** * Will add the image when the map style has loaded. * * @param id the id for the image * @param image the image to be added + * @param sdf the flag indicating image is an SDF or template image * @return this */ @NonNull diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/BitmapUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/BitmapUtils.java index 5584a6eb58..f14e49cb6a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/BitmapUtils.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/BitmapUtils.java @@ -8,9 +8,12 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.annotation.VisibleForTesting; import android.view.View; import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; +import java.util.Arrays; /** * Utility class for creating bitmaps @@ -121,4 +124,23 @@ public class BitmapUtils { return new BitmapDrawable(context.getResources(), compass); } + + /** + * Validates if the bytes of a bitmap matches another + * + * @param bitmap the bitmap to be compared against + * @param other the bitmap to compare with + * @return true if equal + */ + @VisibleForTesting + public static boolean equals(Bitmap bitmap, Bitmap other) { + ByteBuffer buffer = ByteBuffer.allocate(bitmap.getHeight() * bitmap.getRowBytes()); + bitmap.copyPixelsToBuffer(buffer); + + ByteBuffer bufferOther = ByteBuffer.allocate(other.getHeight() * other.getRowBytes()); + other.copyPixelsToBuffer(bufferOther); + + return Arrays.equals(buffer.array(), bufferOther.array()); + } + } diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleBuilderTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleBuilderTest.kt index cb42b0b33d..2dd6b55e28 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleBuilderTest.kt +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleBuilderTest.kt @@ -1,11 +1,14 @@ package com.mapbox.mapboxsdk.maps import android.graphics.Bitmap +import android.graphics.drawable.ShapeDrawable import com.mapbox.mapboxsdk.style.layers.SymbolLayer import com.mapbox.mapboxsdk.style.layers.TransitionOptions import com.mapbox.mapboxsdk.style.sources.GeoJsonSource +import com.mapbox.mapboxsdk.utils.BitmapUtils import io.mockk.mockk import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -95,6 +98,21 @@ class StyleBuilderTest { assertEquals(true, builder.images[0].sdf) } + @Test + fun testWithImageDrawable() { + val drawable = ShapeDrawable() + drawable.intrinsicWidth = 1 + drawable.intrinsicHeight = 1 + val builder = Style.Builder() + builder.withImage("id", drawable, true) + assertTrue(BitmapUtils.equals( + BitmapUtils.getBitmapFromDrawable(drawable)!!, + builder.images[0].bitmap) + ) + assertEquals("id", builder.images[0].id) + assertEquals(true, builder.images[0].sdf) + } + @Test fun testWithTransitionOptions() { val transitionOptions = TransitionOptions(100, 200) diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleTest.kt index 83769914ef..58aac9cc78 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleTest.kt +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleTest.kt @@ -1,6 +1,7 @@ package com.mapbox.mapboxsdk.maps import android.graphics.Bitmap +import android.graphics.drawable.ShapeDrawable import com.mapbox.mapboxsdk.constants.MapboxConstants import com.mapbox.mapboxsdk.style.layers.SymbolLayer import com.mapbox.mapboxsdk.style.layers.TransitionOptions @@ -14,7 +15,6 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner -import java.lang.IllegalStateException @RunWith(RobolectricTestRunner::class) class StyleTest { @@ -277,4 +277,27 @@ class StyleTest { style!!.addLayer(mockk()) } -} \ No newline at end of file + @Test + fun testAddImage() { + val bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) + val builder = Style.Builder().fromUrl(Style.SATELLITE).withImage("id", bitmap) + mapboxMap.setStyle(builder) + verify(exactly = 1) { nativeMapView.styleUrl = Style.SATELLITE } + verify(exactly = 0) { nativeMapView.addImages(any()) } + mapboxMap.notifyStyleLoaded() + verify(exactly = 1) { nativeMapView.addImages(any()) } + } + + @Test + fun testAddDrawable() { + val drawable = ShapeDrawable() + drawable.intrinsicHeight = 10 + drawable.intrinsicWidth = 10 + val builder = Style.Builder().fromUrl(Style.SATELLITE).withImage("id", drawable) + mapboxMap.setStyle(builder) + verify(exactly = 1) { nativeMapView.styleUrl = Style.SATELLITE } + verify(exactly = 0) { nativeMapView.addImages(any()) } + mapboxMap.notifyStyleLoaded() + verify(exactly = 1) { nativeMapView.addImages(any()) } + } +} -- cgit v1.2.1