Skip to content

Commit

Permalink
fix api signature for framing viewannotation
Browse files Browse the repository at this point in the history
  • Loading branch information
ank27 committed Oct 27, 2022
1 parent 4ca6825 commit 7a60489
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.mapbox.geojson.Point
import com.mapbox.maps.*
import com.mapbox.maps.plugin.animation.easeTo
import com.mapbox.maps.plugin.animation.flyTo
import com.mapbox.maps.plugin.gestures.*
import com.mapbox.maps.testapp.R
import com.mapbox.maps.testapp.databinding.ActivityViewAnnotationShowcaseBinding
Expand Down Expand Up @@ -48,8 +48,10 @@ class ViewAnnotationBasicAddActivity : AppCompatActivity(), OnMapClickListener {
}
}
binding.fabReframe.setOnClickListener {
val cameraOptions = viewAnnotationManager.showAnnotations(viewAnnotationViews)
mapboxMap.easeTo(cameraOptions)
val cameraOptions = viewAnnotationManager.cameraForAnnotations(viewAnnotationViews)
cameraOptions?.let {
mapboxMap.flyTo(it)
}
}
}

Expand Down
5 changes: 4 additions & 1 deletion sdk/api/metalava.txt
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@ package com.mapbox.maps.viewannotation {
method public android.view.View addViewAnnotation(@LayoutRes int resId, com.mapbox.maps.ViewAnnotationOptions options);
method public void addViewAnnotation(@LayoutRes int resId, com.mapbox.maps.ViewAnnotationOptions options, androidx.asynclayoutinflater.view.AsyncLayoutInflater asyncInflater, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> asyncInflateCallback);
method public void addViewAnnotation(android.view.View view, com.mapbox.maps.ViewAnnotationOptions options);
method public com.mapbox.maps.CameraOptions? cameraForAnnotations(java.util.List<? extends android.view.View> annotations, com.mapbox.maps.EdgeInsets? edgeInsets = null, Double? bearing = null, Double? pitch = null);
method public android.view.View? getViewAnnotationByFeatureId(String featureId);
method public com.mapbox.maps.ViewAnnotationOptions? getViewAnnotationOptionsByFeatureId(String featureId);
method public com.mapbox.maps.ViewAnnotationOptions? getViewAnnotationOptionsByView(android.view.View view);
Expand All @@ -592,7 +593,6 @@ package com.mapbox.maps.viewannotation {
method public void removeOnViewAnnotationUpdatedListener(com.mapbox.maps.viewannotation.OnViewAnnotationUpdatedListener listener);
method public boolean removeViewAnnotation(android.view.View view);
method public void setViewAnnotationUpdateMode(com.mapbox.maps.viewannotation.ViewAnnotationUpdateMode mode);
method public com.mapbox.maps.CameraOptions showAnnotations(java.util.List<? extends android.view.View> annotations, com.mapbox.maps.EdgeInsets? edgeInsets = null, Double? bearing = null, Double? pitch = null);
method public boolean updateViewAnnotation(android.view.View view, com.mapbox.maps.ViewAnnotationOptions options);
field public static final com.mapbox.maps.viewannotation.ViewAnnotationManager.Companion Companion;
field public static final com.mapbox.maps.viewannotation.ViewAnnotationUpdateMode DEFAULT_UPDATE_MODE;
Expand All @@ -601,6 +601,9 @@ package com.mapbox.maps.viewannotation {
public static final class ViewAnnotationManager.Companion {
}

public final class ViewAnnotationOptionsExt {
}

public final class ViewAnnotationOptionsKtxKt {
method public static inline com.mapbox.maps.ViewAnnotationOptions viewAnnotationOptions(kotlin.jvm.functions.Function1<? super com.mapbox.maps.ViewAnnotationOptions.Builder,kotlin.Unit> block);
}
Expand Down
4 changes: 2 additions & 2 deletions sdk/api/sdk.api
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,7 @@ public abstract interface class com/mapbox/maps/viewannotation/ViewAnnotationMan
public abstract fun addViewAnnotation (ILcom/mapbox/maps/ViewAnnotationOptions;)Landroid/view/View;
public abstract fun addViewAnnotation (ILcom/mapbox/maps/ViewAnnotationOptions;Landroidx/asynclayoutinflater/view/AsyncLayoutInflater;Lkotlin/jvm/functions/Function1;)V
public abstract fun addViewAnnotation (Landroid/view/View;Lcom/mapbox/maps/ViewAnnotationOptions;)V
public abstract fun cameraForAnnotations (Ljava/util/List;Lcom/mapbox/maps/EdgeInsets;Ljava/lang/Double;Ljava/lang/Double;)Lcom/mapbox/maps/CameraOptions;
public abstract fun getViewAnnotationByFeatureId (Ljava/lang/String;)Landroid/view/View;
public abstract fun getViewAnnotationOptionsByFeatureId (Ljava/lang/String;)Lcom/mapbox/maps/ViewAnnotationOptions;
public abstract fun getViewAnnotationOptionsByView (Landroid/view/View;)Lcom/mapbox/maps/ViewAnnotationOptions;
Expand All @@ -594,15 +595,14 @@ public abstract interface class com/mapbox/maps/viewannotation/ViewAnnotationMan
public abstract fun removeOnViewAnnotationUpdatedListener (Lcom/mapbox/maps/viewannotation/OnViewAnnotationUpdatedListener;)V
public abstract fun removeViewAnnotation (Landroid/view/View;)Z
public abstract fun setViewAnnotationUpdateMode (Lcom/mapbox/maps/viewannotation/ViewAnnotationUpdateMode;)V
public abstract fun showAnnotations (Ljava/util/List;Lcom/mapbox/maps/EdgeInsets;Ljava/lang/Double;Ljava/lang/Double;)Lcom/mapbox/maps/CameraOptions;
public abstract fun updateViewAnnotation (Landroid/view/View;Lcom/mapbox/maps/ViewAnnotationOptions;)Z
}

public final class com/mapbox/maps/viewannotation/ViewAnnotationManager$Companion {
}

public final class com/mapbox/maps/viewannotation/ViewAnnotationManager$DefaultImpls {
public static synthetic fun showAnnotations$default (Lcom/mapbox/maps/viewannotation/ViewAnnotationManager;Ljava/util/List;Lcom/mapbox/maps/EdgeInsets;Ljava/lang/Double;Ljava/lang/Double;ILjava/lang/Object;)Lcom/mapbox/maps/CameraOptions;
public static synthetic fun cameraForAnnotations$default (Lcom/mapbox/maps/viewannotation/ViewAnnotationManager;Ljava/util/List;Lcom/mapbox/maps/EdgeInsets;Ljava/lang/Double;Ljava/lang/Double;ILjava/lang/Object;)Lcom/mapbox/maps/CameraOptions;
}

public final class com/mapbox/maps/viewannotation/ViewAnnotationOptionsKtxKt {
Expand Down
132 changes: 49 additions & 83 deletions sdk/src/main/java/com/mapbox/maps/ViewAnnotationManagerImpl.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.mapbox.maps

import android.graphics.Rect
import android.os.Looper
import android.view.*
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
Expand Down Expand Up @@ -147,59 +146,60 @@ internal class ViewAnnotationManagerImpl(
return renderThread.viewAnnotationMode
}

override fun showAnnotations(
override fun cameraForAnnotations(
annotations: List<View>,
edgeInsets: EdgeInsets?,
bearing: Double?,
pitch: Double?
): CameraOptions {
val geometryCollection = mutableListOf<Point>()
// get coordinate bounds from annotation options.
val viewAnnotationOptions = annotations.map {
val options = getViewAnnotationOptionsByView(it)
options?.geometry?.let { geometry ->
geometryCollection.add(Point.fromJson(geometry.toJson()))
): CameraOptions? {
return if (annotations.isNotEmpty()) {
val viewAnnotationOptions = annotations.mapNotNull {
getViewAnnotationOptionsByView(it)
}
return@map options
val coordinates = coordinatesFromAnnotations(viewAnnotationOptions)
val paddings = calculateEdgeInset(viewAnnotationOptions, edgeInsets)
mapboxMap.cameraForCoordinates(
coordinates,
paddings,
bearing,
pitch
)
} else {
null
}
var north = -MAX_LATITUDE
var south = MAX_LATITUDE
var west = MAX_LONGITUDE
var east = -MAX_LONGITUDE
geometryCollection.forEach {
north = maxOf(north, it.latitude())
south = minOf(south, it.latitude())
west = minOf(west, it.longitude())
east = maxOf(east, it.longitude())
}

/**
* Function to get coordinates from list of [ViewAnnotationOptions].
*/
private fun coordinatesFromAnnotations(annotationOptions: List<ViewAnnotationOptions>): List<Point> {
val coordinatesList = mutableListOf<Point>()
annotationOptions.forEach {
it.geometry?.let { geometry ->
coordinatesList.add(Point.fromJson(geometry.toJson()))
}
}
val coordinateBounds =
CoordinateBounds(Point.fromLngLat(west, south), Point.fromLngLat(east, north), false)
val paddings = calculateEdgeInset(viewAnnotationOptions, edgeInsets)

return mapboxMap.cameraForCoordinateBounds(
coordinateBounds,
paddings,
bearing,
pitch
)
return coordinatesList
}

/**
* Calculate paddings to show viewAnnotations.
* Get the topMost, leftMost, rightMost, bottomMost annotation options and apply paddings accordingly.
*/
private fun calculateEdgeInset(
viewAnnotationOptions: List<ViewAnnotationOptions?>,
edgeInsets: EdgeInsets?
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal fun calculateEdgeInset(
viewAnnotationOptions: List<ViewAnnotationOptions>,
edgeInsets: EdgeInsets? = null
): EdgeInsets {
val filteredViewAnnotations = viewAnnotationOptions.filter { it.geometry != null }
val topAnnotation =
viewAnnotationOptions.maxByOrNull { Point.fromJson(it?.geometry?.toJson()!!).latitude() }
filteredViewAnnotations.maxByOrNull { Point.fromJson(it.geometry!!.toJson()).latitude() }
val bottomAnnotation =
viewAnnotationOptions.minByOrNull { Point.fromJson(it?.geometry?.toJson()!!).latitude() }
filteredViewAnnotations.minByOrNull { Point.fromJson(it.geometry!!.toJson()).latitude() }
val leftAnnotation =
viewAnnotationOptions.minByOrNull { Point.fromJson(it?.geometry?.toJson()!!).longitude() }
filteredViewAnnotations.minByOrNull { Point.fromJson(it.geometry!!.toJson()).longitude() }
val rightAnnotation =
viewAnnotationOptions.maxByOrNull { Point.fromJson(it?.geometry?.toJson()!!).longitude() }
filteredViewAnnotations.maxByOrNull { Point.fromJson(it.geometry!!.toJson()).longitude() }

return EdgeInsets(
(edgeInsets?.top ?: 0).toDouble().plus(abs(topAnnotation?.frame()?.top ?: 0)),
Expand All @@ -209,50 +209,6 @@ internal class ViewAnnotationManagerImpl(
)
}

/**
* Get [Rect] from [ViewAnnotationOptions]'s geometry, width and height.
* Views can be INVISIBLE on screen and users should be able to query [ViewAnnotationManager.showAnnotations].
* we calculate [Rect] taken by every [ViewAnnotationOptions] and use it with [calculateEdgeInset] function
* to show full view to the user.
*/
private fun ViewAnnotationOptions.frame(): Rect? {
if (width != null && height != null) {
val offset = Pair(width!! * 0.5, height!! * 0.5)
// create a dummy rect at 0,0.
val rect = Rect(
-offset.first.toInt(),
-offset.second.toInt(),
offset.first.toInt(),
offset.second.toInt()
)

// offset rect with respect to anchor defined in viewannotation options.
when (anchor ?: ViewAnnotationAnchor.CENTER) {
ViewAnnotationAnchor.TOP ->
rect.offset(0, offset.second.toInt())
ViewAnnotationAnchor.TOP_LEFT ->
rect.offset(offset.first.toInt(), offset.second.toInt())
ViewAnnotationAnchor.TOP_RIGHT ->
rect.offset(-offset.first.toInt(), offset.second.toInt())
ViewAnnotationAnchor.BOTTOM ->
rect.offset(0, -offset.second.toInt())
ViewAnnotationAnchor.BOTTOM_LEFT ->
rect.offset(offset.first.toInt(), -offset.second.toInt())
ViewAnnotationAnchor.BOTTOM_RIGHT ->
rect.offset(-offset.first.toInt(), -offset.second.toInt())
ViewAnnotationAnchor.LEFT ->
rect.offset(offset.first.toInt(), 0)
ViewAnnotationAnchor.RIGHT ->
rect.offset(-offset.first.toInt(), 0)
else -> rect.offset(0, 0)
}
// add view annotation option's offsetX and offsetY field to offset the rect.
rect.offset(offsetX ?: 0, offsetY ?: 0)
return rect
}
return null
}

/**
* We will have two calls of this callback:
* - first from render thread with actual position list
Expand Down Expand Up @@ -435,7 +391,10 @@ internal class ViewAnnotationManagerImpl(
// still be handled by OnGlobalLayoutListener
if (annotation.view.visibility == View.VISIBLE) {
mapView.removeView(annotation.view)
updateVisibilityAndNotifyUpdateListeners(annotation, ViewAnnotationVisibility.INVISIBLE)
updateVisibilityAndNotifyUpdateListeners(
annotation,
ViewAnnotationVisibility.INVISIBLE
)
}
}
}
Expand All @@ -458,7 +417,10 @@ internal class ViewAnnotationManagerImpl(
height = descriptor.height
}
}
if (!currentViewsDrawnMap.keys.contains(descriptor.identifier) && mapView.indexOfChild(annotation.view) == -1) {
if (!currentViewsDrawnMap.keys.contains(descriptor.identifier) && mapView.indexOfChild(
annotation.view
) == -1
) {
mapView.addView(annotation.view, annotation.viewLayoutParams)
updateVisibilityAndNotifyUpdateListeners(
annotation,
Expand All @@ -485,7 +447,10 @@ internal class ViewAnnotationManagerImpl(
hiddenViewMap[annotation.view]?.let { zIndex ->
annotation.view.translationZ = zIndex
hiddenViewMap.remove(annotation.view)
updateVisibilityAndNotifyUpdateListeners(annotation, ViewAnnotationVisibility.VISIBLE_AND_POSITIONED)
updateVisibilityAndNotifyUpdateListeners(
annotation,
ViewAnnotationVisibility.VISIBLE_AND_POSITIONED
)
}
// as we preserve correct order we bring each view to the front and correct order will be preserved
annotation.view.bringToFront()
Expand Down Expand Up @@ -551,5 +516,6 @@ internal class ViewAnnotationManagerImpl(
"View annotation with associatedFeatureId=%s already exists!"
internal const val MAX_LATITUDE = 90.0
internal const val MAX_LONGITUDE = 180.0
private const val TAG = "ViewAnnotationImpl"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,15 +158,24 @@ interface ViewAnnotationManager {
fun getViewAnnotationUpdateMode(): ViewAnnotationUpdateMode

/**
* Sets the visible region of the map that displays all the given views.
* This API is not supported by Globe projection.
* Get the [CameraOptions] object bound to specified annotations on the MapView.
*
* Using this method with GlobeProjection might lead to incorrect results.
*
* @param annotations list of views needs to fit in the visible frame.
* @param edgeInsets paddings to apply.
* @param bearing camera bearing to apply.
* @param pitch camera pitch to apply.
*
* @return [CameraOptions] object, Will be null if empty annotations provided.
*
*/
fun showAnnotations(
fun cameraForAnnotations(
annotations: List<View>,
edgeInsets: EdgeInsets? = null,
bearing: Double? = null,
pitch: Double? = null
): CameraOptions
): CameraOptions?

/**
* Static methods and variables.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
@file:JvmName("ViewAnnotationOptionsExt")
package com.mapbox.maps.viewannotation

import android.graphics.Rect
import androidx.annotation.VisibleForTesting
import com.mapbox.maps.ViewAnnotationAnchor
import com.mapbox.maps.ViewAnnotationOptions

/**
* Get [Rect] from [ViewAnnotationOptions]'s geometry, width and height.
* This function takes [ViewAnnotationOptions.geometry] as the center of rectangle and
* use width, height and offset values to calculate [Rect] associated.
*
* @return [Rect] associated with [ViewAnnotationOptions]
*/
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal fun ViewAnnotationOptions.frame(): Rect? {
if (width != null && height != null) {
val offsetWidth = (width!! * 0.5).toInt()
val offsetHeight = (height!! * 0.5).toInt()
// create a dummy rect with center assume at 0,0 with offsetWidth and offsetHeight.
val rect = Rect(
-offsetWidth,
-offsetHeight,
offsetWidth,
offsetHeight
)

// offset rect with respect to anchor defined in viewannotation options.
when (anchor ?: ViewAnnotationAnchor.CENTER) {
ViewAnnotationAnchor.TOP -> rect.offset(0, offsetHeight)
ViewAnnotationAnchor.TOP_LEFT -> rect.offset(offsetWidth, offsetHeight)
ViewAnnotationAnchor.TOP_RIGHT -> rect.offset(-offsetWidth, offsetHeight)
ViewAnnotationAnchor.BOTTOM -> rect.offset(0, -offsetHeight)
ViewAnnotationAnchor.BOTTOM_LEFT -> rect.offset(offsetWidth, -offsetHeight)
ViewAnnotationAnchor.BOTTOM_RIGHT -> rect.offset(-offsetWidth, -offsetHeight)
ViewAnnotationAnchor.LEFT -> rect.offset(offsetWidth, 0)
ViewAnnotationAnchor.RIGHT -> rect.offset(-offsetWidth, 0)
else -> rect.offset(0, 0)
}
// add view annotation option's offsetX and offsetY field to offset the rect.
rect.offset(offsetX ?: 0, offsetY ?: 0)
return rect
}
return null
}
Loading

0 comments on commit 7a60489

Please sign in to comment.