diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f91d972b30..fb14e66940 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1112,6 +1112,18 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".ExampleOverviewActivity" />
+
+
+
+
diff --git a/app/src/main/java/com/mapbox/maps/testapp/examples/markersandcallouts/AnnotationWithCalloutActivity.kt b/app/src/main/java/com/mapbox/maps/testapp/examples/markersandcallouts/AnnotationWithCalloutActivity.kt
new file mode 100644
index 0000000000..5328e9d1cf
--- /dev/null
+++ b/app/src/main/java/com/mapbox/maps/testapp/examples/markersandcallouts/AnnotationWithCalloutActivity.kt
@@ -0,0 +1,139 @@
+package com.mapbox.maps.testapp.examples.markersandcallouts
+
+import android.annotation.SuppressLint
+import android.graphics.Bitmap
+import android.os.Bundle
+import android.view.View
+import android.widget.Button
+import androidx.appcompat.app.AppCompatActivity
+import com.mapbox.geojson.Point
+import com.mapbox.maps.MapView
+import com.mapbox.maps.Style
+import com.mapbox.maps.ViewAnnotationAnchor
+import com.mapbox.maps.extension.style.layers.properties.generated.IconAnchor
+import com.mapbox.maps.plugin.annotation.Annotation
+import com.mapbox.maps.plugin.annotation.annotations
+import com.mapbox.maps.plugin.annotation.generated.*
+import com.mapbox.maps.testapp.R
+import com.mapbox.maps.testapp.databinding.ActivityViewAnnotationShowcaseBinding
+import com.mapbox.maps.testapp.databinding.ItemCalloutViewBinding
+import com.mapbox.maps.testapp.utils.BitmapUtils
+import com.mapbox.maps.viewannotation.viewAnnotationOptions
+
+/**
+ * Example how to add view annotation to the point annotation.
+ */
+class AnnotationWithCalloutActivity : AppCompatActivity() {
+
+ private lateinit var pointAnnotationManager: PointAnnotationManager
+ private lateinit var pointAnnotation: PointAnnotation
+ private lateinit var viewAnnotation: View
+
+ @SuppressLint("SetTextI18n")
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val binding = ActivityViewAnnotationShowcaseBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ val iconBitmap = BitmapUtils.bitmapFromDrawableRes(
+ this@AnnotationWithCalloutActivity,
+ R.drawable.blue_marker_view
+ )!!
+
+ binding.mapView.getMapboxMap().loadStyleUri(Style.MAPBOX_STREETS) {
+ prepareAnnotationMarker(binding.mapView, iconBitmap)
+ prepareViewAnnotation(binding.mapView)
+ // show / hide view annotation based on a marker click
+ pointAnnotationManager.addClickListener { clickedAnnotation ->
+ if (pointAnnotation == clickedAnnotation) {
+ viewAnnotation.toggleViewVisibility()
+ }
+ true
+ }
+ // show / hide view annotation based on marker visibility
+ binding.fabStyleToggle.setOnClickListener {
+ pointAnnotation.iconImageBitmap = if (pointAnnotation.iconImage == null) iconBitmap else null
+ pointAnnotationManager.update(pointAnnotation)
+ }
+ // update view annotation geometry if dragging the marker
+ pointAnnotationManager.addDragListener(object : OnPointAnnotationDragListener {
+ override fun onAnnotationDragStarted(annotation: Annotation<*>) {
+ }
+
+ override fun onAnnotationDrag(annotation: Annotation<*>) {
+ if (annotation == pointAnnotation) {
+ binding.mapView.viewAnnotationManager.updateViewAnnotation(
+ viewAnnotation,
+ viewAnnotationOptions {
+ geometry(pointAnnotation.geometry)
+ }
+ )
+ ItemCalloutViewBinding.bind(viewAnnotation).apply {
+ textNativeView.text = "lat=%.2f\nlon=%.2f".format(
+ pointAnnotation.geometry.latitude(),
+ pointAnnotation.geometry.longitude()
+ )
+ }
+ }
+ }
+
+ override fun onAnnotationDragFinished(annotation: Annotation<*>) {
+ }
+ })
+ }
+ }
+
+ private fun View.toggleViewVisibility() {
+ visibility = if (visibility == View.VISIBLE) View.GONE else View.VISIBLE
+ }
+
+ @SuppressLint("SetTextI18n")
+ private fun prepareViewAnnotation(mapView: MapView) {
+ val viewAnnotationManager = mapView.viewAnnotationManager
+ viewAnnotation = viewAnnotationManager.addViewAnnotation(
+ resId = R.layout.item_callout_view,
+ options = viewAnnotationOptions {
+ geometry(POINT)
+ associatedFeatureId(pointAnnotation.featureIdentifier)
+ anchor(ViewAnnotationAnchor.BOTTOM)
+ offsetY((pointAnnotation.iconImageBitmap?.height!!).toInt())
+ }
+ )
+ ItemCalloutViewBinding.bind(viewAnnotation).apply {
+ textNativeView.text = "lat=%.2f\nlon=%.2f".format(POINT.latitude(), POINT.longitude())
+ closeNativeView.setOnClickListener {
+ viewAnnotationManager.removeViewAnnotation(viewAnnotation)
+ }
+ selectButton.setOnClickListener { b ->
+ val button = b as Button
+ val isSelected = button.text.toString().equals("SELECT", true)
+ val pxDelta = if (isSelected) SELECTED_ADD_COEF_PX else -SELECTED_ADD_COEF_PX
+ button.text = if (isSelected) "DESELECT" else "SELECT"
+ viewAnnotationManager.updateViewAnnotation(
+ viewAnnotation,
+ viewAnnotationOptions {
+ width(viewAnnotationManager.getViewAnnotationOptionsByView(viewAnnotation)?.width!! + pxDelta)
+ height(viewAnnotationManager.getViewAnnotationOptionsByView(viewAnnotation)?.height!! + pxDelta)
+ selected(isSelected)
+ }
+ )
+ }
+ }
+ }
+
+ private fun prepareAnnotationMarker(mapView: MapView, iconBitmap: Bitmap) {
+ val annotationPlugin = mapView.annotations
+ val pointAnnotationOptions: PointAnnotationOptions = PointAnnotationOptions()
+ .withPoint(POINT)
+ .withIconImage(iconBitmap)
+ .withIconAnchor(IconAnchor.BOTTOM)
+ .withDraggable(true)
+ pointAnnotationManager = annotationPlugin.createPointAnnotationManager()
+ pointAnnotation = pointAnnotationManager.create(pointAnnotationOptions)
+ }
+
+ private companion object {
+ const val SELECTED_ADD_COEF_PX = 50
+ val POINT: Point = Point.fromLngLat(0.381457, 6.687337)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/values/example_descriptions.xml b/app/src/main/res/values/example_descriptions.xml
index b55db5e083..3afdaa3f5d 100644
--- a/app/src/main/res/values/example_descriptions.xml
+++ b/app/src/main/res/values/example_descriptions.xml
@@ -87,4 +87,5 @@
Using raster source and layer to render OSM tiles with TileSet class
Add view annotation anchored to a symbol layer feature
Add view annotation on a map click
+ Callout view using annotation plugin
diff --git a/app/src/main/res/values/example_titles.xml b/app/src/main/res/values/example_titles.xml
index bd70bf9cba..6c78b71fce 100644
--- a/app/src/main/res/values/example_titles.xml
+++ b/app/src/main/res/values/example_titles.xml
@@ -87,4 +87,5 @@
TileJson
View annotations: advanced example
View annotations: basic example
+ View annotations: annotation callout
diff --git a/plugin-annotation/src/main/java/com/mapbox/maps/plugin/annotation/AnnotationManagerImpl.kt b/plugin-annotation/src/main/java/com/mapbox/maps/plugin/annotation/AnnotationManagerImpl.kt
index 79cec2e929..505952462e 100644
--- a/plugin-annotation/src/main/java/com/mapbox/maps/plugin/annotation/AnnotationManagerImpl.kt
+++ b/plugin-annotation/src/main/java/com/mapbox/maps/plugin/annotation/AnnotationManagerImpl.kt
@@ -453,7 +453,7 @@ abstract class AnnotationManagerImpl, S : Annota
private fun convertAnnotationsToFeatures(annotations: Collection): List =
annotations.map {
it.setUsedDataDrivenProperties()
- Feature.fromGeometry(it.geometry, it.getJsonObjectCopy())
+ Feature.fromGeometry(it.geometry, it.getJsonObjectCopy(), it.featureIdentifier)
}
/**
diff --git a/sdk-base/src/main/java/com/mapbox/maps/plugin/annotation/Annotation.kt b/sdk-base/src/main/java/com/mapbox/maps/plugin/annotation/Annotation.kt
index f2507b87d7..b39b412320 100644
--- a/sdk-base/src/main/java/com/mapbox/maps/plugin/annotation/Annotation.kt
+++ b/sdk-base/src/main/java/com/mapbox/maps/plugin/annotation/Annotation.kt
@@ -71,6 +71,11 @@ abstract class Annotation(
*/
var isSelected: Boolean = false
+ /**
+ * Unique feature identifier. May be useful to connect an annotation with view annotation.
+ */
+ val featureIdentifier = FEATURE_IDENTIFIER_PREFIX + id.toString()
+
/**
* Static variables and methods.
*/
@@ -87,5 +92,7 @@ abstract class Annotation(
* Minimum latitude value in Mercator projection.
*/
const val MIN_MERCATOR_LATITUDE = -85.05112877980659
+
+ private const val FEATURE_IDENTIFIER_PREFIX = "mapbox_annotation_"
}
}
\ No newline at end of file