react-native-google-maps-plus 1.6.2 → 1.7.0-dev.10

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