react-native-google-maps-plus 1.7.0-dev.1 → 1.7.0-dev.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/android/src/main/java/com/rngooglemapsplus/GoogleMapsViewImpl.kt +457 -451
  2. package/android/src/main/java/com/rngooglemapsplus/LocationHandler.kt +3 -8
  3. package/android/src/main/java/com/rngooglemapsplus/MapCircleBuilder.kt +2 -1
  4. package/android/src/main/java/com/rngooglemapsplus/MapHeatmapBuilder.kt +1 -1
  5. package/android/src/main/java/com/rngooglemapsplus/MapHelper.kt +22 -0
  6. package/android/src/main/java/com/rngooglemapsplus/MapMarkerBuilder.kt +103 -2
  7. package/android/src/main/java/com/rngooglemapsplus/MapPolygonBuilder.kt +2 -1
  8. package/android/src/main/java/com/rngooglemapsplus/MapPolylineBuilder.kt.kt +2 -1
  9. package/android/src/main/java/com/rngooglemapsplus/MapUrlTileOverlayBuilder.kt +40 -0
  10. package/android/src/main/java/com/rngooglemapsplus/RNGoogleMapsPlusView.kt +84 -34
  11. package/android/src/main/java/com/rngooglemapsplus/extensions/LatLngBoundsExtension.kt +10 -0
  12. package/android/src/main/java/com/rngooglemapsplus/extensions/MapObjectTagExtensions.kt +84 -0
  13. package/android/src/main/java/com/rngooglemapsplus/extensions/RNLatLngBoundsExtension.kt +2 -8
  14. package/android/src/main/java/com/rngooglemapsplus/extensions/RNMapTypeExtension.kt +13 -0
  15. package/android/src/main/java/com/rngooglemapsplus/extensions/VisibleRegionExtension.kt +13 -0
  16. package/ios/GoogleMapViewImpl.swift +164 -48
  17. package/ios/MapCircleBuilder.swift +2 -0
  18. package/ios/MapHeatmapBuilder.swift +2 -1
  19. package/ios/MapMarkerBuilder.swift +54 -1
  20. package/ios/MapPolygonBuilder.swift +2 -0
  21. package/ios/MapPolylineBuilder.swift +2 -0
  22. package/ios/MapUrlTileOverlayBuilder.swift +24 -0
  23. package/ios/RNGoogleMapsPlusView.swift +68 -11
  24. package/ios/extensions/GMSCoordinateBounds+Extension.swift +4 -13
  25. package/ios/extensions/GMSVisibleRegion+Extension.swift +14 -0
  26. package/ios/extensions/MapObjectTag+Extension.swift +93 -0
  27. package/ios/extensions/RNLatLngBounds+Extension.swift +4 -4
  28. package/ios/extensions/RNMapType+Extension.swift +18 -0
  29. package/lib/module/types.js.map +1 -1
  30. package/lib/nitrogen/generated/shared/json/RNGoogleMapsPlusViewConfig.json +7 -0
  31. package/lib/typescript/src/RNGoogleMapsPlusView.nitro.d.ts +18 -9
  32. package/lib/typescript/src/RNGoogleMapsPlusView.nitro.d.ts.map +1 -1
  33. package/lib/typescript/src/types.d.ts +18 -5
  34. package/lib/typescript/src/types.d.ts.map +1 -1
  35. package/nitrogen/generated/android/RNGoogleMapsPlusOnLoad.cpp +8 -4
  36. package/nitrogen/generated/android/c++/JFunc_void_RNRegion_RNCamera.hpp +83 -0
  37. package/nitrogen/generated/android/c++/JFunc_void_RNRegion_RNCamera_bool.hpp +2 -0
  38. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +75 -0
  39. package/nitrogen/generated/android/c++/JFunc_void_std__string_RNLatLng.hpp +77 -0
  40. package/nitrogen/generated/android/c++/JFunc_void_std__string_std__string_RNLatLng.hpp +77 -0
  41. package/nitrogen/generated/android/c++/JHybridRNGoogleMapsPlusViewSpec.cpp +237 -83
  42. package/nitrogen/generated/android/c++/JHybridRNGoogleMapsPlusViewSpec.hpp +32 -16
  43. package/nitrogen/generated/android/c++/JRNLatLngBounds.hpp +8 -8
  44. package/nitrogen/generated/android/c++/JRNMapUiSettings.hpp +11 -3
  45. package/nitrogen/generated/android/c++/JRNMarker.hpp +7 -3
  46. package/nitrogen/generated/android/c++/JRNRegion.hpp +23 -13
  47. package/nitrogen/generated/android/c++/JRNUrlTileOverlay.hpp +78 -0
  48. package/nitrogen/generated/android/c++/views/JHybridRNGoogleMapsPlusViewStateUpdater.cpp +28 -0
  49. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngooglemapsplus/Func_void_RNRegion_RNCamera.kt +81 -0
  50. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngooglemapsplus/{Func_void_std__optional_std__string_.kt → Func_void_std__string.kt} +12 -12
  51. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngooglemapsplus/{Func_void_std__optional_std__string__RNLatLng.kt → Func_void_std__string_RNLatLng.kt} +12 -12
  52. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngooglemapsplus/Func_void_std__string_std__string_RNLatLng.kt +81 -0
  53. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngooglemapsplus/HybridRNGoogleMapsPlusViewSpec.kt +122 -24
  54. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngooglemapsplus/RNLatLngBounds.kt +4 -4
  55. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngooglemapsplus/RNMapUiSettings.kt +9 -3
  56. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngooglemapsplus/RNMarker.kt +6 -3
  57. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngooglemapsplus/RNRegion.kt +11 -5
  58. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngooglemapsplus/RNUrlTileOverlay.kt +52 -0
  59. package/nitrogen/generated/ios/RNGoogleMapsPlus-Swift-Cxx-Bridge.cpp +32 -8
  60. package/nitrogen/generated/ios/RNGoogleMapsPlus-Swift-Cxx-Bridge.hpp +168 -43
  61. package/nitrogen/generated/ios/RNGoogleMapsPlus-Swift-Cxx-Umbrella.hpp +3 -0
  62. package/nitrogen/generated/ios/c++/HybridRNGoogleMapsPlusViewSpecSwift.hpp +86 -22
  63. package/nitrogen/generated/ios/c++/views/HybridRNGoogleMapsPlusViewComponent.mm +35 -0
  64. package/nitrogen/generated/ios/swift/Func_void_RNRegion_RNCamera.swift +47 -0
  65. package/nitrogen/generated/ios/swift/Func_void_std__optional_std__string_.swift +6 -6
  66. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
  67. package/nitrogen/generated/ios/swift/Func_void_std__string_RNLatLng.swift +47 -0
  68. package/nitrogen/generated/ios/swift/Func_void_std__string_std__string_RNLatLng.swift +47 -0
  69. package/nitrogen/generated/ios/swift/HybridRNGoogleMapsPlusViewSpec.swift +17 -8
  70. package/nitrogen/generated/ios/swift/HybridRNGoogleMapsPlusViewSpec_cxx.swift +340 -138
  71. package/nitrogen/generated/ios/swift/RNLatLngBounds.swift +8 -8
  72. package/nitrogen/generated/ios/swift/RNMapUiSettings.swift +61 -1
  73. package/nitrogen/generated/ios/swift/RNMarker.swift +24 -1
  74. package/nitrogen/generated/ios/swift/RNRegion.swift +33 -11
  75. package/nitrogen/generated/ios/swift/RNUrlTileOverlay.swift +133 -0
  76. package/nitrogen/generated/shared/c++/HybridRNGoogleMapsPlusViewSpec.cpp +16 -0
  77. package/nitrogen/generated/shared/c++/HybridRNGoogleMapsPlusViewSpec.hpp +41 -22
  78. package/nitrogen/generated/shared/c++/RNLatLngBounds.hpp +9 -9
  79. package/nitrogen/generated/shared/c++/RNMapUiSettings.hpp +10 -2
  80. package/nitrogen/generated/shared/c++/RNMarker.hpp +6 -2
  81. package/nitrogen/generated/shared/c++/RNRegion.hpp +24 -13
  82. package/nitrogen/generated/shared/c++/RNUrlTileOverlay.hpp +96 -0
  83. package/nitrogen/generated/shared/c++/views/HybridRNGoogleMapsPlusViewComponent.cpp +100 -16
  84. package/nitrogen/generated/shared/c++/views/HybridRNGoogleMapsPlusViewComponent.hpp +18 -10
  85. package/nitrogen/generated/shared/json/RNGoogleMapsPlusViewConfig.json +7 -0
  86. package/package.json +4 -2
  87. package/src/RNGoogleMapsPlusView.nitro.ts +20 -8
  88. package/src/types.ts +19 -5
  89. package/android/src/main/java/com/rngooglemapsplus/extensions/LatLngBounds.kt +0 -15
  90. package/nitrogen/generated/android/c++/JFunc_void_std__optional_std__string_.hpp +0 -76
  91. package/nitrogen/generated/android/c++/JFunc_void_std__optional_std__string__RNLatLng.hpp +0 -78
  92. package/nitrogen/generated/ios/swift/Func_void_std__optional_std__string__RNLatLng.swift +0 -54
  93. /package/android/src/main/java/com/rngooglemapsplus/extensions/{RNSize.kt → RNSizeExtension.kt} +0 -0
  94. /package/android/src/main/java/com/rngooglemapsplus/extensions/{RNSnapshotFormat.kt → RNSnapshotFormatExtension.kt} +0 -0
  95. /package/android/src/main/java/com/rngooglemapsplus/extensions/{RNSnapshotResultType.kt → RNSnapshotResultTypeExtension.kt} +0 -0
@@ -1,13 +1,18 @@
1
1
  package com.rngooglemapsplus
2
2
 
3
+ import CircleTag
4
+ import MarkerTag
5
+ import PolygonTag
6
+ import PolylineTag
3
7
  import android.annotation.SuppressLint
4
8
  import android.graphics.Bitmap
9
+ import android.location.Location
5
10
  import android.util.Base64
6
11
  import android.util.Size
12
+ import android.view.View
7
13
  import android.widget.FrameLayout
8
14
  import androidx.core.graphics.scale
9
15
  import com.facebook.react.bridge.LifecycleEventListener
10
- import com.facebook.react.bridge.UiThreadUtil
11
16
  import com.facebook.react.uimanager.PixelUtil.dpToPx
12
17
  import com.facebook.react.uimanager.ThemedReactContext
13
18
  import com.google.android.gms.maps.CameraUpdateFactory
@@ -24,6 +29,7 @@ import com.google.android.gms.maps.model.MapColorScheme
24
29
  import com.google.android.gms.maps.model.MapStyleOptions
25
30
  import com.google.android.gms.maps.model.Marker
26
31
  import com.google.android.gms.maps.model.MarkerOptions
32
+ import com.google.android.gms.maps.model.PointOfInterest
27
33
  import com.google.android.gms.maps.model.Polygon
28
34
  import com.google.android.gms.maps.model.PolygonOptions
29
35
  import com.google.android.gms.maps.model.Polyline
@@ -32,6 +38,8 @@ import com.google.android.gms.maps.model.TileOverlay
32
38
  import com.google.android.gms.maps.model.TileOverlayOptions
33
39
  import com.google.maps.android.data.kml.KmlLayer
34
40
  import com.margelo.nitro.core.Promise
41
+ import com.rngooglemapsplus.extensions.onUi
42
+ import com.rngooglemapsplus.extensions.onUiSync
35
43
  import com.rngooglemapsplus.extensions.toGooglePriority
36
44
  import com.rngooglemapsplus.extensions.toLatLng
37
45
  import com.rngooglemapsplus.extensions.toLocationErrorCode
@@ -42,6 +50,8 @@ import com.rngooglemapsplus.extensions.toRnCamera
42
50
  import com.rngooglemapsplus.extensions.toRnLatLng
43
51
  import com.rngooglemapsplus.extensions.toRnLocation
44
52
  import com.rngooglemapsplus.extensions.toRnRegion
53
+ import idTag
54
+ import tagData
45
55
  import java.io.ByteArrayInputStream
46
56
  import java.io.ByteArrayOutputStream
47
57
  import java.io.File
@@ -59,24 +69,33 @@ class GoogleMapsViewImpl(
59
69
  GoogleMap.OnCameraIdleListener,
60
70
  GoogleMap.OnMapClickListener,
61
71
  GoogleMap.OnMapLongClickListener,
72
+ GoogleMap.OnPoiClickListener,
62
73
  GoogleMap.OnMarkerClickListener,
63
74
  GoogleMap.OnPolylineClickListener,
64
75
  GoogleMap.OnPolygonClickListener,
65
76
  GoogleMap.OnCircleClickListener,
66
77
  GoogleMap.OnMarkerDragListener,
67
78
  GoogleMap.OnIndoorStateChangeListener,
79
+ GoogleMap.OnInfoWindowClickListener,
80
+ GoogleMap.OnInfoWindowCloseListener,
81
+ GoogleMap.OnInfoWindowLongClickListener,
82
+ GoogleMap.OnMyLocationClickListener,
83
+ GoogleMap.OnMyLocationButtonClickListener,
84
+ GoogleMap.InfoWindowAdapter,
68
85
  LifecycleEventListener {
69
86
  private var initialized = false
87
+ private var loaded = false
70
88
  private var destroyed = false
71
89
  private var googleMap: GoogleMap? = null
72
90
  private var mapView: MapView? = null
73
91
 
74
- private val pendingMarkers = mutableListOf<Pair<String, MarkerOptions>>()
92
+ private val pendingMarkers = mutableListOf<Triple<String, MarkerOptions, MarkerTag>>()
75
93
  private val pendingPolylines = mutableListOf<Pair<String, PolylineOptions>>()
76
94
  private val pendingPolygons = mutableListOf<Pair<String, PolygonOptions>>()
77
95
  private val pendingCircles = mutableListOf<Pair<String, CircleOptions>>()
78
96
  private val pendingHeatmaps = mutableListOf<Pair<String, TileOverlayOptions>>()
79
97
  private val pendingKmlLayers = mutableListOf<Pair<String, String>>()
98
+ private val pendingUrlTilesOverlays = mutableListOf<Pair<String, TileOverlayOptions>>()
80
99
 
81
100
  private val markersById = mutableMapOf<String, Marker>()
82
101
  private val polylinesById = mutableMapOf<String, Polyline>()
@@ -84,113 +103,111 @@ class GoogleMapsViewImpl(
84
103
  private val circlesById = mutableMapOf<String, Circle>()
85
104
  private val heatmapsById = mutableMapOf<String, TileOverlay>()
86
105
  private val kmlLayersById = mutableMapOf<String, KmlLayer>()
106
+ private val urlTileOverlaysById = mutableMapOf<String, TileOverlay>()
87
107
 
88
108
  private var cameraMoveReason = -1
89
- private var lastSubmittedCameraPosition: CameraPosition? = null
90
109
 
91
110
  init {
92
111
  reactContext.addLifecycleEventListener(this)
93
112
  }
94
113
 
95
- fun initMapView(googleMapsOptions: GoogleMapOptions) {
96
- if (initialized) return
97
- initialized = true
98
- val result = playServiceHandler.playServicesAvailability()
99
- val errorCode = result.toRNMapErrorCodeOrNull()
114
+ fun initMapView(googleMapsOptions: GoogleMapOptions) =
115
+ onUi {
116
+ if (initialized) return@onUi
117
+ initialized = true
100
118
 
101
- if (errorCode != null) {
102
- onMapError?.invoke(errorCode)
119
+ val result = playServiceHandler.playServicesAvailability()
120
+ val errorCode = result.toRNMapErrorCodeOrNull()
121
+ if (errorCode != null) {
122
+ onMapError?.invoke(errorCode)
123
+ if (errorCode == RNMapErrorCode.PLAY_SERVICES_MISSING ||
124
+ errorCode == RNMapErrorCode.PLAY_SERVICES_INVALID
125
+ ) {
126
+ return@onUi
127
+ }
128
+ }
103
129
 
104
- if (errorCode == RNMapErrorCode.PLAY_SERVICES_MISSING ||
105
- errorCode == RNMapErrorCode.PLAY_SERVICES_INVALID
106
- ) {
107
- return
130
+ mapView = MapView(reactContext, googleMapsOptions)
131
+ super.addView(mapView)
132
+
133
+ mapView?.onCreate(null)
134
+ mapView?.getMapAsync { map ->
135
+ googleMap = map
136
+ googleMap?.setOnMapLoadedCallback {
137
+ googleMap?.setOnCameraMoveStartedListener(this@GoogleMapsViewImpl)
138
+ googleMap?.setOnCameraMoveListener(this@GoogleMapsViewImpl)
139
+ googleMap?.setOnCameraIdleListener(this@GoogleMapsViewImpl)
140
+ googleMap?.setOnMarkerClickListener(this@GoogleMapsViewImpl)
141
+ googleMap?.setOnPolylineClickListener(this@GoogleMapsViewImpl)
142
+ googleMap?.setOnPolygonClickListener(this@GoogleMapsViewImpl)
143
+ googleMap?.setOnCircleClickListener(this@GoogleMapsViewImpl)
144
+ googleMap?.setOnMapClickListener(this@GoogleMapsViewImpl)
145
+ googleMap?.setOnMapLongClickListener(this@GoogleMapsViewImpl)
146
+ googleMap?.setOnPoiClickListener(this@GoogleMapsViewImpl)
147
+ googleMap?.setOnMarkerDragListener(this@GoogleMapsViewImpl)
148
+ googleMap?.setOnInfoWindowClickListener(this@GoogleMapsViewImpl)
149
+ googleMap?.setOnInfoWindowCloseListener(this@GoogleMapsViewImpl)
150
+ googleMap?.setOnInfoWindowLongClickListener(this@GoogleMapsViewImpl)
151
+ googleMap?.setOnMyLocationClickListener(this@GoogleMapsViewImpl)
152
+ googleMap?.setOnMyLocationButtonClickListener(this@GoogleMapsViewImpl)
153
+ googleMap?.setInfoWindowAdapter(this@GoogleMapsViewImpl)
154
+ loaded = true
155
+ onMapLoaded?.invoke(
156
+ map.projection.visibleRegion.toRnRegion(),
157
+ map.cameraPosition.toRnCamera(),
158
+ )
159
+ }
160
+ applyProps()
161
+ initLocationCallbacks()
162
+ onMapReady?.invoke(true)
108
163
  }
109
164
  }
110
165
 
111
- mapView =
112
- MapView(
113
- reactContext,
114
- googleMapsOptions,
166
+ override fun onCameraMoveStarted(reason: Int) =
167
+ onUi {
168
+ if (!loaded) return@onUi
169
+ cameraMoveReason = reason
170
+ val visibleRegion = googleMap?.projection?.visibleRegion ?: return@onUi
171
+ val cameraPosition = googleMap?.cameraPosition ?: return@onUi
172
+ onCameraChangeStart?.invoke(
173
+ visibleRegion.toRnRegion(),
174
+ cameraPosition.toRnCamera(),
175
+ GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE == reason,
115
176
  )
116
-
117
- super.addView(mapView)
118
-
119
- mapView?.onCreate(null)
120
- mapView?.getMapAsync { map ->
121
- googleMap = map
122
- googleMap?.setOnMapLoadedCallback {
123
- googleMap?.setOnCameraMoveStartedListener(this@GoogleMapsViewImpl)
124
- googleMap?.setOnCameraMoveListener(this@GoogleMapsViewImpl)
125
- googleMap?.setOnCameraIdleListener(this@GoogleMapsViewImpl)
126
- googleMap?.setOnMarkerClickListener(this@GoogleMapsViewImpl)
127
- googleMap?.setOnPolylineClickListener(this@GoogleMapsViewImpl)
128
- googleMap?.setOnPolygonClickListener(this@GoogleMapsViewImpl)
129
- googleMap?.setOnCircleClickListener(this@GoogleMapsViewImpl)
130
- googleMap?.setOnMapClickListener(this@GoogleMapsViewImpl)
131
- googleMap?.setOnMapLongClickListener(this@GoogleMapsViewImpl)
132
- googleMap?.setOnMarkerDragListener(this@GoogleMapsViewImpl)
133
- onMapLoaded?.invoke(true)
134
- }
135
- applyProps()
136
- initLocationCallbacks()
137
- onMapReady?.invoke(true)
138
177
  }
139
- }
140
178
 
141
- override fun onCameraMoveStarted(reason: Int) {
142
- lastSubmittedCameraPosition = null
143
- cameraMoveReason = reason
144
- val bounds = googleMap?.projection?.visibleRegion?.latLngBounds ?: return
145
- val cameraPosition = googleMap?.cameraPosition ?: return
146
-
147
- val isGesture = GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE == reason
148
-
149
- onCameraChangeStart?.invoke(
150
- bounds.toRnRegion(),
151
- cameraPosition.toRnCamera(),
152
- isGesture,
153
- )
154
- }
155
-
156
- override fun onCameraMove() {
157
- val bounds = googleMap?.projection?.visibleRegion?.latLngBounds ?: return
158
- val cameraPosition = googleMap?.cameraPosition ?: return
159
-
160
- if (cameraPosition == lastSubmittedCameraPosition) {
161
- return
179
+ override fun onCameraMove() =
180
+ onUi {
181
+ if (!loaded) return@onUi
182
+ val visibleRegion = googleMap?.projection?.visibleRegion ?: return@onUi
183
+ val cameraPosition = googleMap?.cameraPosition ?: return@onUi
184
+ val gesture = GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE == cameraMoveReason
185
+ onCameraChange?.invoke(
186
+ visibleRegion.toRnRegion(),
187
+ cameraPosition.toRnCamera(),
188
+ gesture,
189
+ )
162
190
  }
163
- lastSubmittedCameraPosition = cameraPosition
164
191
 
165
- val isGesture = GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE == cameraMoveReason
166
-
167
- onCameraChange?.invoke(
168
- bounds.toRnRegion(),
169
- cameraPosition.toRnCamera(),
170
- isGesture,
171
- )
172
- }
173
-
174
- override fun onCameraIdle() {
175
- val bounds = googleMap?.projection?.visibleRegion?.latLngBounds ?: return
176
- val cameraPosition = googleMap?.cameraPosition ?: return
177
-
178
- val isGesture = GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE == cameraMoveReason
179
-
180
- onCameraChangeComplete?.invoke(
181
- bounds.toRnRegion(),
182
- cameraPosition.toRnCamera(),
183
- isGesture,
184
- )
185
- }
192
+ override fun onCameraIdle() =
193
+ onUi {
194
+ if (!loaded) return@onUi
195
+ val visibleRegion = googleMap?.projection?.visibleRegion ?: return@onUi
196
+ val cameraPosition = googleMap?.cameraPosition ?: return@onUi
197
+ val gesture = GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE == cameraMoveReason
198
+ onCameraChangeComplete?.invoke(
199
+ visibleRegion.toRnRegion(),
200
+ cameraPosition.toRnCamera(),
201
+ gesture,
202
+ )
203
+ }
186
204
 
187
205
  fun initLocationCallbacks() {
188
206
  locationHandler.onUpdate = { location ->
189
- onLocationUpdate?.invoke(location.toRnLocation())
207
+ onUi { onLocationUpdate?.invoke(location.toRnLocation()) }
190
208
  }
191
-
192
209
  locationHandler.onError = { error ->
193
- onLocationError?.invoke(error)
210
+ onUi { onLocationError?.invoke(error) }
194
211
  }
195
212
  locationHandler.start()
196
213
  }
@@ -209,50 +226,37 @@ class GoogleMapsViewImpl(
209
226
  locationConfig = locationConfig
210
227
 
211
228
  if (pendingMarkers.isNotEmpty()) {
212
- pendingMarkers.forEach { (id, opts) ->
213
- internalAddMarker(id, opts)
214
- }
229
+ pendingMarkers.forEach { (id, opts, markerTag) -> internalAddMarker(id, opts, markerTag) }
215
230
  pendingMarkers.clear()
216
231
  }
217
-
218
232
  if (pendingPolylines.isNotEmpty()) {
219
- pendingPolylines.forEach { (id, opts) ->
220
- internalAddPolyline(id, opts)
221
- }
233
+ pendingPolylines.forEach { (id, opts) -> internalAddPolyline(id, opts) }
222
234
  pendingPolylines.clear()
223
235
  }
224
-
225
236
  if (pendingPolygons.isNotEmpty()) {
226
- pendingPolygons.forEach { (id, opts) ->
227
- internalAddPolygon(id, opts)
228
- }
237
+ pendingPolygons.forEach { (id, opts) -> internalAddPolygon(id, opts) }
229
238
  pendingPolygons.clear()
230
239
  }
231
-
232
240
  if (pendingCircles.isNotEmpty()) {
233
- pendingCircles.forEach { (id, opts) ->
234
- internalAddCircle(id, opts)
235
- }
241
+ pendingCircles.forEach { (id, opts) -> internalAddCircle(id, opts) }
236
242
  pendingCircles.clear()
237
243
  }
238
-
239
244
  if (pendingHeatmaps.isNotEmpty()) {
240
- pendingHeatmaps.forEach { (id, opts) ->
241
- internalAddHeatmap(id, opts)
242
- }
245
+ pendingHeatmaps.forEach { (id, opts) -> internalAddHeatmap(id, opts) }
243
246
  pendingHeatmaps.clear()
244
247
  }
245
-
246
248
  if (pendingKmlLayers.isNotEmpty()) {
247
- pendingKmlLayers.forEach { (id, string) ->
248
- internalAddKmlLayer(id, string)
249
- }
249
+ pendingKmlLayers.forEach { (id, str) -> internalAddKmlLayer(id, str) }
250
250
  pendingKmlLayers.clear()
251
251
  }
252
+ if (pendingUrlTilesOverlays.isNotEmpty()) {
253
+ pendingUrlTilesOverlays.forEach { (id, opts) -> internalAddUrlTileOverlay(id, opts) }
254
+ pendingUrlTilesOverlays.clear()
255
+ }
252
256
  }
253
257
 
254
258
  val currentCamera: CameraPosition?
255
- get() = googleMap?.cameraPosition
259
+ get() = onUiSync { googleMap?.cameraPosition }
256
260
 
257
261
  var initialProps: RNInitialProps? = null
258
262
 
@@ -288,8 +292,8 @@ class GoogleMapsViewImpl(
288
292
  onUi {
289
293
  try {
290
294
  googleMap?.isMyLocationEnabled = value ?: false
291
- } catch (se: SecurityException) {
292
- onLocationError?.invoke(RNLocationErrorCode.PERMISSION_DENIED)
295
+ } catch (_: SecurityException) {
296
+ onLocationError?.let { cb -> cb(RNLocationErrorCode.PERMISSION_DENIED) }
293
297
  } catch (ex: Exception) {
294
298
  val error = ex.toLocationErrorCode(context)
295
299
  onLocationError?.invoke(error)
@@ -300,41 +304,31 @@ class GoogleMapsViewImpl(
300
304
  var buildingEnabled: Boolean? = null
301
305
  set(value) {
302
306
  field = value
303
- onUi {
304
- googleMap?.isBuildingsEnabled = value ?: false
305
- }
307
+ onUi { googleMap?.isBuildingsEnabled = value ?: false }
306
308
  }
307
309
 
308
310
  var trafficEnabled: Boolean? = null
309
311
  set(value) {
310
312
  field = value
311
- onUi {
312
- googleMap?.isTrafficEnabled = value ?: false
313
- }
313
+ onUi { googleMap?.isTrafficEnabled = value ?: false }
314
314
  }
315
315
 
316
316
  var indoorEnabled: Boolean? = null
317
317
  set(value) {
318
318
  field = value
319
- onUi {
320
- googleMap?.isIndoorEnabled = value ?: false
321
- }
319
+ onUi { googleMap?.isIndoorEnabled = value ?: false }
322
320
  }
323
321
 
324
322
  var customMapStyle: MapStyleOptions? = null
325
323
  set(value) {
326
324
  field = value
327
- onUi {
328
- googleMap?.setMapStyle(value)
329
- }
325
+ onUi { googleMap?.setMapStyle(value) }
330
326
  }
331
327
 
332
328
  var userInterfaceStyle: Int? = null
333
329
  set(value) {
334
330
  field = value
335
- onUi {
336
- googleMap?.mapColorScheme = value ?: MapColorScheme.FOLLOW_SYSTEM
337
- }
331
+ onUi { googleMap?.mapColorScheme = value ?: MapColorScheme.FOLLOW_SYSTEM }
338
332
  }
339
333
 
340
334
  var mapZoomConfig: RNMapZoomConfig? = null
@@ -362,9 +356,7 @@ class GoogleMapsViewImpl(
362
356
  var mapType: Int? = null
363
357
  set(value) {
364
358
  field = value
365
- onUi {
366
- googleMap?.mapType = value ?: 1
367
- }
359
+ onUi { googleMap?.mapType = value ?: 1 }
368
360
  }
369
361
 
370
362
  var locationConfig: RNLocationConfig? = null
@@ -379,37 +371,52 @@ class GoogleMapsViewImpl(
379
371
 
380
372
  var onMapError: ((RNMapErrorCode) -> Unit)? = null
381
373
  var onMapReady: ((Boolean) -> Unit)? = null
382
- var onMapLoaded: ((Boolean) -> Unit)? = null
374
+ var onMapLoaded: ((RNRegion, RNCamera) -> Unit)? = null
383
375
  var onLocationUpdate: ((RNLocation) -> Unit)? = null
384
376
  var onLocationError: ((RNLocationErrorCode) -> Unit)? = null
385
377
  var onMapPress: ((RNLatLng) -> Unit)? = null
386
378
  var onMapLongPress: ((RNLatLng) -> Unit)? = null
387
- var onMarkerPress: ((String?) -> Unit)? = null
388
- var onPolylinePress: ((String?) -> Unit)? = null
389
- var onPolygonPress: ((String?) -> Unit)? = null
390
- var onCirclePress: ((String?) -> Unit)? = null
391
- var onMarkerDragStart: ((String?, RNLatLng) -> Unit)? = null
392
- var onMarkerDrag: ((String?, RNLatLng) -> Unit)? = null
393
- var onMarkerDragEnd: ((String?, RNLatLng) -> Unit)? = null
379
+ var onPoiPress: ((String, String, RNLatLng) -> Unit)? = null
380
+ var onMarkerPress: ((String) -> Unit)? = null
381
+ var onPolylinePress: ((String) -> Unit)? = null
382
+ var onPolygonPress: ((String) -> Unit)? = null
383
+ var onCirclePress: ((String) -> Unit)? = null
384
+ var onMarkerDragStart: ((String, RNLatLng) -> Unit)? = null
385
+ var onMarkerDrag: ((String, RNLatLng) -> Unit)? = null
386
+ var onMarkerDragEnd: ((String, RNLatLng) -> Unit)? = null
394
387
  var onIndoorBuildingFocused: ((RNIndoorBuilding) -> Unit)? = null
395
388
  var onIndoorLevelActivated: ((RNIndoorLevel) -> Unit)? = null
389
+ var onInfoWindowPress: ((String) -> Unit)? = null
390
+ var onInfoWindowClose: ((String) -> Unit)? = null
391
+ var onInfoWindowLongPress: ((String) -> Unit)? = null
392
+ var onMyLocationPress: ((RNLocation) -> Unit)? = null
393
+ var onMyLocationButtonPress: ((Boolean) -> Unit)? = null
396
394
  var onCameraChangeStart: ((RNRegion, RNCamera, Boolean) -> Unit)? = null
397
395
  var onCameraChange: ((RNRegion, RNCamera, Boolean) -> Unit)? = null
398
396
  var onCameraChangeComplete: ((RNRegion, RNCamera, Boolean) -> Unit)? = null
399
397
 
398
+ fun showMarkerInfoWindow(id: String) =
399
+ onUi {
400
+ val marker = markersById[id] ?: return@onUi
401
+ marker.showInfoWindow()
402
+ }
403
+
404
+ fun hideMarkerInfoWindow(id: String) =
405
+ onUi {
406
+ val marker = markersById[id] ?: return@onUi
407
+ marker.hideInfoWindow()
408
+ }
409
+
400
410
  fun setCamera(
401
411
  cameraPosition: CameraPosition,
402
412
  animated: Boolean,
403
413
  durationMs: Int,
404
- ) {
405
- onUi {
406
- val update = CameraUpdateFactory.newCameraPosition(cameraPosition)
407
-
408
- if (animated) {
409
- googleMap?.animateCamera(update, durationMs, null)
410
- } else {
411
- googleMap?.moveCamera(update)
412
- }
414
+ ) = onUi {
415
+ val update = CameraUpdateFactory.newCameraPosition(cameraPosition)
416
+ if (animated) {
417
+ googleMap?.animateCamera(update, durationMs, null)
418
+ } else {
419
+ googleMap?.moveCamera(update)
413
420
  }
414
421
  }
415
422
 
@@ -418,93 +425,81 @@ class GoogleMapsViewImpl(
418
425
  padding: RNMapPadding,
419
426
  animated: Boolean,
420
427
  durationMs: Int,
421
- ) {
422
- if (coordinates.isEmpty()) {
423
- return
424
- }
425
- onUi {
426
- val builder = LatLngBounds.Builder()
427
- coordinates.forEach { coord ->
428
- builder.include(coord.toLatLng())
429
- }
430
- val bounds = builder.build()
431
-
432
- val latSpan = bounds.northeast.latitude - bounds.southwest.latitude
433
- val lngSpan = bounds.northeast.longitude - bounds.southwest.longitude
434
-
435
- val latPerPixel = latSpan / (mapView?.height ?: 0)
436
- val lngPerPixel = lngSpan / (mapView?.width ?: 0)
428
+ ) = onUi {
429
+ if (coordinates.isEmpty()) return@onUi
430
+ val builder = LatLngBounds.Builder()
431
+ coordinates.forEach { coord -> builder.include(coord.toLatLng()) }
432
+ val bounds = builder.build()
433
+
434
+ val latSpan = bounds.northeast.latitude - bounds.southwest.latitude
435
+ val lngSpan = bounds.northeast.longitude - bounds.southwest.longitude
436
+
437
+ val h = (mapView?.height ?: 0)
438
+ val w = (mapView?.width ?: 0)
439
+ val latPerPixel = if (h != 0) latSpan / h else 0.0
440
+ val lngPerPixel = if (w != 0) lngSpan / w else 0.0
441
+
442
+ builder.include(
443
+ LatLng(
444
+ bounds.northeast.latitude + (padding.top.dpToPx() * latPerPixel),
445
+ bounds.northeast.longitude,
446
+ ),
447
+ )
448
+ builder.include(
449
+ LatLng(
450
+ bounds.southwest.latitude - (padding.bottom.dpToPx() * latPerPixel),
451
+ bounds.southwest.longitude,
452
+ ),
453
+ )
454
+ builder.include(
455
+ LatLng(
456
+ bounds.northeast.latitude,
457
+ bounds.northeast.longitude + (padding.right.dpToPx() * lngPerPixel),
458
+ ),
459
+ )
460
+ builder.include(
461
+ LatLng(
462
+ bounds.southwest.latitude,
463
+ bounds.southwest.longitude - (padding.left.dpToPx() * lngPerPixel),
464
+ ),
465
+ )
437
466
 
438
- builder.include(
439
- LatLng(
440
- bounds.northeast.latitude + (padding.top.dpToPx() * latPerPixel),
441
- bounds.northeast.longitude,
442
- ),
443
- )
444
- builder.include(
445
- LatLng(
446
- bounds.southwest.latitude - (padding.bottom.dpToPx() * latPerPixel),
447
- bounds.southwest.longitude,
448
- ),
449
- )
450
- builder.include(
451
- LatLng(
452
- bounds.northeast.latitude,
453
- bounds.northeast.longitude + (padding.right.dpToPx() * lngPerPixel),
454
- ),
467
+ val paddedBounds = builder.build()
468
+ val adjustedWidth =
469
+ (w - padding.left.dpToPx() - padding.right.dpToPx()).toInt().coerceAtLeast(0)
470
+ val adjustedHeight =
471
+ (h - padding.top.dpToPx() - padding.bottom.dpToPx()).toInt().coerceAtLeast(0)
472
+
473
+ val update =
474
+ CameraUpdateFactory.newLatLngBounds(
475
+ paddedBounds,
476
+ adjustedWidth,
477
+ adjustedHeight,
478
+ 0,
455
479
  )
456
- builder.include(
457
- LatLng(
458
- bounds.southwest.latitude,
459
- bounds.southwest.longitude - (padding.left.dpToPx() * lngPerPixel),
460
- ),
461
- )
462
-
463
- val paddedBounds = builder.build()
464
-
465
- val adjustedWidth =
466
- ((mapView?.width ?: 0) - padding.left.dpToPx() - padding.right.dpToPx()).toInt()
467
- val adjustedHeight =
468
- ((mapView?.height ?: 0) - padding.top.dpToPx() - padding.bottom.dpToPx()).toInt()
469
-
470
- val update =
471
- CameraUpdateFactory.newLatLngBounds(
472
- paddedBounds,
473
- adjustedWidth,
474
- adjustedHeight,
475
- 0,
476
- )
477
- if (animated) {
478
- googleMap?.animateCamera(update, durationMs, null)
479
- } else {
480
- googleMap?.moveCamera(update)
481
- }
480
+ if (animated) {
481
+ googleMap?.animateCamera(update, durationMs, null)
482
+ } else {
483
+ googleMap?.moveCamera(update)
482
484
  }
483
485
  }
484
486
 
485
- fun setCameraBounds(bounds: LatLngBounds?) {
487
+ fun setCameraBounds(bounds: LatLngBounds?) =
486
488
  onUi {
487
489
  googleMap?.setLatLngBoundsForCameraTarget(bounds)
488
490
  }
489
- }
490
491
 
491
492
  fun animateToBounds(
492
493
  bounds: LatLngBounds,
493
494
  padding: Int,
494
495
  durationMs: Int,
495
496
  lockBounds: Boolean,
496
- ) {
497
- onUi {
498
- if (lockBounds) {
499
- googleMap?.setLatLngBoundsForCameraTarget(bounds)
500
- }
501
- val update =
502
- CameraUpdateFactory.newLatLngBounds(
503
- bounds,
504
- padding,
505
- )
506
- googleMap?.animateCamera(update, durationMs, null)
497
+ ) = onUi {
498
+ if (lockBounds) {
499
+ googleMap?.setLatLngBoundsForCameraTarget(bounds)
507
500
  }
501
+ val update = CameraUpdateFactory.newLatLngBounds(bounds, padding)
502
+ googleMap?.animateCamera(update, durationMs, null)
508
503
  }
509
504
 
510
505
  fun snapshot(
@@ -522,12 +517,7 @@ class GoogleMapsViewImpl(
522
517
  promise.resolve(null)
523
518
  return@snapshot
524
519
  }
525
-
526
- val scaledBitmap =
527
- size?.let {
528
- bitmap.scale(it.width, it.height)
529
- } ?: bitmap
530
-
520
+ val scaledBitmap = size?.let { bitmap.scale(it.width, it.height) } ?: bitmap
531
521
  val output = ByteArrayOutputStream()
532
522
  scaledBitmap.compress(compressFormat, (quality * 100).toInt().coerceIn(0, 100), output)
533
523
  val bytes = output.toByteArray()
@@ -541,325 +531,310 @@ class GoogleMapsViewImpl(
541
531
  promise.resolve("data:image/$format;base64,$base64")
542
532
  }
543
533
 
544
- if (scaledBitmap != bitmap) {
545
- scaledBitmap.recycle()
546
- }
534
+ if (scaledBitmap !== bitmap) scaledBitmap.recycle()
547
535
  bitmap.recycle()
548
- } catch (e: Exception) {
536
+ } catch (_: Exception) {
549
537
  promise.resolve(null)
550
538
  }
551
539
  }
552
540
  }
553
-
554
541
  return promise
555
542
  }
556
543
 
557
544
  fun addMarker(
558
545
  id: String,
559
546
  opts: MarkerOptions,
560
- ) {
547
+ markerTag: MarkerTag,
548
+ ) = onUi {
561
549
  if (googleMap == null) {
562
- pendingMarkers.add(id to opts)
563
- return
550
+ pendingMarkers.add(Triple(id, opts, markerTag))
551
+ return@onUi
564
552
  }
565
553
 
566
- onUi {
567
- markersById.remove(id)?.remove()
568
- }
569
- internalAddMarker(id, opts)
554
+ markersById.remove(id)?.remove()
555
+ internalAddMarker(id, opts, markerTag)
570
556
  }
571
557
 
572
558
  private fun internalAddMarker(
573
559
  id: String,
574
560
  opts: MarkerOptions,
575
- ) {
576
- onUi {
577
- val marker =
578
- googleMap?.addMarker(opts).also {
579
- it?.tag = id
580
- }
581
- if (marker != null) {
582
- markersById[id] = marker
561
+ markerTag: MarkerTag,
562
+ ) = onUi {
563
+ val marker =
564
+ googleMap?.addMarker(opts)?.apply {
565
+ tag = markerTag
583
566
  }
567
+
568
+ if (marker != null) {
569
+ markersById[id] = marker
584
570
  }
585
571
  }
586
572
 
587
573
  fun updateMarker(
588
574
  id: String,
589
575
  block: (Marker) -> Unit,
590
- ) {
591
- val marker = markersById[id] ?: return
592
- onUi {
593
- block(marker)
576
+ ) = onUi {
577
+ val marker = markersById[id] ?: return@onUi
578
+ block(marker)
579
+ if (marker.isInfoWindowShown) {
580
+ marker.hideInfoWindow()
581
+ marker.showInfoWindow()
594
582
  }
595
583
  }
596
584
 
597
- fun removeMarker(id: String) {
585
+ fun removeMarker(id: String) =
598
586
  onUi {
599
- val marker = markersById.remove(id)
600
- marker?.remove()
587
+ markersById.remove(id)?.remove()
601
588
  }
602
- }
603
589
 
604
- fun clearMarkers() {
590
+ fun clearMarkers() =
605
591
  onUi {
606
592
  markersById.values.forEach { it.remove() }
593
+ markersById.clear()
594
+ pendingMarkers.clear()
607
595
  }
608
- markersById.clear()
609
- pendingMarkers.clear()
610
- }
611
596
 
612
597
  fun addPolyline(
613
598
  id: String,
614
599
  opts: PolylineOptions,
615
- ) {
600
+ ) = onUi {
616
601
  if (googleMap == null) {
617
602
  pendingPolylines.add(id to opts)
618
- return
619
- }
620
- onUi {
621
- polylinesById.remove(id)?.remove()
603
+ return@onUi
622
604
  }
605
+ polylinesById.remove(id)?.remove()
623
606
  internalAddPolyline(id, opts)
624
607
  }
625
608
 
626
609
  private fun internalAddPolyline(
627
610
  id: String,
628
611
  opts: PolylineOptions,
629
- ) {
630
- onUi {
631
- val polyline =
632
- googleMap?.addPolyline(opts).also {
633
- it?.tag = id
634
- }
635
- if (polyline != null) {
636
- polylinesById[id] = polyline
612
+ ) = onUi {
613
+ val pl =
614
+ googleMap?.addPolyline(opts).also {
615
+ it?.tag = PolylineTag(id = id)
637
616
  }
638
- }
617
+ if (pl != null) polylinesById[id] = pl
639
618
  }
640
619
 
641
620
  fun updatePolyline(
642
621
  id: String,
643
622
  block: (Polyline) -> Unit,
644
- ) {
645
- val pl = polylinesById[id] ?: return
646
- onUi {
647
- block(pl)
648
- }
623
+ ) = onUi {
624
+ val pl = polylinesById[id] ?: return@onUi
625
+ block(pl)
649
626
  }
650
627
 
651
- fun removePolyline(id: String) {
628
+ fun removePolyline(id: String) =
652
629
  onUi {
653
630
  polylinesById.remove(id)?.remove()
654
631
  }
655
- }
656
632
 
657
- fun clearPolylines() {
633
+ fun clearPolylines() =
658
634
  onUi {
659
635
  polylinesById.values.forEach { it.remove() }
636
+ polylinesById.clear()
637
+ pendingPolylines.clear()
660
638
  }
661
- polylinesById.clear()
662
- pendingPolylines.clear()
663
- }
664
639
 
665
640
  fun addPolygon(
666
641
  id: String,
667
642
  opts: PolygonOptions,
668
- ) {
643
+ ) = onUi {
669
644
  if (googleMap == null) {
670
645
  pendingPolygons.add(id to opts)
671
- return
672
- }
673
-
674
- onUi {
675
- polygonsById.remove(id)?.remove()
646
+ return@onUi
676
647
  }
648
+ polygonsById.remove(id)?.remove()
677
649
  internalAddPolygon(id, opts)
678
650
  }
679
651
 
680
652
  private fun internalAddPolygon(
681
653
  id: String,
682
654
  opts: PolygonOptions,
683
- ) {
684
- onUi {
685
- val polygon =
686
- googleMap?.addPolygon(opts).also {
687
- it?.tag = id
688
- }
689
- if (polygon != null) {
690
- polygonsById[id] = polygon
655
+ ) = onUi {
656
+ val polygon =
657
+ googleMap?.addPolygon(opts).also {
658
+ it?.tag = PolygonTag(id = id)
691
659
  }
692
- }
660
+ if (polygon != null) polygonsById[id] = polygon
693
661
  }
694
662
 
695
663
  fun updatePolygon(
696
664
  id: String,
697
665
  block: (Polygon) -> Unit,
698
- ) {
699
- val polygon = polygonsById[id] ?: return
700
- onUi {
701
- block(polygon)
702
- }
666
+ ) = onUi {
667
+ val polygon = polygonsById[id] ?: return@onUi
668
+ block(polygon)
703
669
  }
704
670
 
705
- fun removePolygon(id: String) {
671
+ fun removePolygon(id: String) =
706
672
  onUi {
707
673
  polygonsById.remove(id)?.remove()
708
674
  }
709
- }
710
675
 
711
- fun clearPolygons() {
676
+ fun clearPolygons() =
712
677
  onUi {
713
678
  polygonsById.values.forEach { it.remove() }
679
+ polygonsById.clear()
680
+ pendingPolygons.clear()
714
681
  }
715
- polygonsById.clear()
716
- pendingPolygons.clear()
717
- }
718
682
 
719
683
  fun addCircle(
720
684
  id: String,
721
685
  opts: CircleOptions,
722
- ) {
686
+ ) = onUi {
723
687
  if (googleMap == null) {
724
688
  pendingCircles.add(id to opts)
725
- return
726
- }
727
-
728
- onUi {
729
- circlesById.remove(id)?.remove()
689
+ return@onUi
730
690
  }
691
+ circlesById.remove(id)?.remove()
731
692
  internalAddCircle(id, opts)
732
693
  }
733
694
 
734
695
  private fun internalAddCircle(
735
696
  id: String,
736
697
  opts: CircleOptions,
737
- ) {
738
- onUi {
739
- val circle =
740
- googleMap?.addCircle(opts).also {
741
- it?.tag = id
742
- }
743
- if (circle != null) {
744
- circlesById[id] = circle
698
+ ) = onUi {
699
+ val circle =
700
+ googleMap?.addCircle(opts).also {
701
+ it?.tag = CircleTag(id = id)
745
702
  }
746
- }
703
+ if (circle != null) circlesById[id] = circle
747
704
  }
748
705
 
749
706
  fun updateCircle(
750
707
  id: String,
751
708
  block: (Circle) -> Unit,
752
- ) {
753
- val circle = circlesById[id] ?: return
754
- onUi {
755
- block(circle)
756
- }
709
+ ) = onUi {
710
+ val circle = circlesById[id] ?: return@onUi
711
+ block(circle)
757
712
  }
758
713
 
759
- fun removeCircle(id: String) {
714
+ fun removeCircle(id: String) =
760
715
  onUi {
761
716
  circlesById.remove(id)?.remove()
762
717
  }
763
- }
764
718
 
765
- fun clearCircles() {
719
+ fun clearCircles() =
766
720
  onUi {
767
721
  circlesById.values.forEach { it.remove() }
722
+ circlesById.clear()
723
+ pendingCircles.clear()
768
724
  }
769
- circlesById.clear()
770
- pendingCircles.clear()
771
- }
772
725
 
773
726
  fun addHeatmap(
774
727
  id: String,
775
728
  opts: TileOverlayOptions,
776
- ) {
729
+ ) = onUi {
777
730
  if (googleMap == null) {
778
731
  pendingHeatmaps.add(id to opts)
779
- return
780
- }
781
-
782
- onUi {
783
- heatmapsById.remove(id)?.remove()
732
+ return@onUi
784
733
  }
734
+ heatmapsById.remove(id)?.remove()
785
735
  internalAddHeatmap(id, opts)
786
736
  }
787
737
 
788
738
  private fun internalAddHeatmap(
789
739
  id: String,
790
740
  opts: TileOverlayOptions,
791
- ) {
792
- onUi {
793
- val heatmap =
794
- googleMap?.addTileOverlay(opts)
795
- if (heatmap != null) {
796
- heatmapsById[id] = heatmap
797
- }
798
- }
741
+ ) = onUi {
742
+ val overlay = googleMap?.addTileOverlay(opts)
743
+ if (overlay != null) heatmapsById[id] = overlay
799
744
  }
800
745
 
801
- fun removeHeatmap(id: String) {
746
+ fun removeHeatmap(id: String) =
802
747
  onUi {
803
- heatmapsById.remove(id)?.remove()
748
+ heatmapsById.remove(id)?.let { heatMap ->
749
+ heatMap.clearTileCache()
750
+ heatMap.remove()
751
+ }
804
752
  }
805
- }
806
753
 
807
- fun clearHeatmaps() {
754
+ fun clearHeatmaps() =
808
755
  onUi {
809
756
  heatmapsById.values.forEach { it.remove() }
757
+ heatmapsById.clear()
758
+ pendingHeatmaps.clear()
810
759
  }
811
- heatmapsById.clear()
812
- pendingHeatmaps.clear()
813
- }
814
760
 
815
761
  fun addKmlLayer(
816
762
  id: String,
817
763
  kmlString: String,
818
- ) {
764
+ ) = onUi {
819
765
  if (googleMap == null) {
820
766
  pendingKmlLayers.add(id to kmlString)
821
- return
822
- }
823
- onUi {
824
- kmlLayersById.remove(id)?.removeLayerFromMap()
767
+ return@onUi
825
768
  }
769
+ kmlLayersById.remove(id)?.removeLayerFromMap()
826
770
  internalAddKmlLayer(id, kmlString)
827
771
  }
828
772
 
829
773
  private fun internalAddKmlLayer(
830
774
  id: String,
831
775
  kmlString: String,
832
- ) {
833
- onUi {
834
- try {
835
- val inputStream = ByteArrayInputStream(kmlString.toByteArray(StandardCharsets.UTF_8))
836
- val layer = KmlLayer(googleMap, inputStream, context)
837
- kmlLayersById[id] = layer
838
- layer.addLayerToMap()
839
- } catch (e: Exception) {
840
- // / ignore
841
- }
776
+ ) = onUi {
777
+ try {
778
+ val inputStream = ByteArrayInputStream(kmlString.toByteArray(StandardCharsets.UTF_8))
779
+ val layer = KmlLayer(googleMap, inputStream, context)
780
+ kmlLayersById[id] = layer
781
+ layer.addLayerToMap()
782
+ } catch (_: Exception) {
783
+ // ignore
842
784
  }
843
785
  }
844
786
 
845
- fun removeKmlLayer(id: String) {
787
+ fun removeKmlLayer(id: String) =
846
788
  onUi {
847
789
  kmlLayersById.remove(id)?.removeLayerFromMap()
848
790
  }
849
- }
850
791
 
851
- fun clearKmlLayer() {
792
+ fun clearKmlLayer() =
852
793
  onUi {
853
794
  kmlLayersById.values.forEach { it.removeLayerFromMap() }
795
+ kmlLayersById.clear()
796
+ pendingKmlLayers.clear()
797
+ }
798
+
799
+ fun addUrlTileOverlay(
800
+ id: String,
801
+ opts: TileOverlayOptions,
802
+ ) = onUi {
803
+ if (googleMap == null) {
804
+ pendingUrlTilesOverlays.add(id to opts)
805
+ return@onUi
854
806
  }
855
- kmlLayersById.clear()
856
- pendingKmlLayers.clear()
807
+ urlTileOverlaysById.remove(id)?.remove()
808
+ internalAddUrlTileOverlay(id, opts)
857
809
  }
858
810
 
859
- fun destroyInternal() {
860
- if (destroyed) return
861
- destroyed = true
811
+ private fun internalAddUrlTileOverlay(
812
+ id: String,
813
+ opts: TileOverlayOptions,
814
+ ) = onUi {
815
+ val overlay = googleMap?.addTileOverlay(opts)
816
+ if (overlay != null) urlTileOverlaysById[id] = overlay
817
+ }
818
+
819
+ fun removeUrlTileOverlay(id: String) =
820
+ onUi {
821
+ urlTileOverlaysById.remove(id)?.let { urlTileOverlay ->
822
+ urlTileOverlay.clearTileCache()
823
+ urlTileOverlay.remove()
824
+ }
825
+ }
826
+
827
+ fun clearUrlTileOverlays() =
828
+ onUi {
829
+ urlTileOverlaysById.values.forEach { it.remove() }
830
+ urlTileOverlaysById.clear()
831
+ pendingUrlTilesOverlays.clear()
832
+ }
833
+
834
+ fun destroyInternal() =
862
835
  onUi {
836
+ if (destroyed) return@onUi
837
+ destroyed = true
863
838
  locationHandler.stop()
864
839
  markerBuilder.cancelAllJobs()
865
840
  clearMarkers()
@@ -868,6 +843,7 @@ class GoogleMapsViewImpl(
868
843
  clearCircles()
869
844
  clearHeatmaps()
870
845
  clearKmlLayer()
846
+ clearUrlTileOverlays()
871
847
  googleMap?.apply {
872
848
  setOnCameraMoveStartedListener(null)
873
849
  setOnCameraMoveListener(null)
@@ -878,7 +854,14 @@ class GoogleMapsViewImpl(
878
854
  setOnCircleClickListener(null)
879
855
  setOnMapClickListener(null)
880
856
  setOnMapLongClickListener(null)
857
+ setOnPoiClickListener(null)
881
858
  setOnMarkerDragListener(null)
859
+ setOnInfoWindowClickListener(null)
860
+ setOnInfoWindowCloseListener(null)
861
+ setOnInfoWindowLongClickListener(null)
862
+ setOnMyLocationClickListener(null)
863
+ setOnMyLocationButtonClickListener(null)
864
+ setInfoWindowAdapter(null)
882
865
  }
883
866
  googleMap = null
884
867
  mapView?.apply {
@@ -891,7 +874,6 @@ class GoogleMapsViewImpl(
891
874
  reactContext.removeLifecycleEventListener(this)
892
875
  initialized = false
893
876
  }
894
- }
895
877
 
896
878
  override fun requestLayout() {
897
879
  super.requestLayout()
@@ -905,105 +887,129 @@ class GoogleMapsViewImpl(
905
887
  }
906
888
  }
907
889
 
908
- override fun onAttachedToWindow() {
909
- super.onAttachedToWindow()
910
- locationHandler.start()
911
- }
890
+ override fun onAttachedToWindow() =
891
+ onUi {
892
+ super.onAttachedToWindow()
893
+ locationHandler.start()
894
+ }
912
895
 
913
- override fun onDetachedFromWindow() {
914
- super.onDetachedFromWindow()
915
- locationHandler.stop()
916
- }
896
+ override fun onDetachedFromWindow() =
897
+ onUi {
898
+ super.onDetachedFromWindow()
899
+ locationHandler.stop()
900
+ }
917
901
 
918
- override fun onHostResume() {
902
+ override fun onHostResume() =
919
903
  onUi {
920
904
  locationHandler.start()
921
905
  mapView?.onResume()
922
906
  }
923
- }
924
907
 
925
- override fun onHostPause() {
908
+ override fun onHostPause() =
926
909
  onUi {
927
910
  locationHandler.stop()
928
911
  mapView?.onPause()
929
912
  }
930
- }
931
913
 
932
914
  override fun onHostDestroy() {
933
915
  destroyInternal()
934
916
  }
935
917
 
936
918
  override fun onMarkerClick(marker: Marker): Boolean {
937
- marker.showInfoWindow()
938
- onMarkerPress?.invoke(marker.tag?.toString())
939
- return true
919
+ onUi {
920
+ onMarkerPress?.invoke(marker.idTag)
921
+ }
922
+ return uiSettings?.consumeOnMarkerPress ?: false
940
923
  }
941
924
 
942
- override fun onPolylineClick(polyline: Polyline) {
943
- onPolylinePress?.invoke(polyline.tag?.toString())
944
- }
925
+ override fun onPolylineClick(polyline: Polyline) =
926
+ onUi {
927
+ onPolylinePress?.invoke(polyline.idTag)
928
+ }
945
929
 
946
- override fun onPolygonClick(polygon: Polygon) {
947
- onPolygonPress?.invoke(polygon.tag?.toString())
948
- }
930
+ override fun onPolygonClick(polygon: Polygon) =
931
+ onUi {
932
+ onPolygonPress?.invoke(polygon.idTag)
933
+ }
949
934
 
950
- override fun onCircleClick(circle: Circle) {
951
- onCirclePress?.invoke(circle.tag?.toString())
952
- }
935
+ override fun onCircleClick(circle: Circle) =
936
+ onUi {
937
+ onCirclePress?.invoke(circle.idTag)
938
+ }
953
939
 
954
- override fun onMapClick(coordinates: LatLng) {
955
- onMapPress?.invoke(
956
- coordinates.toRnLatLng(),
957
- )
958
- }
940
+ override fun onMapClick(coordinates: LatLng) =
941
+ onUi {
942
+ onMapPress?.invoke(coordinates.toRnLatLng())
943
+ }
959
944
 
960
- override fun onMapLongClick(coordinates: LatLng) {
961
- onMapLongPress?.invoke(
962
- coordinates.toRnLatLng(),
963
- )
964
- }
945
+ override fun onMapLongClick(coordinates: LatLng) =
946
+ onUi {
947
+ onMapLongPress?.invoke(coordinates.toRnLatLng())
948
+ }
965
949
 
966
- override fun onMarkerDragStart(marker: Marker) {
967
- onMarkerDragStart?.invoke(
968
- marker.tag?.toString(),
969
- marker.position.toRnLatLng(),
970
- )
971
- }
950
+ override fun onMarkerDragStart(marker: Marker) =
951
+ onUi {
952
+ onMarkerDragStart?.invoke(marker.idTag, marker.position.toRnLatLng())
953
+ }
972
954
 
973
- override fun onMarkerDrag(marker: Marker) {
974
- onMarkerDrag?.invoke(
975
- marker.tag?.toString(),
976
- marker.position.toRnLatLng(),
977
- )
978
- }
955
+ override fun onMarkerDrag(marker: Marker) =
956
+ onUi {
957
+ onMarkerDrag?.invoke(marker.idTag, marker.position.toRnLatLng())
958
+ }
979
959
 
980
- override fun onMarkerDragEnd(marker: Marker) {
981
- onMarkerDragEnd?.invoke(
982
- marker.tag?.toString(),
983
- marker.position.toRnLatLng(),
984
- )
985
- }
960
+ override fun onMarkerDragEnd(marker: Marker) =
961
+ onUi {
962
+ onMarkerDragEnd?.invoke(marker.idTag, marker.position.toRnLatLng())
963
+ }
986
964
 
987
- override fun onIndoorBuildingFocused() {
988
- val building = googleMap?.focusedBuilding ?: return
989
- onIndoorBuildingFocused?.invoke(building.toRNIndoorBuilding())
990
- }
965
+ override fun onIndoorBuildingFocused() =
966
+ onUi {
967
+ val building = googleMap?.focusedBuilding ?: return@onUi
968
+ onIndoorBuildingFocused?.invoke(building.toRNIndoorBuilding())
969
+ }
991
970
 
992
- override fun onIndoorLevelActivated(indoorBuilding: IndoorBuilding) {
993
- val activeLevel = indoorBuilding.levels.getOrNull(indoorBuilding.activeLevelIndex) ?: return
994
- onIndoorLevelActivated?.invoke(
995
- activeLevel.toRNIndoorLevel(
996
- indoorBuilding.activeLevelIndex,
997
- true,
998
- ),
999
- )
1000
- }
1001
- }
971
+ override fun onIndoorLevelActivated(indoorBuilding: IndoorBuilding) =
972
+ onUi {
973
+ val activeLevel =
974
+ indoorBuilding.levels.getOrNull(indoorBuilding.activeLevelIndex) ?: return@onUi
975
+ onIndoorLevelActivated?.invoke(
976
+ activeLevel.toRNIndoorLevel(indoorBuilding.activeLevelIndex, true),
977
+ )
978
+ }
979
+
980
+ override fun onPoiClick(poi: PointOfInterest) =
981
+ onUi {
982
+ onPoiPress?.invoke(poi.placeId, poi.name, poi.latLng.toRnLatLng())
983
+ }
984
+
985
+ override fun onInfoWindowClick(marker: Marker) =
986
+ onUi {
987
+ onInfoWindowPress?.invoke(marker.idTag)
988
+ }
989
+
990
+ override fun onInfoWindowClose(marker: Marker) =
991
+ onUi {
992
+ onInfoWindowClose?.invoke(marker.idTag)
993
+ }
994
+
995
+ override fun onInfoWindowLongClick(marker: Marker) =
996
+ onUi {
997
+ onInfoWindowLongPress?.invoke(marker.idTag)
998
+ }
1002
999
 
1003
- private inline fun onUi(crossinline block: () -> Unit) {
1004
- if (UiThreadUtil.isOnUiThread()) {
1005
- block()
1006
- } else {
1007
- UiThreadUtil.runOnUiThread { block() }
1000
+ override fun onMyLocationClick(location: Location) =
1001
+ onUi {
1002
+ onMyLocationPress?.invoke(location.toRnLocation())
1003
+ }
1004
+
1005
+ override fun onMyLocationButtonClick(): Boolean {
1006
+ onUi {
1007
+ onMyLocationButtonPress?.invoke(true)
1008
+ }
1009
+ return uiSettings?.consumeOnMyLocationButtonPress ?: false
1008
1010
  }
1011
+
1012
+ override fun getInfoContents(marker: Marker): View? = null
1013
+
1014
+ override fun getInfoWindow(marker: Marker): View? = markerBuilder.buildInfoWindow(marker.tagData.iconSvg)
1009
1015
  }