Skip to content

Commit 572a41d

Browse files
authored
Improve usage of bitmap to mapbox image conversions (#2841)
1 parent 274375d commit 572a41d

File tree

35 files changed

+308
-263
lines changed

35 files changed

+308
-263
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@ Mapbox welcomes participation and contributions from everyone.
1111
* Change the signature of experimental `MapboxMap.queryRenderedFeatures(RenderedQueryGeometry, TypedFeaturesetDescriptor, Value?, QueryRenderedFeaturesetFeaturesCallback)` to `MapboxMap.queryRenderedFeatures(TypedFeaturesetDescriptor, RenderedQueryGeometry?, Value?, QueryRenderedFeaturesetFeaturesCallback)`. `RenderedQueryGeometry` being NULL is equivalent to passing a bounding box encompassing the entire map viewport.
1212
* [compose] Change the signature of experimental `MapState.queryRenderedFeatures(RenderedQueryGeometry, TypedFeaturesetDescriptor, Expression?): List` to `MapState.queryRenderedFeatures(TypedFeaturesetDescriptor, RenderedQueryGeometry?, Expression?): List`. `RenderedQueryGeometry` being NULL is equivalent to passing a bounding box encompassing the entire map viewport.
1313

14+
## Features ✨ and improvements 🏁
15+
* Annotate `Bitmap.toMapboxImage()` and related as delicate API due to its native memory allocation.
16+
1417
## Bug fixes 🐞
1518
* Disable false-positive lint error "Incorrect number of expressions".
16-
19+
* Fix possible out of memory in native heap during annotation manager annotation updates (`AnnotationManager.update(...)`).
1720

1821

1922
# 11.7.2 November 05, 2024

app/src/main/java/com/mapbox/maps/testapp/examples/AnimatedImageSourceActivity.kt

Lines changed: 28 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package com.mapbox.maps.testapp.examples
22

3-
import android.content.Context
4-
import android.graphics.Bitmap
53
import android.os.Bundle
6-
import android.os.Handler
7-
import android.os.Looper
84
import androidx.appcompat.app.AppCompatActivity
9-
import com.mapbox.maps.MapboxMap
5+
import androidx.lifecycle.Lifecycle
6+
import androidx.lifecycle.lifecycleScope
7+
import androidx.lifecycle.repeatOnLifecycle
8+
import com.mapbox.maps.Image
9+
import com.mapbox.maps.MapboxDelicateApi
1010
import com.mapbox.maps.Style
1111
import com.mapbox.maps.extension.style.layers.generated.rasterLayer
1212
import com.mapbox.maps.extension.style.sources.generated.ImageSource
@@ -17,22 +17,23 @@ import com.mapbox.maps.extension.style.style
1717
import com.mapbox.maps.testapp.R
1818
import com.mapbox.maps.testapp.databinding.ActivityAnimatedImagesourceBinding
1919
import com.mapbox.maps.testapp.utils.BitmapUtils.bitmapFromDrawableRes
20+
import com.mapbox.maps.toMapboxImage
21+
import kotlinx.coroutines.delay
22+
import kotlinx.coroutines.isActive
23+
import kotlinx.coroutines.launch
2024

2125
/**
2226
* Load a raster image to a style using ImageSource and display it on a map as
2327
* animated weather data using RasterLayer.
2428
*/
2529
class AnimatedImageSourceActivity : AppCompatActivity() {
2630

27-
private val handler: Handler = Handler(Looper.getMainLooper())
28-
private lateinit var mapboxMap: MapboxMap
29-
private lateinit var runnable: Runnable
30-
31+
@OptIn(MapboxDelicateApi::class)
3132
override fun onCreate(savedInstanceState: Bundle?) {
3233
super.onCreate(savedInstanceState)
3334
val binding = ActivityAnimatedImagesourceBinding.inflate(layoutInflater)
3435
setContentView(binding.root)
35-
mapboxMap = binding.mapView.mapboxMap
36+
val mapboxMap = binding.mapView.mapboxMap
3637
mapboxMap.loadStyle(
3738
style(style = Style.STANDARD) {
3839
+imageSource(ID_IMAGE_SOURCE) {
@@ -48,49 +49,27 @@ class AnimatedImageSourceActivity : AppCompatActivity() {
4849
+rasterLayer(ID_IMAGE_LAYER, ID_IMAGE_SOURCE) { }
4950
}
5051
)
51-
}
52-
53-
override fun onStart() {
54-
super.onStart()
52+
val drawables: List<Image> = listOf(
53+
bitmapFromDrawableRes(R.drawable.southeast_radar_0).toMapboxImage(),
54+
bitmapFromDrawableRes(R.drawable.southeast_radar_1).toMapboxImage(),
55+
bitmapFromDrawableRes(R.drawable.southeast_radar_2).toMapboxImage(),
56+
bitmapFromDrawableRes(R.drawable.southeast_radar_3).toMapboxImage(),
57+
)
58+
var drawableIndex = 0
5559
mapboxMap.getStyle {
5660
val imageSource: ImageSource = it.getSourceAs(ID_IMAGE_SOURCE)!!
57-
runnable = RefreshImageRunnable(applicationContext, imageSource, handler)
58-
handler.postDelayed(runnable, 100)
59-
}
60-
}
61-
62-
override fun onStop() {
63-
super.onStop()
64-
if (::runnable.isInitialized) {
65-
handler.removeCallbacks(runnable)
66-
}
67-
}
68-
69-
private class RefreshImageRunnable constructor(
70-
appContext: Context,
71-
private val imageSource: ImageSource,
72-
private val handler: Handler
73-
) :
74-
Runnable {
75-
private val drawables: Array<Bitmap?> = arrayOfNulls(4)
76-
private var drawableIndex: Int
77-
78-
override fun run() {
79-
drawables[drawableIndex++]?.let { bitmap ->
80-
imageSource.updateImage(bitmap)
81-
if (drawableIndex > 3) {
82-
drawableIndex = 0
61+
// Create a new coroutine in the lifecycleScope
62+
lifecycleScope.launch {
63+
// repeatOnLifecycle launches the block in a new coroutine every time the
64+
// lifecycle is in the STARTED state (or above) and cancels it when it's STOPPED.
65+
repeatOnLifecycle(Lifecycle.State.STARTED) {
66+
while (isActive) {
67+
imageSource.updateImage(drawables[drawableIndex++])
68+
drawableIndex %= drawables.size
69+
delay(1000L)
70+
}
8371
}
8472
}
85-
handler.postDelayed(this, 1000)
86-
}
87-
88-
init {
89-
drawables[0] = bitmapFromDrawableRes(appContext, R.drawable.southeast_radar_0)
90-
drawables[1] = bitmapFromDrawableRes(appContext, R.drawable.southeast_radar_1)
91-
drawables[2] = bitmapFromDrawableRes(appContext, R.drawable.southeast_radar_2)
92-
drawables[3] = bitmapFromDrawableRes(appContext, R.drawable.southeast_radar_3)
93-
drawableIndex = 1
9473
}
9574
}
9675

app/src/main/java/com/mapbox/maps/testapp/examples/CircleLayerClusteringActivity.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,7 @@ class CircleLayerClusteringActivity : AppCompatActivity() {
5858

5959
addClusteredGeoJsonSource(it)
6060

61-
bitmapFromDrawableRes(this, R.drawable.ic_cross)?.let { bitmap ->
62-
it.addImage(CROSS_ICON_ID, bitmap, true)
63-
}
61+
it.addImage(CROSS_ICON_ID, bitmapFromDrawableRes(R.drawable.ic_cross), true)
6462

6563
Toast.makeText(
6664
this@CircleLayerClusteringActivity,

app/src/main/java/com/mapbox/maps/testapp/examples/GesturesActivity.kt

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,26 @@ import android.annotation.SuppressLint
44
import android.os.Bundle
55
import android.os.Handler
66
import android.os.Looper
7-
import android.view.*
7+
import android.view.LayoutInflater
8+
import android.view.Menu
9+
import android.view.MenuItem
10+
import android.view.View
11+
import android.view.ViewGroup
12+
import android.view.ViewTreeObserver
813
import android.widget.RelativeLayout
914
import android.widget.TextView
1015
import androidx.annotation.ColorInt
1116
import androidx.annotation.IntDef
1217
import androidx.annotation.NonNull
1318
import androidx.appcompat.app.AppCompatActivity
1419
import androidx.core.content.ContextCompat
15-
import androidx.core.graphics.drawable.toBitmap
1620
import androidx.recyclerview.widget.LinearLayoutManager
1721
import androidx.recyclerview.widget.RecyclerView
18-
import com.mapbox.android.gestures.*
22+
import com.mapbox.android.gestures.AndroidGesturesManager
23+
import com.mapbox.android.gestures.MoveGestureDetector
24+
import com.mapbox.android.gestures.RotateGestureDetector
25+
import com.mapbox.android.gestures.ShoveGestureDetector
26+
import com.mapbox.android.gestures.StandardScaleGestureDetector
1927
import com.mapbox.geojson.Point
2028
import com.mapbox.maps.CameraOptions
2129
import com.mapbox.maps.MapboxMap
@@ -25,10 +33,15 @@ import com.mapbox.maps.plugin.annotation.annotations
2533
import com.mapbox.maps.plugin.annotation.generated.PointAnnotationManager
2634
import com.mapbox.maps.plugin.annotation.generated.PointAnnotationOptions
2735
import com.mapbox.maps.plugin.annotation.generated.createPointAnnotationManager
28-
import com.mapbox.maps.plugin.gestures.*
36+
import com.mapbox.maps.plugin.gestures.GesturesPlugin
37+
import com.mapbox.maps.plugin.gestures.OnMoveListener
38+
import com.mapbox.maps.plugin.gestures.OnRotateListener
39+
import com.mapbox.maps.plugin.gestures.OnScaleListener
40+
import com.mapbox.maps.plugin.gestures.OnShoveListener
41+
import com.mapbox.maps.plugin.gestures.gestures
2942
import com.mapbox.maps.testapp.R
3043
import com.mapbox.maps.testapp.databinding.ActivityGesturesBinding
31-
import java.util.*
44+
import com.mapbox.maps.testapp.utils.BitmapUtils.bitmapFromDrawableRes
3245

3346
/**
3447
* Test activity showcasing APIs around gestures implementation.
@@ -138,7 +151,7 @@ class GesturesActivity : AppCompatActivity() {
138151
.build()
139152
)
140153
mapboxMap.loadStyle(Style.STANDARD) {
141-
it.addImage(MARKER_IMAGE_ID, ContextCompat.getDrawable(this, R.drawable.ic_red_marker)!!.toBitmap())
154+
it.addImage(MARKER_IMAGE_ID, bitmapFromDrawableRes(R.drawable.ic_red_marker))
142155
}
143156

144157
binding.mapView.waitForLayout {

app/src/main/java/com/mapbox/maps/testapp/examples/ImageSourceActivity.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,8 @@ class ImageSourceActivity : AppCompatActivity() {
3636
+rasterLayer(ID_IMAGE_LAYER, ID_IMAGE_SOURCE) {}
3737
}
3838
) {
39-
bitmapFromDrawableRes(this, R.drawable.miami_beach)?.let { bitmap ->
40-
val imageSource: ImageSource = it.getSourceAs(ID_IMAGE_SOURCE)!!
41-
imageSource.updateImage(bitmap)
42-
}
39+
val imageSource: ImageSource = it.getSourceAs(ID_IMAGE_SOURCE)!!
40+
imageSource.updateImage(bitmapFromDrawableRes(R.drawable.miami_beach))
4341
}
4442
}
4543

app/src/main/java/com/mapbox/maps/testapp/examples/RuntimeStylingActivity.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,12 +329,13 @@ class RuntimeStylingActivity : AppCompatActivity() {
329329
style.addLayer(raster)
330330
}
331331

332+
@OptIn(MapboxDelicateApi::class)
332333
private fun addLayerWithoutStyleExtension(style: Style) {
333-
val bitmap = ContextCompat.getDrawable(this, R.drawable.android_symbol)?.toBitmap(64, 64)
334+
val bitmap = ContextCompat.getDrawable(this, R.drawable.android_symbol)!!.toBitmap(64, 64)
334335
val expected = style.addStyleImage(
335336
"myImage",
336337
1f,
337-
bitmap!!.toMapboxImage(),
338+
bitmap.toMapboxImage(),
338339
false,
339340
mutableListOf(),
340341
mutableListOf(),

app/src/main/java/com/mapbox/maps/testapp/examples/SpaceStationLocationActivity.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,19 @@ import androidx.appcompat.app.AppCompatActivity
99
import com.mapbox.geojson.Feature
1010
import com.mapbox.geojson.FeatureCollection
1111
import com.mapbox.geojson.Point
12-
import com.mapbox.maps.*
12+
import com.mapbox.maps.CameraOptions
13+
import com.mapbox.maps.MapInitOptions
14+
import com.mapbox.maps.MapView
15+
import com.mapbox.maps.MapboxMap
16+
import com.mapbox.maps.Style
1317
import com.mapbox.maps.extension.style.layers.addLayer
1418
import com.mapbox.maps.extension.style.layers.generated.symbolLayer
1519
import com.mapbox.maps.extension.style.sources.addSource
1620
import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource
1721
import com.mapbox.maps.extension.style.sources.generated.geoJsonSource
1822
import com.mapbox.maps.extension.style.sources.getSource
23+
import com.mapbox.maps.logE
24+
import com.mapbox.maps.logW
1925
import com.mapbox.maps.plugin.animation.MapAnimationOptions.Companion.mapAnimationOptions
2026
import com.mapbox.maps.plugin.animation.flyTo
2127
import com.mapbox.maps.testapp.R

app/src/main/java/com/mapbox/maps/testapp/examples/TintFillPatternActivity.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package com.mapbox.maps.testapp.examples
22

3-
import android.graphics.*
3+
import android.graphics.Bitmap
4+
import android.graphics.BitmapFactory
5+
import android.graphics.Canvas
6+
import android.graphics.Paint
7+
import android.graphics.PorterDuff
8+
import android.graphics.PorterDuffColorFilter
49
import android.os.Bundle
510
import androidx.annotation.ColorInt
611
import androidx.appcompat.app.AppCompatActivity

app/src/main/java/com/mapbox/maps/testapp/examples/annotation/AnimatePointAnnotationActivity.kt

Lines changed: 32 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,23 @@ import android.view.animation.LinearInterpolator
88
import androidx.appcompat.app.AppCompatActivity
99
import androidx.core.animation.addListener
1010
import com.mapbox.geojson.Point
11-
import com.mapbox.maps.*
11+
import com.mapbox.maps.CoordinateBounds
12+
import com.mapbox.maps.MapLoaded
13+
import com.mapbox.maps.MapLoadedCallback
14+
import com.mapbox.maps.MapboxMap
1215
import com.mapbox.maps.dsl.cameraOptions
1316
import com.mapbox.maps.extension.style.layers.properties.generated.IconPitchAlignment
1417
import com.mapbox.maps.plugin.annotation.annotations
15-
import com.mapbox.maps.plugin.annotation.generated.*
18+
import com.mapbox.maps.plugin.annotation.generated.PointAnnotation
19+
import com.mapbox.maps.plugin.annotation.generated.PointAnnotationManager
20+
import com.mapbox.maps.plugin.annotation.generated.PointAnnotationOptions
21+
import com.mapbox.maps.plugin.annotation.generated.createPointAnnotationManager
1622
import com.mapbox.maps.testapp.R
1723
import com.mapbox.maps.testapp.databinding.ActivityAnnotationBinding
1824
import com.mapbox.maps.testapp.utils.BitmapUtils.bitmapFromDrawableRes
25+
import com.mapbox.maps.toCameraOptions
1926
import com.mapbox.turf.TurfMeasurement
20-
import java.util.*
27+
import java.util.Random
2128

2229
/**
2330
* Example showing how to add point annotations and animate them
@@ -49,45 +56,32 @@ class AnimatePointAnnotationActivity : AppCompatActivity(), MapLoadedCallback {
4956
zoom(12.0)
5057
}
5158
)
52-
loadStyle(Style.STANDARD)
59+
subscribeMapLoaded(this@AnimatePointAnnotationActivity)
5360
}
5461

55-
mapboxMap.subscribeMapLoaded(this@AnimatePointAnnotationActivity)
5662
binding.deleteAll.visibility = View.GONE
5763
binding.changeStyle.visibility = View.GONE
64+
binding.changeSlot.visibility = View.GONE
5865
}
5966

6067
override fun run(eventData: MapLoaded) {
6168
pointAnnotationManager = binding.mapView.annotations.createPointAnnotationManager().apply {
62-
this.iconPitchAlignment = IconPitchAlignment.MAP
63-
bitmapFromDrawableRes(
64-
this@AnimatePointAnnotationActivity,
65-
R.drawable.ic_car_top
66-
)?.let {
67-
val noAnimationOptionList = mutableListOf<PointAnnotationOptions>()
68-
for (i in 0 until noAnimateCarNum) {
69-
noAnimationOptionList.add(
70-
PointAnnotationOptions()
71-
.withPoint(getPointInBounds())
72-
.withIconImage(it)
73-
)
74-
}
75-
create(noAnimationOptionList)
69+
iconPitchAlignment = IconPitchAlignment.MAP
70+
val carTop = bitmapFromDrawableRes(R.drawable.ic_car_top)
71+
val noAnimationOptionList = List(noAnimateCarNum) {
72+
PointAnnotationOptions()
73+
.withPoint(getPointInBounds())
74+
.withIconImage(carTop)
7675
}
77-
bitmapFromDrawableRes(
78-
this@AnimatePointAnnotationActivity,
79-
R.drawable.ic_taxi_top
80-
)?.let {
81-
val animationOptionList = mutableListOf<PointAnnotationOptions>()
82-
for (i in 0 until animateCarNum) {
83-
animationOptionList.add(
84-
PointAnnotationOptions()
85-
.withPoint(getPointInBounds())
86-
.withIconImage(it)
87-
)
88-
}
89-
animateCarList = create(animationOptionList)
76+
create(noAnimationOptionList)
77+
78+
val taxiTop = bitmapFromDrawableRes(R.drawable.ic_taxi_top)
79+
val animationOptionList = List(animateCarNum) {
80+
PointAnnotationOptions()
81+
.withPoint(getPointInBounds())
82+
.withIconImage(taxiTop)
9083
}
84+
animateCarList = create(animationOptionList)
9185
}
9286
animateCars()
9387
}
@@ -99,20 +93,19 @@ class AnimatePointAnnotationActivity : AppCompatActivity(), MapLoadedCallback {
9993

10094
private fun animateCars() {
10195
cleanAnimation()
102-
for (i in 0 until animateCarNum) {
96+
val carEvaluator = CarEvaluator()
97+
animateCarList.forEach { animatedCar ->
10398
val nextPoint = getPointInBounds()
10499
val animator =
105100
ValueAnimator.ofObject(
106-
CarEvaluator(),
107-
animateCarList[i].point,
101+
carEvaluator,
102+
animatedCar.point,
108103
nextPoint
109104
).setDuration(animateDuration)
110-
animateCarList[i].iconRotate = TurfMeasurement.bearing(animateCarList[i].point, nextPoint)
105+
animatedCar.iconRotate = TurfMeasurement.bearing(animatedCar.point, nextPoint)
111106
animator.interpolator = LinearInterpolator()
112107
animator.addUpdateListener { valueAnimator ->
113-
(valueAnimator.animatedValue as Point).let {
114-
animateCarList[i].point = it
115-
}
108+
animatedCar.point = valueAnimator.animatedValue as Point
116109
}
117110
animator.start()
118111
animators.add(animator)

app/src/main/java/com/mapbox/maps/testapp/examples/java/RuntimeStylingJavaActivity.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import android.graphics.drawable.Drawable;
1515
import android.os.Bundle;
1616

17+
import androidx.annotation.OptIn;
1718
import androidx.appcompat.app.AppCompatActivity;
1819
import androidx.core.content.ContextCompat;
1920
import androidx.core.graphics.drawable.DrawableKt;
@@ -24,6 +25,7 @@
2425
import com.mapbox.geojson.FeatureCollection;
2526
import com.mapbox.maps.ExtensionUtils;
2627
import com.mapbox.maps.MapView;
28+
import com.mapbox.maps.MapboxDelicateApi;
2729
import com.mapbox.maps.MapboxMap;
2830
import com.mapbox.maps.Style;
2931
import com.mapbox.maps.extension.style.expressions.generated.Expression;
@@ -342,6 +344,7 @@ private void addRasterLayer(Style style) {
342344
LayerUtils.addLayer(style, raster);
343345
}
344346

347+
@OptIn(markerClass = MapboxDelicateApi.class)
345348
private void addLayerWithoutStyleExtension(Style style) {
346349
final Drawable drawable = ContextCompat.getDrawable(this, R.drawable.android_symbol);
347350
final Bitmap bitmap = DrawableKt.toBitmap(drawable, 64, 64, null);

0 commit comments

Comments
 (0)