@lugg/maps 1.0.0-beta.4 → 1.0.0-beta.6

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 (104) hide show
  1. package/android/src/main/java/com/luggmaps/LuggCalloutView.kt +4 -2
  2. package/android/src/main/java/com/luggmaps/LuggCalloutViewManager.kt +4 -3
  3. package/android/src/main/java/com/luggmaps/LuggMapView.kt +40 -0
  4. package/android/src/main/java/com/luggmaps/LuggMapViewManager.kt +31 -0
  5. package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +23 -0
  6. package/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt +13 -0
  7. package/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt +166 -21
  8. package/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt +5 -0
  9. package/android/src/main/java/com/luggmaps/extensions/ViewExtensions.kt +11 -0
  10. package/ios/LuggCalloutView.h +1 -1
  11. package/ios/LuggCalloutView.mm +21 -11
  12. package/ios/LuggMapView.mm +41 -2
  13. package/ios/LuggMarkerView.h +3 -0
  14. package/ios/LuggMarkerView.mm +23 -0
  15. package/ios/core/AppleMapProvider.mm +236 -16
  16. package/ios/core/GoogleMapProvider.mm +91 -18
  17. package/ios/core/MapProviderDelegate.h +8 -1
  18. package/lib/module/MapProvider.web.js +4 -1
  19. package/lib/module/MapProvider.web.js.map +1 -1
  20. package/lib/module/MapView.js +12 -1
  21. package/lib/module/MapView.js.map +1 -1
  22. package/lib/module/MapView.web.js +21 -18
  23. package/lib/module/MapView.web.js.map +1 -1
  24. package/lib/module/components/Circle.web.js +3 -3
  25. package/lib/module/components/Circle.web.js.map +1 -1
  26. package/lib/module/components/GeoJson.js +3 -3
  27. package/lib/module/components/GeoJson.js.map +1 -1
  28. package/lib/module/components/GroundOverlay.web.js +3 -3
  29. package/lib/module/components/GroundOverlay.web.js.map +1 -1
  30. package/lib/module/components/Marker.js +17 -3
  31. package/lib/module/components/Marker.js.map +1 -1
  32. package/lib/module/components/Marker.web.js +23 -10
  33. package/lib/module/components/Marker.web.js.map +1 -1
  34. package/lib/module/components/Polygon.web.js +3 -3
  35. package/lib/module/components/Polygon.web.js.map +1 -1
  36. package/lib/module/components/Polyline.web.js +6 -12
  37. package/lib/module/components/Polyline.web.js.map +1 -1
  38. package/lib/module/components/TileOverlay.web.js +3 -3
  39. package/lib/module/components/TileOverlay.web.js.map +1 -1
  40. package/lib/module/components/index.js.map +1 -1
  41. package/lib/module/fabric/LuggCalloutViewNativeComponent.ts +1 -1
  42. package/lib/module/fabric/LuggMapViewNativeComponent.ts +9 -1
  43. package/lib/module/fabric/LuggMarkerViewNativeComponent.ts +14 -2
  44. package/lib/module/index.js.map +1 -1
  45. package/lib/typescript/src/MapProvider.web.d.ts +3 -2
  46. package/lib/typescript/src/MapProvider.web.d.ts.map +1 -1
  47. package/lib/typescript/src/MapView.d.ts +1 -1
  48. package/lib/typescript/src/MapView.d.ts.map +1 -1
  49. package/lib/typescript/src/MapView.types.d.ts +36 -1
  50. package/lib/typescript/src/MapView.types.d.ts.map +1 -1
  51. package/lib/typescript/src/MapView.web.d.ts +1 -1
  52. package/lib/typescript/src/MapView.web.d.ts.map +1 -1
  53. package/lib/typescript/src/components/Circle.web.d.ts +1 -1
  54. package/lib/typescript/src/components/Circle.web.d.ts.map +1 -1
  55. package/lib/typescript/src/components/GeoJson.d.ts +2 -1
  56. package/lib/typescript/src/components/GeoJson.d.ts.map +1 -1
  57. package/lib/typescript/src/components/GroundOverlay.web.d.ts +1 -1
  58. package/lib/typescript/src/components/GroundOverlay.web.d.ts.map +1 -1
  59. package/lib/typescript/src/components/Marker.d.ts +6 -3
  60. package/lib/typescript/src/components/Marker.d.ts.map +1 -1
  61. package/lib/typescript/src/components/Marker.types.d.ts +14 -5
  62. package/lib/typescript/src/components/Marker.types.d.ts.map +1 -1
  63. package/lib/typescript/src/components/Marker.web.d.ts +3 -2
  64. package/lib/typescript/src/components/Marker.web.d.ts.map +1 -1
  65. package/lib/typescript/src/components/Polygon.web.d.ts +1 -1
  66. package/lib/typescript/src/components/Polygon.web.d.ts.map +1 -1
  67. package/lib/typescript/src/components/Polyline.web.d.ts +1 -1
  68. package/lib/typescript/src/components/Polyline.web.d.ts.map +1 -1
  69. package/lib/typescript/src/components/TileOverlay.web.d.ts +1 -1
  70. package/lib/typescript/src/components/TileOverlay.web.d.ts.map +1 -1
  71. package/lib/typescript/src/components/index.d.ts +1 -1
  72. package/lib/typescript/src/components/index.d.ts.map +1 -1
  73. package/lib/typescript/src/fabric/LuggCalloutViewNativeComponent.d.ts +1 -1
  74. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts +6 -1
  75. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts.map +1 -1
  76. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts +8 -1
  77. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts.map +1 -1
  78. package/lib/typescript/src/index.d.ts +3 -3
  79. package/lib/typescript/src/index.d.ts.map +1 -1
  80. package/lib/typescript/src/index.web.d.ts +1 -1
  81. package/lib/typescript/src/index.web.d.ts.map +1 -1
  82. package/lib/typescript/src/types.d.ts +0 -4
  83. package/lib/typescript/src/types.d.ts.map +1 -1
  84. package/package.json +1 -1
  85. package/src/MapProvider.web.tsx +5 -3
  86. package/src/MapView.tsx +12 -1
  87. package/src/MapView.types.ts +118 -1
  88. package/src/MapView.web.tsx +378 -345
  89. package/src/components/Circle.web.tsx +81 -79
  90. package/src/components/GeoJson.tsx +3 -3
  91. package/src/components/GroundOverlay.web.tsx +65 -69
  92. package/src/components/Marker.tsx +28 -5
  93. package/src/components/Marker.types.ts +15 -5
  94. package/src/components/Marker.web.tsx +196 -150
  95. package/src/components/Polygon.web.tsx +93 -89
  96. package/src/components/Polyline.web.tsx +235 -235
  97. package/src/components/TileOverlay.web.tsx +83 -81
  98. package/src/components/index.ts +1 -0
  99. package/src/fabric/LuggCalloutViewNativeComponent.ts +1 -1
  100. package/src/fabric/LuggMapViewNativeComponent.ts +9 -1
  101. package/src/fabric/LuggMarkerViewNativeComponent.ts +14 -2
  102. package/src/index.ts +6 -1
  103. package/src/index.web.ts +3 -0
  104. package/src/types.ts +0 -5
@@ -6,17 +6,18 @@ import android.graphics.Canvas
6
6
  import android.view.View
7
7
  import androidx.core.graphics.createBitmap
8
8
  import androidx.core.view.isNotEmpty
9
+ import com.facebook.react.uimanager.PointerEvents
9
10
  import com.facebook.react.views.view.ReactViewGroup
10
11
 
11
12
  class LuggCalloutView(context: Context) : ReactViewGroup(context) {
12
13
  val contentView: ReactViewGroup = ReactViewGroup(context)
13
14
  var bubbled: Boolean = true
14
- var anchorX: Float = 0.5f
15
+ var offsetX: Float = 0f
15
16
  set(value) {
16
17
  field = value
17
18
  onUpdate?.invoke()
18
19
  }
19
- var anchorY: Float = 1.0f
20
+ var offsetY: Float = 0f
20
21
  set(value) {
21
22
  field = value
22
23
  onUpdate?.invoke()
@@ -28,6 +29,7 @@ class LuggCalloutView(context: Context) : ReactViewGroup(context) {
28
29
 
29
30
  init {
30
31
  visibility = GONE
32
+ pointerEvents = PointerEvents.NONE
31
33
  }
32
34
 
33
35
  override fun addView(child: View, index: Int) {
@@ -2,6 +2,7 @@ package com.luggmaps
2
2
 
3
3
  import com.facebook.react.bridge.ReadableMap
4
4
  import com.facebook.react.module.annotations.ReactModule
5
+ import com.facebook.react.uimanager.PixelUtil.dpToPx
5
6
  import com.facebook.react.uimanager.ThemedReactContext
6
7
  import com.facebook.react.uimanager.ViewGroupManager
7
8
  import com.facebook.react.uimanager.ViewManagerDelegate
@@ -22,9 +23,9 @@ class LuggCalloutViewManager :
22
23
  view.bubbled = value
23
24
  }
24
25
 
25
- override fun setAnchor(view: LuggCalloutView, value: ReadableMap?) {
26
- view.anchorX = value?.getDouble("x")?.toFloat() ?: 0.5f
27
- view.anchorY = value?.getDouble("y")?.toFloat() ?: 1.0f
26
+ override fun setOffset(view: LuggCalloutView, value: ReadableMap?) {
27
+ view.offsetX = value?.getDouble("x")?.toFloat()?.dpToPx() ?: 0f
28
+ view.offsetY = value?.getDouble("y")?.toFloat()?.dpToPx() ?: 0f
28
29
  }
29
30
 
30
31
  override fun onDropViewInstance(view: LuggCalloutView) {
@@ -58,6 +58,7 @@ class LuggMapView(private val reactContext: ThemedReactContext) :
58
58
  private var initialLatitude: Double = 37.78
59
59
  private var initialLongitude: Double = -122.43
60
60
  private var initialZoom: Float = 14f
61
+ private var mapType: String = "standard"
61
62
  private var theme: String = "system"
62
63
  private var zoomEnabled: Boolean = true
63
64
  private var scrollEnabled: Boolean = true
@@ -65,9 +66,13 @@ class LuggMapView(private val reactContext: ThemedReactContext) :
65
66
  private var pitchEnabled: Boolean = true
66
67
  private var userLocationEnabled: Boolean = false
67
68
  private var userLocationButtonEnabled: Boolean = false
69
+ private var poiEnabled: Boolean = true
70
+ private var poiFilterMode: String = "including"
71
+ private var poiFilterCategories: List<String> = emptyList()
68
72
  private var minZoom: Double? = null
69
73
  private var maxZoom: Double? = null
70
74
  private var edgeInsets: EdgeInsets = EdgeInsets()
75
+ private var insetAdjustment: String = "never"
71
76
 
72
77
  // region View Lifecycle
73
78
 
@@ -183,10 +188,15 @@ class LuggMapView(private val reactContext: ThemedReactContext) :
183
188
  provider?.setPitchEnabled(pitchEnabled)
184
189
  provider?.setUserLocationEnabled(userLocationEnabled)
185
190
  provider?.setUserLocationButtonEnabled(userLocationButtonEnabled)
191
+ provider?.setMapType(mapType)
186
192
  provider?.setTheme(theme)
187
193
  minZoom?.let { provider?.setMinZoom(it) }
188
194
  maxZoom?.let { provider?.setMaxZoom(it) }
189
195
  provider?.setEdgeInsets(edgeInsets)
196
+ provider?.setInsetAdjustment(insetAdjustment)
197
+ provider?.setPoiEnabled(poiEnabled)
198
+ provider?.setPoiFilterMode(poiFilterMode)
199
+ provider?.setPoiFilterCategories(poiFilterCategories)
190
200
  }
191
201
 
192
202
  fun setProvider(value: String?) {
@@ -257,12 +267,42 @@ class LuggMapView(private val reactContext: ThemedReactContext) :
257
267
  provider?.setMaxZoom(zoom)
258
268
  }
259
269
 
270
+ fun setMapType(value: String) {
271
+ if (mapType == value) return
272
+ mapType = value
273
+ provider?.setMapType(value)
274
+ }
275
+
260
276
  fun setTheme(value: String) {
261
277
  if (theme == value) return
262
278
  theme = value
263
279
  provider?.setTheme(value)
264
280
  }
265
281
 
282
+ fun setPoiEnabled(enabled: Boolean) {
283
+ if (poiEnabled == enabled) return
284
+ poiEnabled = enabled
285
+ provider?.setPoiEnabled(enabled)
286
+ }
287
+
288
+ fun setPoiFilterMode(value: String) {
289
+ if (poiFilterMode == value) return
290
+ poiFilterMode = value
291
+ provider?.setPoiFilterMode(value)
292
+ }
293
+
294
+ fun setPoiFilterCategories(categories: List<String>) {
295
+ if (poiFilterCategories == categories) return
296
+ poiFilterCategories = categories
297
+ provider?.setPoiFilterCategories(categories)
298
+ }
299
+
300
+ fun setInsetAdjustment(value: String) {
301
+ if (insetAdjustment == value) return
302
+ insetAdjustment = value
303
+ provider?.setInsetAdjustment(value)
304
+ }
305
+
266
306
  fun setEdgeInsets(
267
307
  top: Int,
268
308
  left: Int,
@@ -152,11 +152,42 @@ class LuggMapViewManager :
152
152
  view.setMaxZoom(value)
153
153
  }
154
154
 
155
+ @ReactProp(name = "mapType")
156
+ override fun setMapType(view: LuggMapView, value: String?) {
157
+ view.setMapType(value ?: "standard")
158
+ }
159
+
155
160
  @ReactProp(name = "theme")
156
161
  override fun setTheme(view: LuggMapView, value: String?) {
157
162
  view.setTheme(value ?: "system")
158
163
  }
159
164
 
165
+ @ReactProp(name = "poiEnabled", defaultBoolean = true)
166
+ override fun setPoiEnabled(view: LuggMapView, value: Boolean) {
167
+ view.setPoiEnabled(value)
168
+ }
169
+
170
+ @ReactProp(name = "poiFilterMode")
171
+ override fun setPoiFilterMode(view: LuggMapView, value: String?) {
172
+ view.setPoiFilterMode(value ?: "including")
173
+ }
174
+
175
+ @ReactProp(name = "poiFilterCategories")
176
+ override fun setPoiFilterCategories(view: LuggMapView, value: ReadableArray?) {
177
+ val categories = mutableListOf<String>()
178
+ value?.let { array ->
179
+ for (i in 0 until array.size()) {
180
+ array.getString(i)?.let { categories.add(it) }
181
+ }
182
+ }
183
+ view.setPoiFilterCategories(categories)
184
+ }
185
+
186
+ @ReactProp(name = "insetAdjustment")
187
+ override fun setInsetAdjustment(view: LuggMapView, value: String?) {
188
+ view.setInsetAdjustment(value ?: "never")
189
+ }
190
+
160
191
  @ReactProp(name = "edgeInsets")
161
192
  override fun setEdgeInsets(view: LuggMapView, value: ReadableMap?) {
162
193
  value?.let {
@@ -16,6 +16,8 @@ import com.luggmaps.extensions.dispatchEvent
16
16
  interface LuggMarkerViewDelegate {
17
17
  fun markerViewDidUpdate(markerView: LuggMarkerView)
18
18
  fun markerViewDidLayout(markerView: LuggMarkerView)
19
+ fun showCalloutForMarkerView(markerView: LuggMarkerView)
20
+ fun hideCalloutForMarkerView(markerView: LuggMarkerView)
19
21
  }
20
22
 
21
23
  class LuggMarkerView(context: Context) : ReactViewGroup(context) {
@@ -57,6 +59,9 @@ class LuggMarkerView(context: Context) : ReactViewGroup(context) {
57
59
  var rasterize: Boolean = true
58
60
  private set
59
61
 
62
+ var centerOnPress: Boolean = true
63
+ private set
64
+
60
65
  var draggable: Boolean = false
61
66
  private set
62
67
 
@@ -75,6 +80,12 @@ class LuggMarkerView(context: Context) : ReactViewGroup(context) {
75
80
  var calloutView: LuggCalloutView? = null
76
81
  private set
77
82
 
83
+ val scaledContentHeight: Float
84
+ get() {
85
+ val (_, height) = measureContentBounds()
86
+ return height * scale
87
+ }
88
+
78
89
  private fun measureContentBounds(): Pair<Int, Int> {
79
90
  var maxWidth = 0
80
91
  var maxHeight = 0
@@ -266,6 +277,10 @@ class LuggMarkerView(context: Context) : ReactViewGroup(context) {
266
277
  this.rasterize = rasterize
267
278
  }
268
279
 
280
+ fun setCenterOnPress(centerOnPress: Boolean) {
281
+ this.centerOnPress = centerOnPress
282
+ }
283
+
269
284
  fun setDraggable(draggable: Boolean) {
270
285
  this.draggable = draggable
271
286
  }
@@ -286,6 +301,14 @@ class LuggMarkerView(context: Context) : ReactViewGroup(context) {
286
301
  dispatchEvent(MarkerDragEvent(this, MarkerDragEvent.DRAG_END, latitude, longitude, x, y))
287
302
  }
288
303
 
304
+ fun showCallout() {
305
+ delegate?.showCalloutForMarkerView(this)
306
+ }
307
+
308
+ fun hideCallout() {
309
+ delegate?.hideCalloutForMarkerView(this)
310
+ }
311
+
289
312
  fun setName(name: String?) {
290
313
  this.name = name
291
314
  }
@@ -92,11 +92,24 @@ class LuggMarkerViewManager :
92
92
  view.setRasterize(value)
93
93
  }
94
94
 
95
+ @ReactProp(name = "centerOnPress", defaultBoolean = true)
96
+ override fun setCenterOnPress(view: LuggMarkerView, value: Boolean) {
97
+ view.setCenterOnPress(value)
98
+ }
99
+
95
100
  @ReactProp(name = "draggable", defaultBoolean = false)
96
101
  override fun setDraggable(view: LuggMarkerView, value: Boolean) {
97
102
  view.setDraggable(value)
98
103
  }
99
104
 
105
+ override fun showCallout(view: LuggMarkerView) {
106
+ view.showCallout()
107
+ }
108
+
109
+ override fun hideCallout(view: LuggMarkerView) {
110
+ view.hideCallout()
111
+ }
112
+
100
113
  companion object {
101
114
  const val NAME = "LuggMarkerView"
102
115
  }
@@ -1,10 +1,14 @@
1
1
  package com.luggmaps.core
2
2
 
3
3
  import android.annotation.SuppressLint
4
+ import android.content.ComponentCallbacks2
4
5
  import android.content.Context
6
+ import android.content.res.Configuration
5
7
  import android.view.View
6
8
  import android.widget.ImageView
7
9
  import androidx.core.graphics.createBitmap
10
+ import androidx.core.view.ViewCompat
11
+ import androidx.core.view.WindowInsetsCompat
8
12
  import com.facebook.react.uimanager.PixelUtil.dpToPx
9
13
  import com.google.android.gms.maps.CameraUpdateFactory
10
14
  import com.google.android.gms.maps.GoogleMap
@@ -25,7 +29,6 @@ import com.google.android.gms.maps.model.Marker
25
29
  import com.google.android.gms.maps.model.Polygon
26
30
  import com.google.android.gms.maps.model.PolygonOptions
27
31
  import com.google.android.gms.maps.model.PolylineOptions
28
- import com.google.android.gms.maps.model.TileOverlay
29
32
  import com.google.android.gms.maps.model.TileOverlayOptions
30
33
  import com.google.android.gms.maps.model.UrlTileProvider
31
34
  import com.luggmaps.LuggCalloutView
@@ -42,7 +45,11 @@ import com.luggmaps.LuggPolylineView
42
45
  import com.luggmaps.LuggPolylineViewDelegate
43
46
  import com.luggmaps.LuggTileOverlayView
44
47
  import com.luggmaps.LuggTileOverlayViewDelegate
48
+ import com.luggmaps.extensions.findViewByTag
45
49
  import java.net.URL
50
+ import kotlin.math.atan
51
+ import kotlin.math.pow
52
+ import kotlin.math.sinh
46
53
 
47
54
  class GoogleMapProvider(private val context: Context) :
48
55
  MapProvider,
@@ -63,7 +70,8 @@ class GoogleMapProvider(private val context: Context) :
63
70
  GoogleMap.OnGroundOverlayClickListener,
64
71
  GoogleMap.OnMarkerClickListener,
65
72
  GoogleMap.OnMarkerDragListener,
66
- GoogleMap.InfoWindowAdapter {
73
+ GoogleMap.InfoWindowAdapter,
74
+ ComponentCallbacks2 {
67
75
 
68
76
  override var delegate: MapProviderDelegate? = null
69
77
  override val isMapReady: Boolean get() = _isMapReady
@@ -71,6 +79,7 @@ class GoogleMapProvider(private val context: Context) :
71
79
  var mapId: String = DEMO_MAP_ID
72
80
 
73
81
  private var wrapperView: LuggMapWrapperView? = null
82
+ private var currentNightMode: Int = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
74
83
  private var mapView: MapView? = null
75
84
  private var googleMap: GoogleMap? = null
76
85
  private var _isMapReady = false
@@ -113,6 +122,10 @@ class GoogleMapProvider(private val context: Context) :
113
122
  // Edge Insets
114
123
  private var edgeInsets: EdgeInsets = EdgeInsets()
115
124
 
125
+ // Inset adjustment
126
+ private var insetAdjustment: String = "never"
127
+ private var systemInsets: EdgeInsets = EdgeInsets()
128
+
116
129
  // region MapProvider
117
130
 
118
131
  override fun initializeMap(wrapperView: View, latitude: Double, longitude: Double, zoom: Float) {
@@ -125,6 +138,8 @@ class GoogleMapProvider(private val context: Context) :
125
138
  val wrapper = wrapperView as LuggMapWrapperView
126
139
  this.wrapperView = wrapper
127
140
 
141
+ context.applicationContext.registerComponentCallbacks(this)
142
+
128
143
  val options = GoogleMapOptions().mapId(mapId)
129
144
  mapView = MapView(context, options).also { view ->
130
145
  view.onCreate(null)
@@ -135,6 +150,8 @@ class GoogleMapProvider(private val context: Context) :
135
150
  }
136
151
 
137
152
  override fun destroy() {
153
+ detachWindowInsetsListener()
154
+ context.applicationContext.unregisterComponentCallbacks(this)
138
155
  dismissNonBubbledCallout()
139
156
  for (markerView in liveMarkerViews) {
140
157
  markerView.onUpdate = null
@@ -201,6 +218,7 @@ class GoogleMapProvider(private val context: Context) :
201
218
  applyUiSettings()
202
219
  applyZoomLimits()
203
220
  applyEdgeInsets()
221
+ applyInsetAdjustment()
204
222
  applyTheme()
205
223
  applyUserLocation()
206
224
  processPendingMarkers()
@@ -289,10 +307,17 @@ class GoogleMapProvider(private val context: Context) :
289
307
 
290
308
  val calloutView = view.calloutView
291
309
  if (calloutView != null && !calloutView.bubbled && calloutView.hasCustomContent) {
292
- googleMap?.animateCamera(CameraUpdateFactory.newLatLng(marker.position))
310
+ if (view.centerOnPress) {
311
+ googleMap?.animateCamera(CameraUpdateFactory.newLatLng(marker.position))
312
+ }
293
313
  showNonBubbledCallout(marker, calloutView)
294
314
  return true
295
315
  }
316
+
317
+ if (!view.centerOnPress) {
318
+ marker.showInfoWindow()
319
+ return true
320
+ }
296
321
  }
297
322
  return false
298
323
  }
@@ -406,8 +431,8 @@ class GoogleMapProvider(private val context: Context) :
406
431
  val map = googleMap ?: return
407
432
 
408
433
  val point = map.projection.toScreenLocation(marker.position)
409
- contentView.translationX = point.x - contentView.width * calloutView.anchorX
410
- contentView.translationY = point.y - markerView.contentView.height * markerView.anchorY - contentView.height * calloutView.anchorY
434
+ contentView.translationX = point.x - contentView.width / 2f + calloutView.offsetX
435
+ contentView.translationY = point.y - markerView.scaledContentHeight * markerView.anchorY - contentView.height + calloutView.offsetY
411
436
  }
412
437
 
413
438
  // endregion
@@ -451,6 +476,15 @@ class GoogleMapProvider(private val context: Context) :
451
476
  googleMap?.uiSettings?.isMyLocationButtonEnabled = enabled
452
477
  }
453
478
 
479
+ override fun setMapType(value: String) {
480
+ googleMap?.mapType = when (value) {
481
+ "satellite" -> GoogleMap.MAP_TYPE_SATELLITE
482
+ "terrain" -> GoogleMap.MAP_TYPE_TERRAIN
483
+ "hybrid" -> GoogleMap.MAP_TYPE_HYBRID
484
+ else -> GoogleMap.MAP_TYPE_NORMAL
485
+ }
486
+ }
487
+
454
488
  override fun setTheme(value: String) {
455
489
  theme = value
456
490
  applyTheme()
@@ -490,7 +524,7 @@ class GoogleMapProvider(private val context: Context) :
490
524
 
491
525
  if (map != null && oldInsets != edgeInsets) {
492
526
  val cameraUpdate = CameraUpdateFactory.newCameraPosition(map.cameraPosition)
493
- applyEdgeInsets()
527
+ applyEdgeInsets(duration)
494
528
  when {
495
529
  duration < 0 -> map.animateCamera(cameraUpdate)
496
530
  duration > 0 -> map.animateCamera(cameraUpdate, duration, null)
@@ -501,6 +535,17 @@ class GoogleMapProvider(private val context: Context) :
501
535
  }
502
536
  }
503
537
 
538
+ override fun setInsetAdjustment(value: String) {
539
+ insetAdjustment = value
540
+ applyInsetAdjustment()
541
+ }
542
+
543
+ override fun setPoiEnabled(enabled: Boolean) {}
544
+
545
+ override fun setPoiFilterMode(mode: String) {}
546
+
547
+ override fun setPoiFilterCategories(categories: List<String>) {}
548
+
504
549
  // endregion
505
550
 
506
551
  // region MarkerViewDelegate
@@ -522,6 +567,30 @@ class GoogleMapProvider(private val context: Context) :
522
567
  syncMarkerView(markerView)
523
568
  }
524
569
 
570
+ override fun showCalloutForMarkerView(markerView: LuggMarkerView) {
571
+ val marker = markerView.marker ?: return
572
+ val calloutView = markerView.calloutView
573
+
574
+ if (calloutView != null && calloutView.hasCustomContent) {
575
+ dismissNonBubbledCallout()
576
+ if (calloutView.bubbled) {
577
+ marker.showInfoWindow()
578
+ } else {
579
+ showNonBubbledCallout(marker, calloutView)
580
+ }
581
+ return
582
+ }
583
+
584
+ if (!markerView.title.isNullOrEmpty()) {
585
+ marker.showInfoWindow()
586
+ }
587
+ }
588
+
589
+ override fun hideCalloutForMarkerView(markerView: LuggMarkerView) {
590
+ dismissNonBubbledCallout()
591
+ markerView.marker?.hideInfoWindow()
592
+ }
593
+
525
594
  // endregion
526
595
 
527
596
  // region PolylineViewDelegate
@@ -998,9 +1067,9 @@ class GoogleMapProvider(private val context: Context) :
998
1067
  val tileProvider = object : UrlTileProvider(tileSize, tileSize) {
999
1068
  override fun getTileUrl(x: Int, y: Int, zoom: Int): URL? {
1000
1069
  if (hasBounds) {
1001
- val n = Math.pow(2.0, zoom.toDouble())
1002
- val tileSWLat = Math.toDegrees(Math.atan(Math.sinh(Math.PI * (1 - 2.0 * (y + 1) / n))))
1003
- val tileNELat = Math.toDegrees(Math.atan(Math.sinh(Math.PI * (1 - 2.0 * y / n))))
1070
+ val n = 2.0.pow(zoom.toDouble())
1071
+ val tileSWLat = Math.toDegrees(atan(sinh(Math.PI * (1 - 2.0 * (y + 1) / n))))
1072
+ val tileNELat = Math.toDegrees(atan(sinh(Math.PI * (1 - 2.0 * y / n))))
1004
1073
  val tileSWLng = x / n * 360.0 - 180.0
1005
1074
  val tileNELng = (x + 1) / n * 360.0 - 180.0
1006
1075
 
@@ -1079,11 +1148,11 @@ class GoogleMapProvider(private val context: Context) :
1079
1148
  val map = googleMap ?: return
1080
1149
  if (coordinates.isEmpty()) return
1081
1150
 
1082
- val latLngs = coordinates.filterIsInstance<LatLng>()
1083
- if (latLngs.isEmpty()) return
1151
+ val latLongs = coordinates.filterIsInstance<LatLng>()
1152
+ if (latLongs.isEmpty()) return
1084
1153
 
1085
- val boundsBuilder = com.google.android.gms.maps.model.LatLngBounds.Builder()
1086
- latLngs.forEach { boundsBuilder.include(it) }
1154
+ val boundsBuilder = LatLngBounds.Builder()
1155
+ latLongs.forEach { boundsBuilder.include(it) }
1087
1156
  val bounds = boundsBuilder.build()
1088
1157
 
1089
1158
  val top = edgeInsetsTop.toFloat().dpToPx().toInt()
@@ -1091,11 +1160,12 @@ class GoogleMapProvider(private val context: Context) :
1091
1160
  val bottom = edgeInsetsBottom.toFloat().dpToPx().toInt()
1092
1161
  val right = edgeInsetsRight.toFloat().dpToPx().toInt()
1093
1162
 
1163
+ val combined = combinedEdgeInsets()
1094
1164
  map.setPadding(
1095
- edgeInsets.left + left,
1096
- edgeInsets.top + top,
1097
- edgeInsets.right + right,
1098
- edgeInsets.bottom + bottom
1165
+ combined.left + left,
1166
+ combined.top + top,
1167
+ combined.right + right,
1168
+ combined.bottom + bottom
1099
1169
  )
1100
1170
 
1101
1171
  val cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, 0)
@@ -1106,7 +1176,7 @@ class GoogleMapProvider(private val context: Context) :
1106
1176
  else -> map.moveCamera(cameraUpdate)
1107
1177
  }
1108
1178
 
1109
- map.setPadding(edgeInsets.left, edgeInsets.top, edgeInsets.right, edgeInsets.bottom)
1179
+ map.setPadding(combined.left, combined.top, combined.right, combined.bottom)
1110
1180
  }
1111
1181
 
1112
1182
  // endregion
@@ -1130,17 +1200,92 @@ class GoogleMapProvider(private val context: Context) :
1130
1200
  }
1131
1201
  }
1132
1202
 
1133
- private fun applyEdgeInsets() {
1134
- googleMap?.setPadding(edgeInsets.left, edgeInsets.top, edgeInsets.right, edgeInsets.bottom)
1203
+ private fun applyInsetAdjustment() {
1204
+ if (insetAdjustment == "automatic") {
1205
+ attachWindowInsetsListener()
1206
+ } else {
1207
+ detachWindowInsetsListener()
1208
+ systemInsets = EdgeInsets()
1209
+ }
1210
+ applyEdgeInsets()
1135
1211
  }
1136
1212
 
1213
+ private fun combinedEdgeInsets(): EdgeInsets =
1214
+ if (insetAdjustment == "automatic") {
1215
+ EdgeInsets(
1216
+ edgeInsets.top + systemInsets.top,
1217
+ edgeInsets.left + systemInsets.left,
1218
+ edgeInsets.bottom + systemInsets.bottom,
1219
+ edgeInsets.right + systemInsets.right
1220
+ )
1221
+ } else {
1222
+ edgeInsets
1223
+ }
1224
+
1225
+ private fun applyEdgeInsets(duration: Int = 0) {
1226
+ val combined = combinedEdgeInsets()
1227
+ googleMap?.setPadding(combined.left, combined.top, combined.right, combined.bottom)
1228
+ applyWatermarkTranslation(combined, duration)
1229
+ }
1230
+
1231
+ private fun applyWatermarkTranslation(insets: EdgeInsets, duration: Int = 0) {
1232
+ val view = mapView ?: return
1233
+ view.findViewByTag("GoogleWatermark")?.let { watermark ->
1234
+ val targetY = -insets.bottom.toFloat()
1235
+ val targetX = insets.left.toFloat()
1236
+ if (duration > 0) {
1237
+ watermark.animate()
1238
+ .translationY(targetY)
1239
+ .translationX(targetX)
1240
+ .setDuration(duration.toLong())
1241
+ .start()
1242
+ } else if (duration < 0) {
1243
+ watermark.animate()
1244
+ .translationY(targetY)
1245
+ .translationX(targetX)
1246
+ .start()
1247
+ } else {
1248
+ watermark.translationY = targetY
1249
+ watermark.translationX = targetX
1250
+ }
1251
+ }
1252
+ }
1253
+
1254
+ private fun attachWindowInsetsListener() {
1255
+ val view = mapView ?: return
1256
+ ViewCompat.setOnApplyWindowInsetsListener(view) { _, windowInsets ->
1257
+ val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
1258
+ systemInsets = EdgeInsets(insets.top, insets.left, insets.bottom, insets.right)
1259
+ applyEdgeInsets()
1260
+ windowInsets
1261
+ }
1262
+ ViewCompat.requestApplyInsets(view)
1263
+ }
1264
+
1265
+ private fun detachWindowInsetsListener() {
1266
+ mapView?.let { ViewCompat.setOnApplyWindowInsetsListener(it, null) }
1267
+ }
1268
+
1269
+ override fun onConfigurationChanged(newConfig: Configuration) {
1270
+ val newNightMode = newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
1271
+ if (newNightMode != currentNightMode) {
1272
+ currentNightMode = newNightMode
1273
+ applyTheme()
1274
+ }
1275
+ }
1276
+
1277
+ @Deprecated("Deprecated in Java")
1278
+ override fun onLowMemory() {}
1279
+
1280
+ override fun onTrimMemory(level: Int) {}
1281
+
1137
1282
  private fun applyTheme() {
1138
1283
  val colorScheme = when (theme) {
1139
1284
  "dark" -> MapColorScheme.DARK
1140
1285
  "light" -> MapColorScheme.LIGHT
1141
1286
  else -> MapColorScheme.FOLLOW_SYSTEM
1142
1287
  }
1143
- googleMap?.setMapColorScheme(colorScheme)
1288
+ googleMap?.mapColorScheme = colorScheme
1144
1289
  }
1145
1290
 
1146
1291
  @SuppressLint("MissingPermission")
@@ -32,11 +32,16 @@ interface MapProvider {
32
32
  fun setPitchEnabled(enabled: Boolean)
33
33
  fun setUserLocationEnabled(enabled: Boolean)
34
34
  fun setUserLocationButtonEnabled(enabled: Boolean)
35
+ fun setMapType(value: String)
35
36
  fun setTheme(value: String)
36
37
  fun setMinZoom(zoom: Double)
37
38
  fun setMaxZoom(zoom: Double)
38
39
  fun setEdgeInsets(edgeInsets: EdgeInsets)
39
40
  fun setEdgeInsets(edgeInsets: EdgeInsets, duration: Int)
41
+ fun setInsetAdjustment(value: String)
42
+ fun setPoiEnabled(enabled: Boolean)
43
+ fun setPoiFilterMode(mode: String)
44
+ fun setPoiFilterCategories(categories: List<String>)
40
45
 
41
46
  // Children
42
47
  fun addMarkerView(markerView: LuggMarkerView)
@@ -1,6 +1,7 @@
1
1
  package com.luggmaps.extensions
2
2
 
3
3
  import android.view.View
4
+ import android.view.ViewGroup
4
5
  import com.facebook.react.uimanager.ThemedReactContext
5
6
  import com.facebook.react.uimanager.UIManagerHelper
6
7
  import com.facebook.react.uimanager.events.Event
@@ -12,3 +13,13 @@ fun View.dispatchEvent(event: Event<*>) {
12
13
  )
13
14
  eventDispatcher?.dispatchEvent(event)
14
15
  }
16
+
17
+ fun View.findViewByTag(tag: String): View? {
18
+ if (this.tag?.toString() == tag) return this
19
+ if (this !is ViewGroup) return null
20
+ for (i in 0 until childCount) {
21
+ val found = getChildAt(i).findViewByTag(tag)
22
+ if (found != null) return found
23
+ }
24
+ return null
25
+ }
@@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN
13
13
  @interface LuggCalloutView : RCTViewComponentView
14
14
 
15
15
  @property(nonatomic, readonly) BOOL bubbled;
16
- @property(nonatomic, readonly) CGPoint anchor;
16
+ @property(nonatomic, readonly) CGPoint offset;
17
17
  @property(nonatomic, readonly) BOOL hasCustomContent;
18
18
  @property(nonatomic, readonly) UIView *contentView;
19
19
  @property(nonatomic, weak, nullable) id<LuggCalloutViewDelegate> delegate;