11package com.mapbox.maps.plugin.annotation
22
33import android.graphics.PointF
4+ import androidx.annotation.RestrictTo
45import com.mapbox.android.gestures.MoveGestureDetector
56import com.mapbox.common.Cancelable
67import com.mapbox.geojson.Feature
78import com.mapbox.geojson.FeatureCollection
89import com.mapbox.geojson.Geometry
910import com.mapbox.geojson.Point
10- import com.mapbox.maps.*
11+ import com.mapbox.maps.LayerPosition
12+ import com.mapbox.maps.RenderedQueryGeometry
13+ import com.mapbox.maps.RenderedQueryOptions
14+ import com.mapbox.maps.ScreenCoordinate
1115import com.mapbox.maps.extension.style.StyleInterface
1216import com.mapbox.maps.extension.style.expressions.dsl.generated.literal
1317import com.mapbox.maps.extension.style.expressions.generated.Expression
@@ -27,16 +31,21 @@ import com.mapbox.maps.extension.style.layers.generated.symbolLayer
2731import com.mapbox.maps.extension.style.sources.addSource
2832import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource
2933import com.mapbox.maps.extension.style.sources.generated.geoJsonSource
34+ import com.mapbox.maps.logE
35+ import com.mapbox.maps.logW
3036import com.mapbox.maps.plugin.InvalidPluginConfigurationException
3137import com.mapbox.maps.plugin.Plugin.Companion.MAPBOX_GESTURES_PLUGIN_ID
3238import com.mapbox.maps.plugin.annotation.generated.PointAnnotation
33- import com.mapbox.maps.plugin.delegates.*
39+ import com.mapbox.maps.plugin.delegates.MapCameraManagerDelegate
40+ import com.mapbox.maps.plugin.delegates.MapDelegateProvider
41+ import com.mapbox.maps.plugin.delegates.MapFeatureQueryDelegate
42+ import com.mapbox.maps.plugin.delegates.MapListenerDelegate
43+ import com.mapbox.maps.plugin.delegates.MapStyleStateDelegate
3444import com.mapbox.maps.plugin.gestures.GesturesPlugin
35- import com.mapbox.maps.plugin.gestures.OnMapClickListener
36- import com.mapbox.maps.plugin.gestures.OnMapLongClickListener
3745import com.mapbox.maps.plugin.gestures.OnMoveListener
46+ import com.mapbox.maps.plugin.gestures.TopPriorityAsyncMapClickListener
3847import com.mapbox.maps.plugin.gestures.TopPriorityOnMoveListener
39- import java.util.*
48+ import java.lang.UnsupportedOperationException
4049import java.util.concurrent.ConcurrentHashMap
4150import java.util.concurrent.CountDownLatch
4251import java.util.concurrent.TimeUnit
@@ -590,50 +599,103 @@ abstract class AnnotationManagerImpl<G : Geometry, T : Annotation<G>, S : Annota
590599 }
591600
592601 /* *
593- * Class handle the map click event
602+ * For internal usage.
603+ *
604+ * @suppress
594605 */
595- inner class MapClick : OnMapClickListener {
606+ @RestrictTo(RestrictTo .Scope .LIBRARY_GROUP_PREFIX )
607+ abstract inner class TopPriorityClickListenerImpl : TopPriorityAsyncMapClickListener {
608+
609+ private var asyncQrfCancelable: Cancelable ? = null
610+
596611 /* *
597- * Called when the user clicks on the map view.
598- * Note that calling this method is blocking main thread until querying map for features is finished.
599- *
600- * @param point The projected map coordinate the user clicked on.
601- * @return True if this click should be consumed and not passed further to other listeners registered afterwards,
602- * false otherwise.
612+ * For internal usage.
603613 */
604- override fun onMapClick (point : Point ): Boolean {
605- queryMapForFeatures(point)?.let {
606- clickListeners.forEach { listener ->
607- if (listener.onAnnotationClick(it)) {
608- return true
614+ abstract fun couldSkipClick (): Boolean
615+
616+ /* *
617+ * For internal usage.
618+ */
619+ abstract fun processAnnotation (annotation : T ): Boolean
620+
621+ /* *
622+ * For internal usage.
623+ */
624+ override fun asyncHandleClick (point : Point , continueToNextListener : () -> Unit ) {
625+ if (couldSkipClick()) {
626+ return
627+ }
628+ try {
629+ asyncQrfCancelable?.cancel()
630+ } catch (e: UnsatisfiedLinkError ) {
631+ // fastest solution to fix the unit test
632+ }
633+ asyncQrfCancelable = queryMapForFeaturesAsync(
634+ mapCameraManagerDelegate.pixelForCoordinate(point)
635+ ) { annotation, errorOrCanceled ->
636+ // if ongoing QRF is canceled by a new one - QRF callback returns an error
637+ // we should not then invoke `consumeCallback` in gestures as it will be invoked for the new QRF
638+ // even although it did not even start
639+ if (errorOrCanceled) {
640+ return @queryMapForFeaturesAsync
641+ }
642+ annotation?.let {
643+ if (processAnnotation(it)) {
644+ // return early and do not invoke continueToNextListener
645+ return @queryMapForFeaturesAsync
609646 }
610647 }
611- selectAnnotation(it )
648+ continueToNextListener.invoke( )
612649 }
613- return false
614650 }
615- }
616651
617- /* *
618- * Class handle the map long click event
619- */
620- inner class MapLongClick : OnMapLongClickListener {
621652 /* *
622- * Called when the user long clicks on the map view.
623- *
624- * @param point The projected map coordinate the user clicked on.
625- * @return True if this click should be consumed and not passed further to other listeners registered afterwards,
626- * false otherwise.
653+ * Must not be used.
654+ */
655+ override fun onMapClick (point : Point ): Boolean {
656+ throw UnsupportedOperationException ()
657+ }
658+
659+ /* *
660+ * Must not be used.
627661 */
628662 override fun onMapLongClick (point : Point ): Boolean {
629- if (longClickListeners.isEmpty()) {
630- return false
663+ throw UnsupportedOperationException ()
664+ }
665+ }
666+
667+ /* *
668+ * Class handle the map click event.
669+ *
670+ * This class should not be used directly.
671+ */
672+ inner class MapClick : TopPriorityClickListenerImpl () {
673+
674+ override fun couldSkipClick () = clickListeners.isEmpty() && interactionListener.isEmpty()
675+
676+ override fun processAnnotation (annotation : T ): Boolean {
677+ clickListeners.forEach { userAnnotationClickListener ->
678+ if (userAnnotationClickListener.onAnnotationClick(annotation)) {
679+ return true
680+ }
631681 }
632- queryMapForFeatures(point)?.let {
633- longClickListeners.forEach { listener ->
634- if (listener.onAnnotationLongClick(it)) {
635- return true
636- }
682+ selectAnnotation(annotation)
683+ return false
684+ }
685+ }
686+
687+ /* *
688+ * Class handle the map long click event.
689+ * This class should not be used directly.
690+ */
691+ inner class MapLongClick : TopPriorityClickListenerImpl () {
692+
693+ override fun couldSkipClick () = longClickListeners.isEmpty()
694+
695+ override fun processAnnotation (annotation : T ): Boolean {
696+ longClickListeners.forEach { userAnnotationLongClickListener ->
697+ if (userAnnotationLongClickListener.onAnnotationLongClick(annotation)) {
698+ return true
637699 }
638700 }
639701 return false
@@ -662,7 +724,7 @@ abstract class AnnotationManagerImpl<G : Geometry, T : Annotation<G>, S : Annota
662724 detector.focalPoint.x.toDouble(),
663725 detector.focalPoint.y.toDouble()
664726 )
665- ) { annotation ->
727+ ) { annotation, _ ->
666728 annotation?.let {
667729 startDragging(it)
668730 }
@@ -779,6 +841,8 @@ abstract class AnnotationManagerImpl<G : Geometry, T : Annotation<G>, S : Annota
779841 /* *
780842 * Query the rendered annotation around the point
781843 *
844+ * Note: this method blocks main thread.
845+ *
782846 * @param point the point for querying
783847 * @return the queried annotation at this point
784848 */
@@ -790,6 +854,8 @@ abstract class AnnotationManagerImpl<G : Geometry, T : Annotation<G>, S : Annota
790854 /* *
791855 * Query the rendered annotation around the point
792856 *
857+ * Note: this method blocks main thread.
858+ *
793859 * @param screenCoordinate the screenCoordinate for querying
794860 * @return the queried annotation on this screenCoordinate
795861 */
@@ -836,7 +902,7 @@ abstract class AnnotationManagerImpl<G : Geometry, T : Annotation<G>, S : Annota
836902 return annotation
837903 }
838904
839- private fun queryMapForFeaturesAsync (screenCoordinate : ScreenCoordinate , qrfResult : (T ? ) -> Unit ): Cancelable {
905+ private fun queryMapForFeaturesAsync (screenCoordinate : ScreenCoordinate , qrfResult : (T ? , Boolean ) -> Unit ): Cancelable {
840906 val layerList = mutableListOf<String >()
841907 layer?.let {
842908 layerList.add(it.layerId)
@@ -856,19 +922,28 @@ abstract class AnnotationManagerImpl<G : Geometry, T : Annotation<G>, S : Annota
856922 val id = annotationId.asLong
857923 when {
858924 annotationMap.containsKey(id) -> {
859- qrfResult(annotationMap[id])
925+ qrfResult(annotationMap[id], false )
860926 }
927+
861928 dragAnnotationMap.containsKey(id) -> {
862- qrfResult(dragAnnotationMap[id])
929+ qrfResult(dragAnnotationMap[id], false )
863930 }
931+
864932 else -> {
865933 logE(
866934 TAG ,
867935 " The queried id: $id , doesn't belong to an active annotation."
868936 )
869937 }
870938 }
871- } ? : qrfResult(null )
939+ } ? : run {
940+ if (features.isError) {
941+ // error is called when QRF is canceled
942+ qrfResult(null , true )
943+ } else {
944+ qrfResult(null , false )
945+ }
946+ }
872947 }
873948 }
874949
0 commit comments