1
1
package com.mapbox.maps.plugin.annotation
2
2
3
3
import android.graphics.PointF
4
+ import androidx.annotation.RestrictTo
4
5
import com.mapbox.android.gestures.MoveGestureDetector
5
6
import com.mapbox.common.Cancelable
6
7
import com.mapbox.geojson.Feature
7
8
import com.mapbox.geojson.FeatureCollection
8
9
import com.mapbox.geojson.Geometry
9
10
import 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
11
15
import com.mapbox.maps.extension.style.StyleInterface
12
16
import com.mapbox.maps.extension.style.expressions.dsl.generated.literal
13
17
import com.mapbox.maps.extension.style.expressions.generated.Expression
@@ -27,16 +31,21 @@ import com.mapbox.maps.extension.style.layers.generated.symbolLayer
27
31
import com.mapbox.maps.extension.style.sources.addSource
28
32
import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource
29
33
import com.mapbox.maps.extension.style.sources.generated.geoJsonSource
34
+ import com.mapbox.maps.logE
35
+ import com.mapbox.maps.logW
30
36
import com.mapbox.maps.plugin.InvalidPluginConfigurationException
31
37
import com.mapbox.maps.plugin.Plugin.Companion.MAPBOX_GESTURES_PLUGIN_ID
32
38
import 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
34
44
import com.mapbox.maps.plugin.gestures.GesturesPlugin
35
- import com.mapbox.maps.plugin.gestures.OnMapClickListener
36
- import com.mapbox.maps.plugin.gestures.OnMapLongClickListener
37
45
import com.mapbox.maps.plugin.gestures.OnMoveListener
46
+ import com.mapbox.maps.plugin.gestures.TopPriorityAsyncMapClickListener
38
47
import com.mapbox.maps.plugin.gestures.TopPriorityOnMoveListener
39
- import java.util.*
48
+ import java.lang.UnsupportedOperationException
40
49
import java.util.concurrent.ConcurrentHashMap
41
50
import java.util.concurrent.CountDownLatch
42
51
import java.util.concurrent.TimeUnit
@@ -590,50 +599,103 @@ abstract class AnnotationManagerImpl<G : Geometry, T : Annotation<G>, S : Annota
590
599
}
591
600
592
601
/* *
593
- * Class handle the map click event
602
+ * For internal usage.
603
+ *
604
+ * @suppress
594
605
*/
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
+
596
611
/* *
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.
603
613
*/
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
609
646
}
610
647
}
611
- selectAnnotation(it )
648
+ continueToNextListener.invoke( )
612
649
}
613
- return false
614
650
}
615
- }
616
651
617
- /* *
618
- * Class handle the map long click event
619
- */
620
- inner class MapLongClick : OnMapLongClickListener {
621
652
/* *
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.
627
661
*/
628
662
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
+ }
631
681
}
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
637
699
}
638
700
}
639
701
return false
@@ -662,7 +724,7 @@ abstract class AnnotationManagerImpl<G : Geometry, T : Annotation<G>, S : Annota
662
724
detector.focalPoint.x.toDouble(),
663
725
detector.focalPoint.y.toDouble()
664
726
)
665
- ) { annotation ->
727
+ ) { annotation, _ ->
666
728
annotation?.let {
667
729
startDragging(it)
668
730
}
@@ -779,6 +841,8 @@ abstract class AnnotationManagerImpl<G : Geometry, T : Annotation<G>, S : Annota
779
841
/* *
780
842
* Query the rendered annotation around the point
781
843
*
844
+ * Note: this method blocks main thread.
845
+ *
782
846
* @param point the point for querying
783
847
* @return the queried annotation at this point
784
848
*/
@@ -790,6 +854,8 @@ abstract class AnnotationManagerImpl<G : Geometry, T : Annotation<G>, S : Annota
790
854
/* *
791
855
* Query the rendered annotation around the point
792
856
*
857
+ * Note: this method blocks main thread.
858
+ *
793
859
* @param screenCoordinate the screenCoordinate for querying
794
860
* @return the queried annotation on this screenCoordinate
795
861
*/
@@ -836,7 +902,7 @@ abstract class AnnotationManagerImpl<G : Geometry, T : Annotation<G>, S : Annota
836
902
return annotation
837
903
}
838
904
839
- private fun queryMapForFeaturesAsync (screenCoordinate : ScreenCoordinate , qrfResult : (T ? ) -> Unit ): Cancelable {
905
+ private fun queryMapForFeaturesAsync (screenCoordinate : ScreenCoordinate , qrfResult : (T ? , Boolean ) -> Unit ): Cancelable {
840
906
val layerList = mutableListOf<String >()
841
907
layer?.let {
842
908
layerList.add(it.layerId)
@@ -856,19 +922,28 @@ abstract class AnnotationManagerImpl<G : Geometry, T : Annotation<G>, S : Annota
856
922
val id = annotationId.asLong
857
923
when {
858
924
annotationMap.containsKey(id) -> {
859
- qrfResult(annotationMap[id])
925
+ qrfResult(annotationMap[id], false )
860
926
}
927
+
861
928
dragAnnotationMap.containsKey(id) -> {
862
- qrfResult(dragAnnotationMap[id])
929
+ qrfResult(dragAnnotationMap[id], false )
863
930
}
931
+
864
932
else -> {
865
933
logE(
866
934
TAG ,
867
935
" The queried id: $id , doesn't belong to an active annotation."
868
936
)
869
937
}
870
938
}
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
+ }
872
947
}
873
948
}
874
949
0 commit comments