@lugg/maps 0.2.0-alpha.3 → 0.2.0-alpha.30

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 (124) hide show
  1. package/README.md +21 -3
  2. package/android/src/main/java/com/luggmaps/LuggMapView.kt +265 -0
  3. package/android/src/main/java/com/luggmaps/{LuggGoogleMapViewManager.kt → LuggMapViewManager.kt} +75 -36
  4. package/android/src/main/java/com/luggmaps/LuggMapWrapperView.kt +6 -5
  5. package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +136 -14
  6. package/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt +21 -0
  7. package/android/src/main/java/com/luggmaps/LuggPackage.kt +1 -4
  8. package/android/src/main/java/com/luggmaps/LuggPolylineView.kt +16 -0
  9. package/android/src/main/java/com/luggmaps/LuggPolylineViewManager.kt +22 -0
  10. package/android/src/main/java/com/luggmaps/{LuggGoogleMapView.kt → core/GoogleMapProvider.kt} +251 -241
  11. package/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt +55 -0
  12. package/android/src/main/java/com/luggmaps/core/PolylineAnimator.kt +163 -52
  13. package/ios/LuggMapView.h +9 -0
  14. package/ios/LuggMapView.mm +312 -0
  15. package/ios/LuggMarkerView.h +9 -0
  16. package/ios/LuggMarkerView.mm +79 -0
  17. package/ios/LuggPolylineView.h +4 -0
  18. package/ios/LuggPolylineView.mm +23 -0
  19. package/ios/core/AppleMapProvider.h +15 -0
  20. package/ios/core/AppleMapProvider.mm +664 -0
  21. package/ios/core/GMSPolylineAnimator.h +3 -0
  22. package/ios/core/GMSPolylineAnimator.m +175 -40
  23. package/ios/core/GoogleMapProvider.h +13 -0
  24. package/ios/core/GoogleMapProvider.mm +513 -0
  25. package/ios/core/MKPolylineAnimator.h +4 -0
  26. package/ios/core/MKPolylineAnimator.m +224 -68
  27. package/ios/core/MapProviderDelegate.h +72 -0
  28. package/ios/core/PolylineAnimatorBase.h +24 -1
  29. package/ios/core/PolylineAnimatorBase.m +82 -19
  30. package/ios/extensions/MKMapView+Zoom.h +2 -0
  31. package/ios/extensions/MKMapView+Zoom.m +14 -4
  32. package/lib/module/MapProvider.js +13 -0
  33. package/lib/module/MapProvider.js.map +1 -0
  34. package/lib/module/MapProvider.types.js +4 -0
  35. package/lib/module/MapProvider.types.js.map +1 -0
  36. package/lib/module/MapProvider.web.js +20 -0
  37. package/lib/module/MapProvider.web.js.map +1 -0
  38. package/lib/module/MapView.js +29 -16
  39. package/lib/module/MapView.js.map +1 -1
  40. package/lib/module/MapView.web.js +290 -0
  41. package/lib/module/MapView.web.js.map +1 -0
  42. package/lib/module/components/Marker.js +19 -2
  43. package/lib/module/components/Marker.js.map +1 -1
  44. package/lib/module/components/Marker.web.js +33 -0
  45. package/lib/module/components/Marker.web.js.map +1 -0
  46. package/lib/module/components/Polyline.js +17 -4
  47. package/lib/module/components/Polyline.js.map +1 -1
  48. package/lib/module/components/Polyline.web.js +229 -0
  49. package/lib/module/components/Polyline.web.js.map +1 -0
  50. package/lib/module/components/index.js +2 -2
  51. package/lib/module/components/index.js.map +1 -1
  52. package/lib/module/components/index.web.js +5 -0
  53. package/lib/module/components/index.web.js.map +1 -0
  54. package/lib/module/fabric/{LuggGoogleMapViewNativeComponent.ts → LuggMapViewNativeComponent.ts} +19 -7
  55. package/lib/module/fabric/LuggMarkerViewNativeComponent.ts +7 -1
  56. package/lib/module/fabric/LuggPolylineViewNativeComponent.ts +8 -0
  57. package/lib/module/index.js +4 -2
  58. package/lib/module/index.js.map +1 -1
  59. package/lib/module/index.web.js +7 -0
  60. package/lib/module/index.web.js.map +1 -0
  61. package/lib/typescript/src/MapProvider.d.ts +8 -0
  62. package/lib/typescript/src/MapProvider.d.ts.map +1 -0
  63. package/lib/typescript/src/MapProvider.types.d.ts +16 -0
  64. package/lib/typescript/src/MapProvider.types.d.ts.map +1 -0
  65. package/lib/typescript/src/MapProvider.web.d.ts +11 -0
  66. package/lib/typescript/src/MapProvider.web.d.ts.map +1 -0
  67. package/lib/typescript/src/MapView.d.ts +4 -4
  68. package/lib/typescript/src/MapView.d.ts.map +1 -1
  69. package/lib/typescript/src/MapView.types.d.ts +23 -7
  70. package/lib/typescript/src/MapView.types.d.ts.map +1 -1
  71. package/lib/typescript/src/MapView.web.d.ts +3 -0
  72. package/lib/typescript/src/MapView.web.d.ts.map +1 -0
  73. package/lib/typescript/src/components/Marker.d.ts +25 -1
  74. package/lib/typescript/src/components/Marker.d.ts.map +1 -1
  75. package/lib/typescript/src/components/Marker.web.d.ts +3 -0
  76. package/lib/typescript/src/components/Marker.web.d.ts.map +1 -0
  77. package/lib/typescript/src/components/Polyline.d.ts +36 -1
  78. package/lib/typescript/src/components/Polyline.d.ts.map +1 -1
  79. package/lib/typescript/src/components/Polyline.web.d.ts +3 -0
  80. package/lib/typescript/src/components/Polyline.web.d.ts.map +1 -0
  81. package/lib/typescript/src/components/index.d.ts +4 -2
  82. package/lib/typescript/src/components/index.d.ts.map +1 -1
  83. package/lib/typescript/src/components/index.web.d.ts +5 -0
  84. package/lib/typescript/src/components/index.web.d.ts.map +1 -0
  85. package/lib/typescript/src/fabric/{LuggGoogleMapViewNativeComponent.d.ts → LuggMapViewNativeComponent.d.ts} +8 -4
  86. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts.map +1 -0
  87. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts +4 -1
  88. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts.map +1 -1
  89. package/lib/typescript/src/fabric/LuggPolylineViewNativeComponent.d.ts +7 -0
  90. package/lib/typescript/src/fabric/LuggPolylineViewNativeComponent.d.ts.map +1 -1
  91. package/lib/typescript/src/index.d.ts +8 -3
  92. package/lib/typescript/src/index.d.ts.map +1 -1
  93. package/lib/typescript/src/index.web.d.ts +10 -0
  94. package/lib/typescript/src/index.web.d.ts.map +1 -0
  95. package/lib/typescript/src/types.d.ts +4 -0
  96. package/lib/typescript/src/types.d.ts.map +1 -1
  97. package/package.json +17 -3
  98. package/src/MapProvider.tsx +10 -0
  99. package/src/MapProvider.types.ts +16 -0
  100. package/src/MapProvider.web.tsx +14 -0
  101. package/src/MapView.tsx +28 -26
  102. package/src/MapView.types.ts +24 -7
  103. package/src/MapView.web.tsx +364 -0
  104. package/src/components/Marker.tsx +50 -4
  105. package/src/components/Marker.web.tsx +33 -0
  106. package/src/components/Polyline.tsx +51 -2
  107. package/src/components/Polyline.web.tsx +287 -0
  108. package/src/components/index.ts +8 -2
  109. package/src/components/index.web.ts +4 -0
  110. package/src/fabric/{LuggGoogleMapViewNativeComponent.ts → LuggMapViewNativeComponent.ts} +19 -7
  111. package/src/fabric/LuggMarkerViewNativeComponent.ts +7 -1
  112. package/src/fabric/LuggPolylineViewNativeComponent.ts +8 -0
  113. package/src/index.ts +18 -2
  114. package/src/index.web.ts +20 -0
  115. package/src/types.ts +5 -0
  116. package/ios/LuggAppleMapView.h +0 -16
  117. package/ios/LuggAppleMapView.mm +0 -580
  118. package/ios/LuggGoogleMapView.h +0 -13
  119. package/ios/LuggGoogleMapView.mm +0 -454
  120. package/lib/module/fabric/LuggAppleMapViewNativeComponent.ts +0 -83
  121. package/lib/typescript/src/fabric/LuggAppleMapViewNativeComponent.d.ts +0 -54
  122. package/lib/typescript/src/fabric/LuggAppleMapViewNativeComponent.d.ts.map +0 -1
  123. package/lib/typescript/src/fabric/LuggGoogleMapViewNativeComponent.d.ts.map +0 -1
  124. package/src/fabric/LuggAppleMapViewNativeComponent.ts +0 -83
@@ -1,13 +1,10 @@
1
- package com.luggmaps
1
+ package com.luggmaps.core
2
2
 
3
3
  import android.annotation.SuppressLint
4
- import android.util.Log
4
+ import android.content.Context
5
5
  import android.view.View
6
- import android.view.ViewGroup
7
6
  import com.facebook.react.uimanager.PixelUtil.dpToPx
8
- import com.facebook.react.uimanager.ThemedReactContext
9
7
  import com.facebook.react.util.RNLog
10
- import com.facebook.react.views.view.ReactViewGroup
11
8
  import com.google.android.gms.maps.CameraUpdateFactory
12
9
  import com.google.android.gms.maps.GoogleMap
13
10
  import com.google.android.gms.maps.GoogleMapOptions
@@ -15,32 +12,16 @@ import com.google.android.gms.maps.MapView
15
12
  import com.google.android.gms.maps.OnMapReadyCallback
16
13
  import com.google.android.gms.maps.model.AdvancedMarker
17
14
  import com.google.android.gms.maps.model.AdvancedMarkerOptions
18
- import com.google.android.gms.maps.model.AdvancedMarkerOptions.CollisionBehavior
19
15
  import com.google.android.gms.maps.model.LatLng
16
+ import com.google.android.gms.maps.model.MapColorScheme
20
17
  import com.google.android.gms.maps.model.PolylineOptions
21
- import com.luggmaps.core.PolylineAnimator
22
-
23
- interface LuggGoogleMapViewEventDelegate {
24
- fun onCameraMove(
25
- view: LuggGoogleMapView,
26
- latitude: Double,
27
- longitude: Double,
28
- zoom: Float,
29
- gesture: Boolean
30
- )
31
- fun onCameraIdle(
32
- view: LuggGoogleMapView,
33
- latitude: Double,
34
- longitude: Double,
35
- zoom: Float,
36
- gesture: Boolean
37
- )
38
- fun onReady(view: LuggGoogleMapView)
39
- }
18
+ import com.luggmaps.LuggMarkerView
19
+ import com.luggmaps.LuggMarkerViewDelegate
20
+ import com.luggmaps.LuggPolylineView
21
+ import com.luggmaps.LuggPolylineViewDelegate
40
22
 
41
- @SuppressLint("ViewConstructor")
42
- class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
43
- ReactViewGroup(reactContext),
23
+ class GoogleMapProvider(private val context: Context) :
24
+ MapProvider,
44
25
  OnMapReadyCallback,
45
26
  LuggMarkerViewDelegate,
46
27
  LuggPolylineViewDelegate,
@@ -48,21 +29,23 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
48
29
  GoogleMap.OnCameraMoveListener,
49
30
  GoogleMap.OnCameraIdleListener {
50
31
 
51
- var eventDelegate: LuggGoogleMapViewEventDelegate? = null
32
+ override var delegate: MapProviderDelegate? = null
33
+ override val isMapReady: Boolean get() = _isMapReady
34
+
35
+ var mapId: String = DEMO_MAP_ID
36
+
52
37
  private var mapView: MapView? = null
53
- private var mapWrapperView: LuggMapWrapperView? = null
54
38
  private var googleMap: GoogleMap? = null
55
- private var isMapReady = false
39
+ private var _isMapReady = false
56
40
  private var isDragging = false
57
- private var mapId: String = DEMO_MAP_ID
58
- private val pendingMarkerViews = mutableListOf<LuggMarkerView>()
59
- private val pendingPolylineViews = mutableListOf<LuggPolylineView>()
41
+ private val pendingMarkerViews = mutableSetOf<LuggMarkerView>()
42
+ private val pendingPolylineViews = mutableSetOf<LuggPolylineView>()
60
43
  private val polylineAnimators = mutableMapOf<LuggPolylineView, PolylineAnimator>()
61
44
 
62
45
  // Initial camera settings
63
- private var initialLatitude: Double = 37.78
64
- private var initialLongitude: Double = -122.43
65
- private var initialZoom: Float = 14f
46
+ private var initialLatitude: Double = 0.0
47
+ private var initialLongitude: Double = 0.0
48
+ private var initialZoom: Float = 10f
66
49
 
67
50
  // UI settings
68
51
  private var zoomEnabled: Boolean = true
@@ -70,95 +53,55 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
70
53
  private var rotateEnabled: Boolean = true
71
54
  private var pitchEnabled: Boolean = true
72
55
  private var userLocationEnabled: Boolean = false
56
+ private var userLocationButtonEnabled: Boolean = false
73
57
 
74
58
  // Zoom limits
75
59
  private var minZoom: Float? = null
76
60
  private var maxZoom: Float? = null
77
61
 
78
- // Padding
79
- private var paddingTop: Int = 0
80
- private var paddingLeft: Int = 0
81
- private var paddingBottom: Int = 0
82
- private var paddingRight: Int = 0
62
+ // Theme
63
+ private var theme: String = "system"
83
64
 
84
- // region View Lifecycle
65
+ // Edge Insets
66
+ private var edgeInsets: EdgeInsets = EdgeInsets()
85
67
 
86
- override fun addView(child: View?, index: Int) {
87
- super.addView(child, index)
88
- when (child) {
89
- is LuggMapWrapperView -> mapWrapperView = child
68
+ // region MapProvider
90
69
 
91
- is LuggMarkerView -> {
92
- child.delegate = this
93
- syncMarkerView(child)
94
- }
70
+ override fun initializeMap(wrapperView: View, latitude: Double, longitude: Double, zoom: Float) {
71
+ if (mapView != null) return
95
72
 
96
- is LuggPolylineView -> {
97
- child.delegate = this
98
- syncPolylineView(child)
99
- }
100
- }
101
- }
102
-
103
- override fun removeViewAt(index: Int) {
104
- val view = getChildAt(index)
105
- if (view is LuggMarkerView) {
106
- Log.d(TAG, "removing markerView: ${view.name}")
107
- view.marker?.remove()
108
- view.marker = null
109
- } else if (view is LuggPolylineView) {
110
- polylineAnimators[view]?.destroy()
111
- polylineAnimators.remove(view)
112
- view.polyline?.remove()
113
- view.polyline = null
114
- }
115
- super.removeViewAt(index)
116
- }
73
+ initialLatitude = latitude
74
+ initialLongitude = longitude
75
+ initialZoom = zoom
117
76
 
118
- override fun onAttachedToWindow() {
119
- super.onAttachedToWindow()
120
- if (mapView == null && mapWrapperView != null) {
121
- initializeMap()
77
+ val options = GoogleMapOptions().mapId(mapId)
78
+ mapView = MapView(context, options).also { view ->
79
+ view.onCreate(null)
80
+ view.onResume()
81
+ view.getMapAsync(this)
82
+ (wrapperView as android.view.ViewGroup).addView(view)
122
83
  }
123
84
  }
124
85
 
125
- fun onDropViewInstance() {
126
- Log.d(TAG, "dropping mapView instance")
86
+ override fun destroy() {
127
87
  pendingMarkerViews.clear()
128
88
  pendingPolylineViews.clear()
129
89
  polylineAnimators.values.forEach { it.destroy() }
130
90
  polylineAnimators.clear()
91
+ googleMap?.setOnCameraMoveStartedListener(null)
92
+ googleMap?.setOnCameraMoveListener(null)
93
+ googleMap?.setOnCameraIdleListener(null)
131
94
  googleMap?.clear()
132
95
  googleMap = null
133
- isMapReady = false
96
+ _isMapReady = false
134
97
  mapView?.onPause()
135
98
  mapView?.onDestroy()
136
99
  mapView = null
137
- mapWrapperView = null
138
- }
139
-
140
- // endregion
141
-
142
- // region Map Initialization
143
-
144
- private fun initializeMap() {
145
- if (mapView != null || mapWrapperView == null) return
146
-
147
- val options = GoogleMapOptions().mapId(mapId)
148
- mapView = MapView(context, options).also { view ->
149
- view.onCreate(null)
150
- view.onResume()
151
- view.getMapAsync(this)
152
- mapWrapperView?.addView(
153
- view,
154
- LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
155
- )
156
- }
157
100
  }
158
101
 
159
102
  override fun onMapReady(map: GoogleMap) {
160
103
  googleMap = map
161
- isMapReady = true
104
+ _isMapReady = true
162
105
 
163
106
  val position = LatLng(initialLatitude, initialLongitude)
164
107
  map.moveCamera(CameraUpdateFactory.newLatLngZoom(position, initialZoom))
@@ -169,53 +112,69 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
169
112
 
170
113
  applyUiSettings()
171
114
  applyZoomLimits()
172
- applyPadding()
115
+ applyEdgeInsets()
116
+ applyTheme()
173
117
  applyUserLocation()
174
118
  processPendingMarkers()
175
119
  processPendingPolylines()
176
120
 
177
- eventDelegate?.onReady(this)
121
+ delegate?.mapProviderDidReady()
178
122
  }
179
123
 
124
+ // endregion
125
+
126
+ // region Camera Listeners
127
+
180
128
  override fun onCameraMoveStarted(reason: Int) {
181
129
  isDragging = reason == GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE
130
+ if (isDragging) {
131
+ polylineAnimators.values.forEach { it.pause() }
132
+ }
182
133
  }
183
134
 
184
135
  override fun onCameraMove() {
185
136
  val map = googleMap ?: return
186
137
  val position = map.cameraPosition
187
- eventDelegate?.onCameraMove(this, position.target.latitude, position.target.longitude, position.zoom, isDragging)
138
+ delegate?.mapProviderDidMoveCamera(position.target.latitude, position.target.longitude, position.zoom, isDragging)
188
139
  }
189
140
 
190
141
  override fun onCameraIdle() {
191
142
  val map = googleMap ?: return
192
143
  val position = map.cameraPosition
193
- eventDelegate?.onCameraIdle(this, position.target.latitude, position.target.longitude, position.zoom, isDragging)
144
+ delegate?.mapProviderDidIdleCamera(position.target.latitude, position.target.longitude, position.zoom, isDragging)
145
+ if (isDragging) {
146
+ polylineAnimators.values.forEach { it.resume() }
147
+ }
194
148
  isDragging = false
195
149
  }
196
150
 
197
- private fun applyUiSettings() {
198
- googleMap?.uiSettings?.apply {
199
- isZoomGesturesEnabled = zoomEnabled
200
- isScrollGesturesEnabled = scrollEnabled
201
- isRotateGesturesEnabled = rotateEnabled
202
- isTiltGesturesEnabled = pitchEnabled
203
- }
151
+ // endregion
152
+
153
+ // region Props
154
+
155
+ override fun setZoomEnabled(enabled: Boolean) {
156
+ zoomEnabled = enabled
157
+ googleMap?.uiSettings?.isZoomGesturesEnabled = enabled
204
158
  }
205
159
 
206
- private fun applyZoomLimits() {
207
- googleMap?.apply {
208
- minZoom?.let { setMinZoomPreference(it) }
209
- maxZoom?.let { setMaxZoomPreference(it) }
210
- }
160
+ override fun setScrollEnabled(enabled: Boolean) {
161
+ scrollEnabled = enabled
162
+ googleMap?.uiSettings?.isScrollGesturesEnabled = enabled
211
163
  }
212
164
 
213
- private fun applyPadding() {
214
- googleMap?.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom)
165
+ override fun setRotateEnabled(enabled: Boolean) {
166
+ rotateEnabled = enabled
167
+ googleMap?.uiSettings?.isRotateGesturesEnabled = enabled
168
+ }
169
+
170
+ override fun setPitchEnabled(enabled: Boolean) {
171
+ pitchEnabled = enabled
172
+ googleMap?.uiSettings?.isTiltGesturesEnabled = enabled
215
173
  }
216
174
 
217
175
  @SuppressLint("MissingPermission")
218
- private fun applyUserLocation() {
176
+ override fun setUserLocationEnabled(enabled: Boolean) {
177
+ userLocationEnabled = enabled
219
178
  val hasPermission =
220
179
  context.checkSelfPermission(
221
180
  android.Manifest.permission.ACCESS_FINE_LOCATION
@@ -225,12 +184,59 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
225
184
  googleMap?.isMyLocationEnabled = userLocationEnabled && hasPermission
226
185
  }
227
186
 
228
- // endregion
187
+ override fun setUserLocationButtonEnabled(enabled: Boolean) {
188
+ userLocationButtonEnabled = enabled
189
+ googleMap?.uiSettings?.isMyLocationButtonEnabled = enabled
190
+ }
229
191
 
230
- // region PolylineViewDelegate
192
+ override fun setTheme(value: String) {
193
+ theme = value
194
+ applyTheme()
195
+ }
231
196
 
232
- override fun polylineViewDidUpdate(polylineView: LuggPolylineView) {
233
- syncPolylineView(polylineView)
197
+ override fun setMinZoom(zoom: Double) {
198
+ minZoom = if (zoom > 0) zoom.toFloat() else null
199
+ googleMap?.let { map ->
200
+ minZoom?.let { map.setMinZoomPreference(it) } ?: map.resetMinMaxZoomPreference()
201
+ }
202
+ }
203
+
204
+ override fun setMaxZoom(zoom: Double) {
205
+ maxZoom = if (zoom > 0) zoom.toFloat() else null
206
+ googleMap?.let { map ->
207
+ maxZoom?.let { map.setMaxZoomPreference(it) } ?: map.resetMinMaxZoomPreference()
208
+ }
209
+ }
210
+
211
+ override fun setEdgeInsets(edgeInsets: EdgeInsets) {
212
+ val oldInsets = this.edgeInsets
213
+ this.edgeInsets = edgeInsets
214
+ applyEdgeInsets()
215
+
216
+ val map = googleMap
217
+ if (map != null && oldInsets != edgeInsets) {
218
+ mapView?.post {
219
+ setEdgeInsets(edgeInsets, 0)
220
+ }
221
+ }
222
+ }
223
+
224
+ override fun setEdgeInsets(edgeInsets: EdgeInsets, duration: Int) {
225
+ val map = googleMap
226
+ val oldInsets = this.edgeInsets
227
+ this.edgeInsets = edgeInsets
228
+
229
+ if (map != null && oldInsets != edgeInsets) {
230
+ val cameraUpdate = CameraUpdateFactory.newCameraPosition(map.cameraPosition)
231
+ applyEdgeInsets()
232
+ when {
233
+ duration < 0 -> map.animateCamera(cameraUpdate)
234
+ duration > 0 -> map.animateCamera(cameraUpdate, duration, null)
235
+ else -> map.moveCamera(cameraUpdate)
236
+ }
237
+ } else {
238
+ applyEdgeInsets()
239
+ }
234
240
  }
235
241
 
236
242
  // endregion
@@ -239,16 +245,12 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
239
245
 
240
246
  override fun markerViewDidLayout(markerView: LuggMarkerView) {
241
247
  if (googleMap == null) {
242
- if (!pendingMarkerViews.contains(markerView)) {
243
- pendingMarkerViews.add(markerView)
244
- }
248
+ pendingMarkerViews.add(markerView)
245
249
  return
246
250
  }
247
251
 
248
252
  if (markerView.hasCustomView) {
249
- // Recreate marker with custom view
250
- markerView.marker?.remove()
251
- addMarkerViewToMap(markerView)
253
+ markerView.updateIcon { addMarkerViewToMap(markerView) }
252
254
  } else {
253
255
  syncMarkerView(markerView)
254
256
  }
@@ -260,18 +262,33 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
260
262
 
261
263
  // endregion
262
264
 
265
+ // region PolylineViewDelegate
266
+
267
+ override fun polylineViewDidUpdate(polylineView: LuggPolylineView) {
268
+ syncPolylineView(polylineView)
269
+ }
270
+
271
+ // endregion
272
+
263
273
  // region Marker Management
264
274
 
275
+ override fun addMarkerView(markerView: LuggMarkerView) {
276
+ markerView.delegate = this
277
+ syncMarkerView(markerView)
278
+ }
279
+
280
+ override fun removeMarkerView(markerView: LuggMarkerView) {
281
+ markerView.marker?.remove()
282
+ markerView.marker = null
283
+ }
284
+
265
285
  private fun syncMarkerView(markerView: LuggMarkerView) {
266
286
  if (googleMap == null) {
267
- if (!pendingMarkerViews.contains(markerView)) {
268
- pendingMarkerViews.add(markerView)
269
- }
287
+ pendingMarkerViews.add(markerView)
270
288
  return
271
289
  }
272
290
 
273
291
  if (markerView.marker == null) {
274
- // Custom views need layout first before adding to map
275
292
  if (markerView.hasCustomView) return
276
293
  addMarkerViewToMap(markerView)
277
294
  return
@@ -282,57 +299,59 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
282
299
  title = markerView.title
283
300
  snippet = markerView.description
284
301
  setAnchor(markerView.anchorX, markerView.anchorY)
285
- if (!markerView.hasCustomView) {
286
- iconView = null
287
- }
302
+ zIndex = markerView.zIndex
303
+ rotation = markerView.rotate
304
+ }
305
+
306
+ if (markerView.hasCustomView && markerView.scaleChanged) {
307
+ markerView.applyScaleToMarker()
308
+ markerView.clearScaleChanged()
288
309
  }
289
310
  }
290
311
 
291
312
  private fun processPendingMarkers() {
292
313
  if (googleMap == null) return
293
-
294
- Log.d(TAG, "processing pending markers ${pendingMarkerViews.size}")
295
314
  pendingMarkerViews.forEach { addMarkerViewToMap(it) }
296
315
  pendingMarkerViews.clear()
297
316
  }
298
317
 
299
318
  private fun addMarkerViewToMap(markerView: LuggMarkerView) {
300
- val map = googleMap ?: run {
301
- RNLog.w(reactContext, "Lugg: addMarkerViewToMap called without a map")
302
- return
303
- }
319
+ val map = googleMap ?: return
304
320
 
305
321
  val position = LatLng(markerView.latitude, markerView.longitude)
306
- val iconView = markerView.iconView
307
-
308
- (iconView.parent as? ViewGroup)?.removeView(iconView)
309
-
310
322
  val options = AdvancedMarkerOptions()
311
323
  .position(position)
312
324
  .title(markerView.title)
313
325
  .snippet(markerView.description)
314
- .collisionBehavior(CollisionBehavior.REQUIRED)
315
-
316
- Log.d(TAG, "adding marker: ${markerView.name} customview: ${markerView.hasCustomView}")
317
- if (markerView.hasCustomView) {
318
- options.iconView(iconView)
319
- }
320
326
 
321
327
  val marker = map.addMarker(options) as AdvancedMarker
322
328
  marker.setAnchor(markerView.anchorX, markerView.anchorY)
329
+ marker.zIndex = markerView.zIndex
330
+ marker.rotation = markerView.rotate
323
331
 
324
332
  markerView.marker = marker
333
+ markerView.applyIconToMarker()
325
334
  }
326
335
 
327
336
  // endregion
328
337
 
329
338
  // region Polyline Management
330
339
 
340
+ override fun addPolylineView(polylineView: LuggPolylineView) {
341
+ polylineView.delegate = this
342
+ syncPolylineView(polylineView)
343
+ }
344
+
345
+ override fun removePolylineView(polylineView: LuggPolylineView) {
346
+ polylineAnimators[polylineView]?.destroy()
347
+ polylineAnimators.remove(polylineView)
348
+ polylineView.polyline?.remove()
349
+ polylineView.polyline = null
350
+ }
351
+
331
352
  private fun syncPolylineView(polylineView: LuggPolylineView) {
332
353
  if (googleMap == null) {
333
- if (!pendingPolylineViews.contains(polylineView)) {
334
- pendingPolylineViews.add(polylineView)
335
- }
354
+ pendingPolylineViews.add(polylineView)
336
355
  return
337
356
  }
338
357
 
@@ -342,11 +361,13 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
342
361
  }
343
362
 
344
363
  polylineView.polyline?.width = polylineView.strokeWidth.dpToPx()
364
+ polylineView.polyline?.zIndex = polylineView.zIndex
345
365
 
346
366
  polylineAnimators[polylineView]?.apply {
347
367
  coordinates = polylineView.coordinates
348
368
  strokeColors = polylineView.strokeColors
349
369
  strokeWidth = polylineView.strokeWidth.dpToPx()
370
+ animatedOptions = polylineView.animatedOptions
350
371
  animated = polylineView.animated
351
372
  update()
352
373
  }
@@ -354,7 +375,6 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
354
375
 
355
376
  private fun processPendingPolylines() {
356
377
  if (googleMap == null) return
357
-
358
378
  pendingPolylineViews.forEach { addPolylineViewToMap(it) }
359
379
  pendingPolylineViews.clear()
360
380
  }
@@ -364,6 +384,7 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
364
384
 
365
385
  val options = PolylineOptions()
366
386
  .width(polylineView.strokeWidth.dpToPx())
387
+ .zIndex(polylineView.zIndex)
367
388
 
368
389
  val polyline = map.addPolyline(options)
369
390
  polylineView.polyline = polyline
@@ -373,6 +394,7 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
373
394
  coordinates = polylineView.coordinates
374
395
  strokeColors = polylineView.strokeColors
375
396
  strokeWidth = polylineView.strokeWidth.dpToPx()
397
+ animatedOptions = polylineView.animatedOptions
376
398
  animated = polylineView.animated
377
399
  update()
378
400
  }
@@ -382,83 +404,25 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
382
404
 
383
405
  // endregion
384
406
 
385
- // region Property Setters
386
-
387
- fun setMapId(value: String?) {
388
- if (value.isNullOrEmpty()) return
389
-
390
- if (mapView != null) {
391
- RNLog.w(reactContext, "Lugg: mapId cannot be changed after map is initialized")
392
- return
393
- }
394
-
395
- mapId = value
396
- }
397
-
398
- fun setInitialCoordinate(latitude: Double, longitude: Double) {
399
- initialLatitude = latitude
400
- initialLongitude = longitude
401
- }
402
-
403
- fun setInitialZoom(zoom: Double) {
404
- initialZoom = zoom.toFloat()
405
- }
406
-
407
- fun setZoomEnabled(enabled: Boolean) {
408
- zoomEnabled = enabled
409
- googleMap?.uiSettings?.isZoomGesturesEnabled = enabled
410
- }
411
-
412
- fun setScrollEnabled(enabled: Boolean) {
413
- scrollEnabled = enabled
414
- googleMap?.uiSettings?.isScrollGesturesEnabled = enabled
415
- }
416
-
417
- fun setRotateEnabled(enabled: Boolean) {
418
- rotateEnabled = enabled
419
- googleMap?.uiSettings?.isRotateGesturesEnabled = enabled
420
- }
421
-
422
- fun setPitchEnabled(enabled: Boolean) {
423
- pitchEnabled = enabled
424
- googleMap?.uiSettings?.isTiltGesturesEnabled = enabled
425
- }
426
-
427
- fun setUserLocationEnabled(enabled: Boolean) {
428
- userLocationEnabled = enabled
429
- applyUserLocation()
430
- }
407
+ // region Lifecycle
431
408
 
432
- fun setMinZoom(zoom: Double) {
433
- minZoom = if (zoom > 0) zoom.toFloat() else null
434
- googleMap?.let { map ->
435
- minZoom?.let { map.setMinZoomPreference(it) } ?: map.resetMinMaxZoomPreference()
436
- }
409
+ override fun pauseAnimations() {
410
+ polylineAnimators.values.forEach { it.pause() }
437
411
  }
438
412
 
439
- fun setMaxZoom(zoom: Double) {
440
- maxZoom = if (zoom > 0) zoom.toFloat() else null
441
- googleMap?.let { map ->
442
- maxZoom?.let { map.setMaxZoomPreference(it) } ?: map.resetMinMaxZoomPreference()
443
- }
444
- }
445
-
446
- fun setMapPadding(top: Int, left: Int, bottom: Int, right: Int) {
447
- paddingTop = top
448
- paddingLeft = left
449
- paddingBottom = bottom
450
- paddingRight = right
451
- applyPadding()
413
+ override fun resumeAnimations() {
414
+ polylineAnimators.values.forEach { it.resume() }
452
415
  }
453
416
 
454
417
  // endregion
455
418
 
456
419
  // region Commands
457
420
 
458
- fun moveCamera(latitude: Double, longitude: Double, zoom: Double, duration: Int) {
421
+ override fun moveCamera(latitude: Double, longitude: Double, zoom: Double, duration: Int) {
459
422
  val map = googleMap ?: return
460
423
  val position = LatLng(latitude, longitude)
461
- val cameraUpdate = CameraUpdateFactory.newLatLngZoom(position, zoom.toFloat())
424
+ val targetZoom = if (zoom > 0) zoom.toFloat() else map.cameraPosition.zoom
425
+ val cameraUpdate = CameraUpdateFactory.newLatLngZoom(position, targetZoom)
462
426
  when {
463
427
  duration < 0 -> map.animateCamera(cameraUpdate)
464
428
  duration > 0 -> map.animateCamera(cameraUpdate, duration, null)
@@ -466,32 +430,34 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
466
430
  }
467
431
  }
468
432
 
469
- fun fitCoordinates(
470
- coordinates: List<LatLng>,
471
- paddingTop: Int,
472
- paddingLeft: Int,
473
- paddingBottom: Int,
474
- paddingRight: Int,
433
+ override fun fitCoordinates(
434
+ coordinates: List<Any>,
435
+ edgeInsetsTop: Int,
436
+ edgeInsetsLeft: Int,
437
+ edgeInsetsBottom: Int,
438
+ edgeInsetsRight: Int,
475
439
  duration: Int
476
440
  ) {
477
441
  val map = googleMap ?: return
478
442
  if (coordinates.isEmpty()) return
479
443
 
444
+ val latLngs = coordinates.filterIsInstance<LatLng>()
445
+ if (latLngs.isEmpty()) return
446
+
480
447
  val boundsBuilder = com.google.android.gms.maps.model.LatLngBounds.Builder()
481
- coordinates.forEach { boundsBuilder.include(it) }
448
+ latLngs.forEach { boundsBuilder.include(it) }
482
449
  val bounds = boundsBuilder.build()
483
450
 
484
- val top = paddingTop.toFloat().dpToPx().toInt()
485
- val left = paddingLeft.toFloat().dpToPx().toInt()
486
- val bottom = paddingBottom.toFloat().dpToPx().toInt()
487
- val right = paddingRight.toFloat().dpToPx().toInt()
451
+ val top = edgeInsetsTop.toFloat().dpToPx().toInt()
452
+ val left = edgeInsetsLeft.toFloat().dpToPx().toInt()
453
+ val bottom = edgeInsetsBottom.toFloat().dpToPx().toInt()
454
+ val right = edgeInsetsRight.toFloat().dpToPx().toInt()
488
455
 
489
- // Set padding before camera update, then restore after
490
456
  map.setPadding(
491
- this.paddingLeft + left,
492
- this.paddingTop + top,
493
- this.paddingRight + right,
494
- this.paddingBottom + bottom
457
+ edgeInsets.left + left,
458
+ edgeInsets.top + top,
459
+ edgeInsets.right + right,
460
+ edgeInsets.bottom + bottom
495
461
  )
496
462
 
497
463
  val cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, 0)
@@ -502,14 +468,58 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
502
468
  else -> map.moveCamera(cameraUpdate)
503
469
  }
504
470
 
505
- // Restore base padding
506
- map.setPadding(this.paddingLeft, this.paddingTop, this.paddingRight, this.paddingBottom)
471
+ map.setPadding(edgeInsets.left, edgeInsets.top, edgeInsets.right, edgeInsets.bottom)
472
+ }
473
+
474
+ // endregion
475
+
476
+ // region Private
477
+
478
+ private fun applyUiSettings() {
479
+ googleMap?.uiSettings?.apply {
480
+ isZoomGesturesEnabled = zoomEnabled
481
+ isScrollGesturesEnabled = scrollEnabled
482
+ isRotateGesturesEnabled = rotateEnabled
483
+ isTiltGesturesEnabled = pitchEnabled
484
+ isMyLocationButtonEnabled = userLocationButtonEnabled
485
+ }
486
+ }
487
+
488
+ private fun applyZoomLimits() {
489
+ googleMap?.apply {
490
+ minZoom?.let { setMinZoomPreference(it) }
491
+ maxZoom?.let { setMaxZoomPreference(it) }
492
+ }
493
+ }
494
+
495
+ private fun applyEdgeInsets() {
496
+ googleMap?.setPadding(edgeInsets.left, edgeInsets.top, edgeInsets.right, edgeInsets.bottom)
497
+ }
498
+
499
+ private fun applyTheme() {
500
+ val colorScheme = when (theme) {
501
+ "dark" -> MapColorScheme.DARK
502
+ "light" -> MapColorScheme.LIGHT
503
+ else -> MapColorScheme.FOLLOW_SYSTEM
504
+ }
505
+ googleMap?.setMapColorScheme(colorScheme)
506
+ }
507
+
508
+ @SuppressLint("MissingPermission")
509
+ private fun applyUserLocation() {
510
+ val hasPermission =
511
+ context.checkSelfPermission(
512
+ android.Manifest.permission.ACCESS_FINE_LOCATION
513
+ ) == android.content.pm.PackageManager.PERMISSION_GRANTED ||
514
+ context.checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) ==
515
+ android.content.pm.PackageManager.PERMISSION_GRANTED
516
+ googleMap?.isMyLocationEnabled = userLocationEnabled && hasPermission
517
+ googleMap?.uiSettings?.isMyLocationButtonEnabled = userLocationButtonEnabled
507
518
  }
508
519
 
509
520
  // endregion
510
521
 
511
522
  companion object {
512
- private const val TAG = "Lugg"
513
523
  const val DEMO_MAP_ID = "DEMO_MAP_ID"
514
524
  }
515
525
  }