@lugg/maps 0.2.0-alpha.8 → 1.0.0-beta.0

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 (173) hide show
  1. package/README.md +20 -19
  2. package/android/src/main/java/com/luggmaps/LuggMapView.kt +290 -0
  3. package/android/src/main/java/com/luggmaps/{LuggGoogleMapViewManager.kt → LuggMapViewManager.kt} +99 -36
  4. package/android/src/main/java/com/luggmaps/LuggMapWrapperView.kt +13 -5
  5. package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +157 -13
  6. package/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt +28 -0
  7. package/android/src/main/java/com/luggmaps/LuggPackage.kt +1 -4
  8. package/android/src/main/java/com/luggmaps/LuggPolygonView.kt +85 -0
  9. package/android/src/main/java/com/luggmaps/LuggPolygonViewManager.kt +100 -0
  10. package/android/src/main/java/com/luggmaps/LuggPolylineView.kt +16 -0
  11. package/android/src/main/java/com/luggmaps/LuggPolylineViewManager.kt +22 -0
  12. package/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt +698 -0
  13. package/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt +60 -0
  14. package/android/src/main/java/com/luggmaps/core/PolylineAnimator.kt +89 -24
  15. package/android/src/main/java/com/luggmaps/events/LongPressEvent.kt +29 -0
  16. package/android/src/main/java/com/luggmaps/events/MarkerDragEvent.kt +41 -0
  17. package/android/src/main/java/com/luggmaps/events/MarkerPressEvent.kt +34 -0
  18. package/android/src/main/java/com/luggmaps/events/PolygonPressEvent.kt +12 -0
  19. package/android/src/main/java/com/luggmaps/events/PressEvent.kt +29 -0
  20. package/ios/LuggMapView.h +9 -0
  21. package/ios/LuggMapView.mm +343 -0
  22. package/ios/LuggMarkerView.h +13 -0
  23. package/ios/LuggMarkerView.mm +119 -0
  24. package/ios/LuggPolygonView.h +31 -0
  25. package/ios/LuggPolygonView.mm +151 -0
  26. package/ios/LuggPolylineView.h +4 -0
  27. package/ios/LuggPolylineView.mm +23 -0
  28. package/ios/core/AppleMapProvider.h +15 -0
  29. package/ios/core/AppleMapProvider.mm +1029 -0
  30. package/ios/core/GMSPolylineAnimator.h +3 -0
  31. package/ios/core/GMSPolylineAnimator.m +101 -15
  32. package/ios/core/GoogleMapProvider.h +13 -0
  33. package/ios/core/GoogleMapProvider.mm +689 -0
  34. package/ios/core/MKPolylineAnimator.h +4 -0
  35. package/ios/core/MKPolylineAnimator.m +167 -42
  36. package/ios/core/MapProviderDelegate.h +84 -0
  37. package/ios/core/PolylineAnimatorBase.h +24 -1
  38. package/ios/core/PolylineAnimatorBase.m +82 -19
  39. package/ios/events/LongPressEvent.h +29 -0
  40. package/ios/events/MarkerDragEvent.h +69 -0
  41. package/ios/events/MarkerPressEvent.h +29 -0
  42. package/ios/events/PolygonPressEvent.h +20 -0
  43. package/ios/events/PressEvent.h +29 -0
  44. package/ios/extensions/MKMapView+Zoom.h +2 -0
  45. package/ios/extensions/MKMapView+Zoom.m +14 -4
  46. package/lib/module/MapProvider.js +2 -4
  47. package/lib/module/MapProvider.js.map +1 -1
  48. package/lib/module/MapProvider.web.js +12 -7
  49. package/lib/module/MapProvider.web.js.map +1 -1
  50. package/lib/module/MapView.js +33 -16
  51. package/lib/module/MapView.js.map +1 -1
  52. package/lib/module/MapView.web.js +315 -217
  53. package/lib/module/MapView.web.js.map +1 -1
  54. package/lib/module/components/GeoJson.js +159 -0
  55. package/lib/module/components/GeoJson.js.map +1 -0
  56. package/lib/module/components/GeoJson.types.js +4 -0
  57. package/lib/module/components/GeoJson.types.js.map +1 -0
  58. package/lib/module/components/Marker.js +28 -4
  59. package/lib/module/components/Marker.js.map +1 -1
  60. package/lib/module/components/Marker.types.js +4 -0
  61. package/lib/module/components/Marker.types.js.map +1 -0
  62. package/lib/module/components/Marker.web.js +98 -27
  63. package/lib/module/components/Marker.web.js.map +1 -1
  64. package/lib/module/components/Polygon.js +46 -0
  65. package/lib/module/components/Polygon.js.map +1 -0
  66. package/lib/module/components/Polygon.types.js +4 -0
  67. package/lib/module/components/Polygon.types.js.map +1 -0
  68. package/lib/module/components/Polygon.web.js +87 -0
  69. package/lib/module/components/Polygon.web.js.map +1 -0
  70. package/lib/module/components/Polyline.js +17 -4
  71. package/lib/module/components/Polyline.js.map +1 -1
  72. package/lib/module/components/Polyline.types.js +4 -0
  73. package/lib/module/components/Polyline.types.js.map +1 -0
  74. package/lib/module/components/Polyline.web.js +108 -31
  75. package/lib/module/components/Polyline.web.js.map +1 -1
  76. package/lib/module/components/index.js +4 -2
  77. package/lib/module/components/index.js.map +1 -1
  78. package/lib/module/components/index.web.js +2 -0
  79. package/lib/module/components/index.web.js.map +1 -1
  80. package/lib/module/fabric/{LuggGoogleMapViewNativeComponent.ts → LuggMapViewNativeComponent.ts} +43 -7
  81. package/lib/module/fabric/LuggMarkerViewNativeComponent.ts +49 -1
  82. package/lib/module/fabric/LuggPolygonViewNativeComponent.ts +25 -0
  83. package/lib/module/fabric/LuggPolylineViewNativeComponent.ts +8 -0
  84. package/lib/module/index.js +4 -1
  85. package/lib/module/index.js.map +1 -1
  86. package/lib/module/index.web.js +4 -1
  87. package/lib/module/index.web.js.map +1 -1
  88. package/lib/typescript/src/MapProvider.d.ts +1 -1
  89. package/lib/typescript/src/MapProvider.d.ts.map +1 -1
  90. package/lib/typescript/src/MapProvider.web.d.ts +17 -1
  91. package/lib/typescript/src/MapProvider.web.d.ts.map +1 -1
  92. package/lib/typescript/src/MapView.d.ts +4 -4
  93. package/lib/typescript/src/MapView.d.ts.map +1 -1
  94. package/lib/typescript/src/MapView.types.d.ts +35 -9
  95. package/lib/typescript/src/MapView.types.d.ts.map +1 -1
  96. package/lib/typescript/src/MapView.web.d.ts +2 -11
  97. package/lib/typescript/src/MapView.web.d.ts.map +1 -1
  98. package/lib/typescript/src/components/GeoJson.d.ts +4 -0
  99. package/lib/typescript/src/components/GeoJson.d.ts.map +1 -0
  100. package/lib/typescript/src/components/GeoJson.types.d.ts +79 -0
  101. package/lib/typescript/src/components/GeoJson.types.d.ts.map +1 -0
  102. package/lib/typescript/src/components/Marker.d.ts +6 -33
  103. package/lib/typescript/src/components/Marker.d.ts.map +1 -1
  104. package/lib/typescript/src/components/Marker.types.d.ts +74 -0
  105. package/lib/typescript/src/components/Marker.types.d.ts.map +1 -0
  106. package/lib/typescript/src/components/Marker.web.d.ts +2 -5
  107. package/lib/typescript/src/components/Marker.web.d.ts.map +1 -1
  108. package/lib/typescript/src/components/Polygon.d.ts +10 -0
  109. package/lib/typescript/src/components/Polygon.d.ts.map +1 -0
  110. package/lib/typescript/src/components/Polygon.types.d.ts +33 -0
  111. package/lib/typescript/src/components/Polygon.types.d.ts.map +1 -0
  112. package/lib/typescript/src/components/Polygon.web.d.ts +3 -0
  113. package/lib/typescript/src/components/Polygon.web.d.ts.map +1 -0
  114. package/lib/typescript/src/components/Polyline.d.ts +6 -21
  115. package/lib/typescript/src/components/Polyline.d.ts.map +1 -1
  116. package/lib/typescript/src/components/Polyline.types.d.ts +53 -0
  117. package/lib/typescript/src/components/Polyline.types.d.ts.map +1 -0
  118. package/lib/typescript/src/components/Polyline.web.d.ts +2 -5
  119. package/lib/typescript/src/components/Polyline.web.d.ts.map +1 -1
  120. package/lib/typescript/src/components/index.d.ts +8 -2
  121. package/lib/typescript/src/components/index.d.ts.map +1 -1
  122. package/lib/typescript/src/components/index.web.d.ts +6 -2
  123. package/lib/typescript/src/components/index.web.d.ts.map +1 -1
  124. package/lib/typescript/src/fabric/{LuggGoogleMapViewNativeComponent.d.ts → LuggMapViewNativeComponent.d.ts} +30 -4
  125. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts.map +1 -0
  126. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts +45 -1
  127. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts.map +1 -1
  128. package/lib/typescript/src/fabric/LuggPolygonViewNativeComponent.d.ts +18 -0
  129. package/lib/typescript/src/fabric/LuggPolygonViewNativeComponent.d.ts.map +1 -0
  130. package/lib/typescript/src/fabric/LuggPolylineViewNativeComponent.d.ts +7 -0
  131. package/lib/typescript/src/fabric/LuggPolylineViewNativeComponent.d.ts.map +1 -1
  132. package/lib/typescript/src/index.d.ts +11 -3
  133. package/lib/typescript/src/index.d.ts.map +1 -1
  134. package/lib/typescript/src/index.web.d.ts +9 -1
  135. package/lib/typescript/src/index.web.d.ts.map +1 -1
  136. package/lib/typescript/src/types.d.ts +11 -0
  137. package/lib/typescript/src/types.d.ts.map +1 -1
  138. package/package.json +10 -9
  139. package/src/MapProvider.tsx +1 -3
  140. package/src/MapProvider.web.tsx +12 -3
  141. package/src/MapView.tsx +32 -26
  142. package/src/MapView.types.ts +43 -9
  143. package/src/MapView.web.tsx +376 -244
  144. package/src/components/GeoJson.tsx +221 -0
  145. package/src/components/GeoJson.types.ts +107 -0
  146. package/src/components/Marker.tsx +45 -36
  147. package/src/components/Marker.types.ts +75 -0
  148. package/src/components/Marker.web.tsx +116 -28
  149. package/src/components/Polygon.tsx +52 -0
  150. package/src/components/Polygon.types.ts +33 -0
  151. package/src/components/Polygon.web.tsx +104 -0
  152. package/src/components/Polyline.tsx +23 -22
  153. package/src/components/Polyline.types.ts +55 -0
  154. package/src/components/Polyline.web.tsx +151 -37
  155. package/src/components/index.ts +16 -2
  156. package/src/components/index.web.ts +6 -2
  157. package/src/fabric/{LuggGoogleMapViewNativeComponent.ts → LuggMapViewNativeComponent.ts} +43 -7
  158. package/src/fabric/LuggMarkerViewNativeComponent.ts +49 -1
  159. package/src/fabric/LuggPolygonViewNativeComponent.ts +25 -0
  160. package/src/fabric/LuggPolylineViewNativeComponent.ts +8 -0
  161. package/src/index.ts +28 -1
  162. package/src/index.web.ts +15 -1
  163. package/src/types.ts +13 -0
  164. package/android/src/main/java/com/luggmaps/LuggGoogleMapView.kt +0 -511
  165. package/ios/LuggAppleMapView.h +0 -16
  166. package/ios/LuggAppleMapView.mm +0 -592
  167. package/ios/LuggGoogleMapView.h +0 -13
  168. package/ios/LuggGoogleMapView.mm +0 -455
  169. package/lib/module/fabric/LuggAppleMapViewNativeComponent.ts +0 -83
  170. package/lib/typescript/src/fabric/LuggAppleMapViewNativeComponent.d.ts +0 -54
  171. package/lib/typescript/src/fabric/LuggAppleMapViewNativeComponent.d.ts.map +0 -1
  172. package/lib/typescript/src/fabric/LuggGoogleMapViewNativeComponent.d.ts.map +0 -1
  173. package/src/fabric/LuggAppleMapViewNativeComponent.ts +0 -83
@@ -0,0 +1,698 @@
1
+ package com.luggmaps.core
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.content.Context
5
+ import android.view.View
6
+ import com.facebook.react.uimanager.PixelUtil.dpToPx
7
+ import com.google.android.gms.maps.CameraUpdateFactory
8
+ import com.google.android.gms.maps.GoogleMap
9
+ import com.google.android.gms.maps.GoogleMapOptions
10
+ import com.google.android.gms.maps.MapView
11
+ import com.google.android.gms.maps.OnMapReadyCallback
12
+ import com.google.android.gms.maps.model.AdvancedMarker
13
+ import com.google.android.gms.maps.model.AdvancedMarkerOptions
14
+ import com.google.android.gms.maps.model.LatLng
15
+ import com.google.android.gms.maps.model.MapColorScheme
16
+ import com.google.android.gms.maps.model.Marker
17
+ import com.google.android.gms.maps.model.Polygon
18
+ import com.google.android.gms.maps.model.PolygonOptions
19
+ import com.google.android.gms.maps.model.PolylineOptions
20
+ import com.luggmaps.LuggMapWrapperView
21
+ import com.luggmaps.LuggMarkerView
22
+ import com.luggmaps.LuggMarkerViewDelegate
23
+ import com.luggmaps.LuggPolygonView
24
+ import com.luggmaps.LuggPolygonViewDelegate
25
+ import com.luggmaps.LuggPolylineView
26
+ import com.luggmaps.LuggPolylineViewDelegate
27
+
28
+ class GoogleMapProvider(private val context: Context) :
29
+ MapProvider,
30
+ OnMapReadyCallback,
31
+ LuggMarkerViewDelegate,
32
+ LuggPolylineViewDelegate,
33
+ LuggPolygonViewDelegate,
34
+ GoogleMap.OnCameraMoveStartedListener,
35
+ GoogleMap.OnCameraMoveListener,
36
+ GoogleMap.OnCameraIdleListener,
37
+ GoogleMap.OnMapClickListener,
38
+ GoogleMap.OnMapLongClickListener,
39
+ GoogleMap.OnPolygonClickListener,
40
+ GoogleMap.OnMarkerClickListener,
41
+ GoogleMap.OnMarkerDragListener {
42
+
43
+ override var delegate: MapProviderDelegate? = null
44
+ override val isMapReady: Boolean get() = _isMapReady
45
+
46
+ var mapId: String = DEMO_MAP_ID
47
+
48
+ private var wrapperView: LuggMapWrapperView? = null
49
+ private var mapView: MapView? = null
50
+ private var googleMap: GoogleMap? = null
51
+ private var _isMapReady = false
52
+ private var isDragging = false
53
+ private val pendingMarkerViews = mutableSetOf<LuggMarkerView>()
54
+ private val pendingPolylineViews = mutableSetOf<LuggPolylineView>()
55
+ private val pendingPolygonViews = mutableSetOf<LuggPolygonView>()
56
+ private val polylineAnimators = mutableMapOf<LuggPolylineView, PolylineAnimator>()
57
+ private val polygonToViewMap = mutableMapOf<Polygon, LuggPolygonView>()
58
+ private val markerToViewMap = mutableMapOf<Marker, LuggMarkerView>()
59
+ private var tapLocation: LatLng? = null
60
+
61
+ // Initial camera settings
62
+ private var initialLatitude: Double = 0.0
63
+ private var initialLongitude: Double = 0.0
64
+ private var initialZoom: Float = 10f
65
+
66
+ // UI settings
67
+ private var zoomEnabled: Boolean = true
68
+ private var scrollEnabled: Boolean = true
69
+ private var rotateEnabled: Boolean = true
70
+ private var pitchEnabled: Boolean = true
71
+ private var userLocationEnabled: Boolean = false
72
+ private var userLocationButtonEnabled: Boolean = false
73
+
74
+ // Zoom limits
75
+ private var minZoom: Float? = null
76
+ private var maxZoom: Float? = null
77
+
78
+ // Theme
79
+ private var theme: String = "system"
80
+
81
+ // Edge Insets
82
+ private var edgeInsets: EdgeInsets = EdgeInsets()
83
+
84
+ // region MapProvider
85
+
86
+ override fun initializeMap(wrapperView: View, latitude: Double, longitude: Double, zoom: Float) {
87
+ if (mapView != null) return
88
+
89
+ initialLatitude = latitude
90
+ initialLongitude = longitude
91
+ initialZoom = zoom
92
+
93
+ val wrapper = wrapperView as LuggMapWrapperView
94
+ this.wrapperView = wrapper
95
+
96
+ val options = GoogleMapOptions().mapId(mapId)
97
+ mapView = MapView(context, options).also { view ->
98
+ view.onCreate(null)
99
+ view.onResume()
100
+ view.getMapAsync(this)
101
+ wrapper.addView(view)
102
+ }
103
+ }
104
+
105
+ override fun destroy() {
106
+ pendingMarkerViews.clear()
107
+ pendingPolylineViews.clear()
108
+ pendingPolygonViews.clear()
109
+ polylineAnimators.values.forEach { it.destroy() }
110
+ polylineAnimators.clear()
111
+ polygonToViewMap.clear()
112
+ markerToViewMap.clear()
113
+ wrapperView?.touchEventHandler = null
114
+ wrapperView = null
115
+ googleMap?.setOnCameraMoveStartedListener(null)
116
+ googleMap?.setOnCameraMoveListener(null)
117
+ googleMap?.setOnCameraIdleListener(null)
118
+ googleMap?.setOnMapClickListener(null)
119
+ googleMap?.setOnMapLongClickListener(null)
120
+ googleMap?.setOnPolygonClickListener(null)
121
+ googleMap?.setOnMarkerClickListener(null)
122
+ googleMap?.setOnMarkerDragListener(null)
123
+ googleMap?.clear()
124
+ googleMap = null
125
+ _isMapReady = false
126
+ mapView?.onPause()
127
+ mapView?.onDestroy()
128
+ mapView = null
129
+ }
130
+
131
+ override fun onMapReady(map: GoogleMap) {
132
+ googleMap = map
133
+ _isMapReady = true
134
+
135
+ val position = LatLng(initialLatitude, initialLongitude)
136
+ map.moveCamera(CameraUpdateFactory.newLatLngZoom(position, initialZoom))
137
+
138
+ map.setOnCameraMoveStartedListener(this)
139
+ map.setOnCameraMoveListener(this)
140
+ map.setOnCameraIdleListener(this)
141
+ map.setOnMapClickListener(this)
142
+ map.setOnMapLongClickListener(this)
143
+ map.setOnPolygonClickListener(this)
144
+ map.setOnMarkerClickListener(this)
145
+ map.setOnMarkerDragListener(this)
146
+
147
+ wrapperView?.touchEventHandler = { event ->
148
+ if (event.action == android.view.MotionEvent.ACTION_DOWN) {
149
+ tapLocation = map.projection.fromScreenLocation(android.graphics.Point(event.x.toInt(), event.y.toInt()))
150
+ }
151
+ }
152
+
153
+ applyUiSettings()
154
+ applyZoomLimits()
155
+ applyEdgeInsets()
156
+ applyTheme()
157
+ applyUserLocation()
158
+ processPendingMarkers()
159
+ processPendingPolylines()
160
+ processPendingPolygons()
161
+
162
+ delegate?.mapProviderDidReady()
163
+ }
164
+
165
+ // endregion
166
+
167
+ // region Camera Listeners
168
+
169
+ override fun onCameraMoveStarted(reason: Int) {
170
+ isDragging = reason == GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE
171
+ if (isDragging) {
172
+ polylineAnimators.values.forEach { it.pause() }
173
+ }
174
+ }
175
+
176
+ override fun onCameraMove() {
177
+ val map = googleMap ?: return
178
+ val position = map.cameraPosition
179
+ delegate?.mapProviderDidMoveCamera(position.target.latitude, position.target.longitude, position.zoom, isDragging)
180
+ }
181
+
182
+ override fun onCameraIdle() {
183
+ val map = googleMap ?: return
184
+ val position = map.cameraPosition
185
+ delegate?.mapProviderDidIdleCamera(position.target.latitude, position.target.longitude, position.zoom, isDragging)
186
+ if (isDragging) {
187
+ polylineAnimators.values.forEach { it.resume() }
188
+ }
189
+ isDragging = false
190
+ }
191
+
192
+ override fun onMapClick(latLng: LatLng) {
193
+ val map = googleMap ?: return
194
+ val point = map.projection.toScreenLocation(latLng)
195
+ delegate?.mapProviderDidPress(latLng.latitude, latLng.longitude, point.x.toFloat(), point.y.toFloat())
196
+ }
197
+
198
+ override fun onMapLongClick(latLng: LatLng) {
199
+ val map = googleMap ?: return
200
+ val point = map.projection.toScreenLocation(latLng)
201
+ delegate?.mapProviderDidLongPress(latLng.latitude, latLng.longitude, point.x.toFloat(), point.y.toFloat())
202
+ }
203
+
204
+ override fun onPolygonClick(polygon: Polygon) {
205
+ val polygonView = polygonToViewMap[polygon]
206
+ if (polygonView?.tappable == true) {
207
+ polygonView.emitPressEvent()
208
+ } else {
209
+ onMapClick(tapLocation ?: return)
210
+ }
211
+ }
212
+
213
+ override fun onMarkerClick(marker: Marker): Boolean {
214
+ markerToViewMap[marker]?.let { view ->
215
+ val point = googleMap?.projection?.toScreenLocation(marker.position)
216
+ view.emitPressEvent(point?.x?.toFloat() ?: 0f, point?.y?.toFloat() ?: 0f)
217
+ }
218
+ return false
219
+ }
220
+
221
+ override fun onMarkerDragStart(marker: Marker) {
222
+ markerToViewMap[marker]?.let { view ->
223
+ view.isDragging = true
224
+ view.setCoordinate(marker.position.latitude, marker.position.longitude)
225
+ val point = googleMap?.projection?.toScreenLocation(marker.position)
226
+ view.emitDragStartEvent(point?.x?.toFloat() ?: 0f, point?.y?.toFloat() ?: 0f)
227
+ }
228
+ }
229
+
230
+ override fun onMarkerDrag(marker: Marker) {
231
+ markerToViewMap[marker]?.let { view ->
232
+ view.setCoordinate(marker.position.latitude, marker.position.longitude)
233
+ val point = googleMap?.projection?.toScreenLocation(marker.position)
234
+ view.emitDragChangeEvent(point?.x?.toFloat() ?: 0f, point?.y?.toFloat() ?: 0f)
235
+ }
236
+ }
237
+
238
+ override fun onMarkerDragEnd(marker: Marker) {
239
+ markerToViewMap[marker]?.let { view ->
240
+ view.isDragging = false
241
+ view.setCoordinate(marker.position.latitude, marker.position.longitude)
242
+ val point = googleMap?.projection?.toScreenLocation(marker.position)
243
+ view.emitDragEndEvent(point?.x?.toFloat() ?: 0f, point?.y?.toFloat() ?: 0f)
244
+ }
245
+ }
246
+
247
+ // endregion
248
+
249
+ // region Props
250
+
251
+ override fun setZoomEnabled(enabled: Boolean) {
252
+ zoomEnabled = enabled
253
+ googleMap?.uiSettings?.isZoomGesturesEnabled = enabled
254
+ }
255
+
256
+ override fun setScrollEnabled(enabled: Boolean) {
257
+ scrollEnabled = enabled
258
+ googleMap?.uiSettings?.isScrollGesturesEnabled = enabled
259
+ }
260
+
261
+ override fun setRotateEnabled(enabled: Boolean) {
262
+ rotateEnabled = enabled
263
+ googleMap?.uiSettings?.isRotateGesturesEnabled = enabled
264
+ }
265
+
266
+ override fun setPitchEnabled(enabled: Boolean) {
267
+ pitchEnabled = enabled
268
+ googleMap?.uiSettings?.isTiltGesturesEnabled = enabled
269
+ }
270
+
271
+ @SuppressLint("MissingPermission")
272
+ override fun setUserLocationEnabled(enabled: Boolean) {
273
+ userLocationEnabled = enabled
274
+ val hasPermission =
275
+ context.checkSelfPermission(
276
+ android.Manifest.permission.ACCESS_FINE_LOCATION
277
+ ) == android.content.pm.PackageManager.PERMISSION_GRANTED ||
278
+ context.checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) ==
279
+ android.content.pm.PackageManager.PERMISSION_GRANTED
280
+ googleMap?.isMyLocationEnabled = userLocationEnabled && hasPermission
281
+ }
282
+
283
+ override fun setUserLocationButtonEnabled(enabled: Boolean) {
284
+ userLocationButtonEnabled = enabled
285
+ googleMap?.uiSettings?.isMyLocationButtonEnabled = enabled
286
+ }
287
+
288
+ override fun setTheme(value: String) {
289
+ theme = value
290
+ applyTheme()
291
+ }
292
+
293
+ override fun setMinZoom(zoom: Double) {
294
+ minZoom = if (zoom > 0) zoom.toFloat() else null
295
+ googleMap?.let { map ->
296
+ minZoom?.let { map.setMinZoomPreference(it) } ?: map.resetMinMaxZoomPreference()
297
+ }
298
+ }
299
+
300
+ override fun setMaxZoom(zoom: Double) {
301
+ maxZoom = if (zoom > 0) zoom.toFloat() else null
302
+ googleMap?.let { map ->
303
+ maxZoom?.let { map.setMaxZoomPreference(it) } ?: map.resetMinMaxZoomPreference()
304
+ }
305
+ }
306
+
307
+ override fun setEdgeInsets(edgeInsets: EdgeInsets) {
308
+ val oldInsets = this.edgeInsets
309
+ this.edgeInsets = edgeInsets
310
+ applyEdgeInsets()
311
+
312
+ val map = googleMap
313
+ if (map != null && oldInsets != edgeInsets) {
314
+ mapView?.post {
315
+ setEdgeInsets(edgeInsets, 0)
316
+ }
317
+ }
318
+ }
319
+
320
+ override fun setEdgeInsets(edgeInsets: EdgeInsets, duration: Int) {
321
+ val map = googleMap
322
+ val oldInsets = this.edgeInsets
323
+ this.edgeInsets = edgeInsets
324
+
325
+ if (map != null && oldInsets != edgeInsets) {
326
+ val cameraUpdate = CameraUpdateFactory.newCameraPosition(map.cameraPosition)
327
+ applyEdgeInsets()
328
+ when {
329
+ duration < 0 -> map.animateCamera(cameraUpdate)
330
+ duration > 0 -> map.animateCamera(cameraUpdate, duration, null)
331
+ else -> map.moveCamera(cameraUpdate)
332
+ }
333
+ } else {
334
+ applyEdgeInsets()
335
+ }
336
+ }
337
+
338
+ // endregion
339
+
340
+ // region MarkerViewDelegate
341
+
342
+ override fun markerViewDidLayout(markerView: LuggMarkerView) {
343
+ if (googleMap == null) {
344
+ pendingMarkerViews.add(markerView)
345
+ return
346
+ }
347
+
348
+ if (markerView.hasCustomView) {
349
+ markerView.updateIcon { addMarkerViewToMap(markerView) }
350
+ } else {
351
+ syncMarkerView(markerView)
352
+ }
353
+ }
354
+
355
+ override fun markerViewDidUpdate(markerView: LuggMarkerView) {
356
+ syncMarkerView(markerView)
357
+ }
358
+
359
+ // endregion
360
+
361
+ // region PolylineViewDelegate
362
+
363
+ override fun polylineViewDidUpdate(polylineView: LuggPolylineView) {
364
+ syncPolylineView(polylineView)
365
+ }
366
+
367
+ // endregion
368
+
369
+ // region PolygonViewDelegate
370
+
371
+ override fun polygonViewDidUpdate(polygonView: LuggPolygonView) {
372
+ syncPolygonView(polygonView)
373
+ }
374
+
375
+ // endregion
376
+
377
+ // region Marker Management
378
+
379
+ override fun addMarkerView(markerView: LuggMarkerView) {
380
+ markerView.delegate = this
381
+ syncMarkerView(markerView)
382
+ }
383
+
384
+ override fun removeMarkerView(markerView: LuggMarkerView) {
385
+ markerView.marker?.let { markerToViewMap.remove(it) }
386
+ markerView.marker?.remove()
387
+ markerView.marker = null
388
+ }
389
+
390
+ private fun syncMarkerView(markerView: LuggMarkerView) {
391
+ if (googleMap == null) {
392
+ pendingMarkerViews.add(markerView)
393
+ return
394
+ }
395
+
396
+ if (markerView.marker == null) {
397
+ if (markerView.hasCustomView) return
398
+ addMarkerViewToMap(markerView)
399
+ return
400
+ }
401
+
402
+ markerView.marker?.apply {
403
+ if (!markerView.isDragging) {
404
+ position = LatLng(markerView.latitude, markerView.longitude)
405
+ }
406
+ title = markerView.title
407
+ snippet = markerView.description
408
+ setAnchor(markerView.anchorX, markerView.anchorY)
409
+ zIndex = markerView.zIndex
410
+ rotation = markerView.rotate
411
+ isDraggable = markerView.draggable
412
+ }
413
+
414
+ if (markerView.hasCustomView && markerView.scaleChanged) {
415
+ markerView.applyScaleToMarker()
416
+ markerView.clearScaleChanged()
417
+ }
418
+ }
419
+
420
+ private fun processPendingMarkers() {
421
+ if (googleMap == null) return
422
+ pendingMarkerViews.forEach { addMarkerViewToMap(it) }
423
+ pendingMarkerViews.clear()
424
+ }
425
+
426
+ private fun addMarkerViewToMap(markerView: LuggMarkerView) {
427
+ val map = googleMap ?: return
428
+
429
+ val position = LatLng(markerView.latitude, markerView.longitude)
430
+ val options = AdvancedMarkerOptions()
431
+ .position(position)
432
+ .title(markerView.title)
433
+ .snippet(markerView.description)
434
+
435
+ val marker = map.addMarker(options) as AdvancedMarker
436
+ marker.setAnchor(markerView.anchorX, markerView.anchorY)
437
+ marker.zIndex = markerView.zIndex
438
+ marker.rotation = markerView.rotate
439
+ marker.isDraggable = markerView.draggable
440
+
441
+ markerView.marker = marker
442
+ markerToViewMap[marker] = markerView
443
+ markerView.applyIconToMarker()
444
+ }
445
+
446
+ // endregion
447
+
448
+ // region Polyline Management
449
+
450
+ override fun addPolylineView(polylineView: LuggPolylineView) {
451
+ polylineView.delegate = this
452
+ syncPolylineView(polylineView)
453
+ }
454
+
455
+ override fun removePolylineView(polylineView: LuggPolylineView) {
456
+ polylineAnimators[polylineView]?.destroy()
457
+ polylineAnimators.remove(polylineView)
458
+ polylineView.polyline?.remove()
459
+ polylineView.polyline = null
460
+ }
461
+
462
+ private fun syncPolylineView(polylineView: LuggPolylineView) {
463
+ if (googleMap == null) {
464
+ pendingPolylineViews.add(polylineView)
465
+ return
466
+ }
467
+
468
+ if (polylineView.polyline == null) {
469
+ addPolylineViewToMap(polylineView)
470
+ return
471
+ }
472
+
473
+ polylineView.polyline?.width = polylineView.strokeWidth.dpToPx()
474
+ polylineView.polyline?.zIndex = polylineView.zIndex
475
+
476
+ polylineAnimators[polylineView]?.apply {
477
+ coordinates = polylineView.coordinates
478
+ strokeColors = polylineView.strokeColors
479
+ strokeWidth = polylineView.strokeWidth.dpToPx()
480
+ animatedOptions = polylineView.animatedOptions
481
+ animated = polylineView.animated
482
+ update()
483
+ }
484
+ }
485
+
486
+ private fun processPendingPolylines() {
487
+ if (googleMap == null) return
488
+ pendingPolylineViews.forEach { addPolylineViewToMap(it) }
489
+ pendingPolylineViews.clear()
490
+ }
491
+
492
+ private fun addPolylineViewToMap(polylineView: LuggPolylineView) {
493
+ val map = googleMap ?: return
494
+
495
+ val options = PolylineOptions()
496
+ .width(polylineView.strokeWidth.dpToPx())
497
+ .zIndex(polylineView.zIndex)
498
+
499
+ val polyline = map.addPolyline(options)
500
+ polylineView.polyline = polyline
501
+
502
+ val animator = PolylineAnimator().apply {
503
+ this.polyline = polyline
504
+ coordinates = polylineView.coordinates
505
+ strokeColors = polylineView.strokeColors
506
+ strokeWidth = polylineView.strokeWidth.dpToPx()
507
+ animatedOptions = polylineView.animatedOptions
508
+ animated = polylineView.animated
509
+ update()
510
+ }
511
+
512
+ polylineAnimators[polylineView] = animator
513
+ }
514
+
515
+ // endregion
516
+
517
+ // region Polygon Management
518
+
519
+ override fun addPolygonView(polygonView: LuggPolygonView) {
520
+ polygonView.delegate = this
521
+ syncPolygonView(polygonView)
522
+ }
523
+
524
+ override fun removePolygonView(polygonView: LuggPolygonView) {
525
+ polygonView.polygon?.let { polygonToViewMap.remove(it) }
526
+ polygonView.polygon?.remove()
527
+ polygonView.polygon = null
528
+ }
529
+
530
+ private fun syncPolygonView(polygonView: LuggPolygonView) {
531
+ if (googleMap == null) {
532
+ pendingPolygonViews.add(polygonView)
533
+ return
534
+ }
535
+
536
+ if (polygonView.polygon == null) {
537
+ addPolygonViewToMap(polygonView)
538
+ return
539
+ }
540
+
541
+ polygonView.polygon?.apply {
542
+ points = polygonView.coordinates
543
+ holes = polygonView.holes
544
+ fillColor = polygonView.fillColor
545
+ strokeColor = polygonView.strokeColor
546
+ strokeWidth = polygonView.strokeWidth.dpToPx()
547
+ zIndex = polygonView.zIndex
548
+ isClickable = true
549
+ }
550
+ }
551
+
552
+ private fun processPendingPolygons() {
553
+ if (googleMap == null) return
554
+ pendingPolygonViews.forEach { addPolygonViewToMap(it) }
555
+ pendingPolygonViews.clear()
556
+ }
557
+
558
+ private fun addPolygonViewToMap(polygonView: LuggPolygonView) {
559
+ val map = googleMap ?: return
560
+
561
+ val options = PolygonOptions()
562
+ .addAll(polygonView.coordinates)
563
+ .fillColor(polygonView.fillColor)
564
+ .strokeColor(polygonView.strokeColor)
565
+ .strokeWidth(polygonView.strokeWidth.dpToPx())
566
+ .zIndex(polygonView.zIndex)
567
+ .clickable(true)
568
+
569
+ for (hole in polygonView.holes) {
570
+ options.addHole(hole)
571
+ }
572
+
573
+ val polygon = map.addPolygon(options)
574
+ polygonView.polygon = polygon
575
+ polygonToViewMap[polygon] = polygonView
576
+ }
577
+
578
+ // endregion
579
+
580
+ // region Lifecycle
581
+
582
+ override fun pauseAnimations() {
583
+ polylineAnimators.values.forEach { it.pause() }
584
+ }
585
+
586
+ override fun resumeAnimations() {
587
+ polylineAnimators.values.forEach { it.resume() }
588
+ }
589
+
590
+ // endregion
591
+
592
+ // region Commands
593
+
594
+ override fun moveCamera(latitude: Double, longitude: Double, zoom: Double, duration: Int) {
595
+ val map = googleMap ?: return
596
+ val position = LatLng(latitude, longitude)
597
+ val targetZoom = if (zoom > 0) zoom.toFloat() else map.cameraPosition.zoom
598
+ val cameraUpdate = CameraUpdateFactory.newLatLngZoom(position, targetZoom)
599
+ when {
600
+ duration < 0 -> map.animateCamera(cameraUpdate)
601
+ duration > 0 -> map.animateCamera(cameraUpdate, duration, null)
602
+ else -> map.moveCamera(cameraUpdate)
603
+ }
604
+ }
605
+
606
+ override fun fitCoordinates(
607
+ coordinates: List<Any>,
608
+ edgeInsetsTop: Int,
609
+ edgeInsetsLeft: Int,
610
+ edgeInsetsBottom: Int,
611
+ edgeInsetsRight: Int,
612
+ duration: Int
613
+ ) {
614
+ val map = googleMap ?: return
615
+ if (coordinates.isEmpty()) return
616
+
617
+ val latLngs = coordinates.filterIsInstance<LatLng>()
618
+ if (latLngs.isEmpty()) return
619
+
620
+ val boundsBuilder = com.google.android.gms.maps.model.LatLngBounds.Builder()
621
+ latLngs.forEach { boundsBuilder.include(it) }
622
+ val bounds = boundsBuilder.build()
623
+
624
+ val top = edgeInsetsTop.toFloat().dpToPx().toInt()
625
+ val left = edgeInsetsLeft.toFloat().dpToPx().toInt()
626
+ val bottom = edgeInsetsBottom.toFloat().dpToPx().toInt()
627
+ val right = edgeInsetsRight.toFloat().dpToPx().toInt()
628
+
629
+ map.setPadding(
630
+ edgeInsets.left + left,
631
+ edgeInsets.top + top,
632
+ edgeInsets.right + right,
633
+ edgeInsets.bottom + bottom
634
+ )
635
+
636
+ val cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, 0)
637
+
638
+ when {
639
+ duration < 0 -> map.animateCamera(cameraUpdate)
640
+ duration > 0 -> map.animateCamera(cameraUpdate, duration, null)
641
+ else -> map.moveCamera(cameraUpdate)
642
+ }
643
+
644
+ map.setPadding(edgeInsets.left, edgeInsets.top, edgeInsets.right, edgeInsets.bottom)
645
+ }
646
+
647
+ // endregion
648
+
649
+ // region Private
650
+
651
+ private fun applyUiSettings() {
652
+ googleMap?.uiSettings?.apply {
653
+ isZoomGesturesEnabled = zoomEnabled
654
+ isScrollGesturesEnabled = scrollEnabled
655
+ isRotateGesturesEnabled = rotateEnabled
656
+ isTiltGesturesEnabled = pitchEnabled
657
+ isMyLocationButtonEnabled = userLocationButtonEnabled
658
+ }
659
+ }
660
+
661
+ private fun applyZoomLimits() {
662
+ googleMap?.apply {
663
+ minZoom?.let { setMinZoomPreference(it) }
664
+ maxZoom?.let { setMaxZoomPreference(it) }
665
+ }
666
+ }
667
+
668
+ private fun applyEdgeInsets() {
669
+ googleMap?.setPadding(edgeInsets.left, edgeInsets.top, edgeInsets.right, edgeInsets.bottom)
670
+ }
671
+
672
+ private fun applyTheme() {
673
+ val colorScheme = when (theme) {
674
+ "dark" -> MapColorScheme.DARK
675
+ "light" -> MapColorScheme.LIGHT
676
+ else -> MapColorScheme.FOLLOW_SYSTEM
677
+ }
678
+ googleMap?.setMapColorScheme(colorScheme)
679
+ }
680
+
681
+ @SuppressLint("MissingPermission")
682
+ private fun applyUserLocation() {
683
+ val hasPermission =
684
+ context.checkSelfPermission(
685
+ android.Manifest.permission.ACCESS_FINE_LOCATION
686
+ ) == android.content.pm.PackageManager.PERMISSION_GRANTED ||
687
+ context.checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) ==
688
+ android.content.pm.PackageManager.PERMISSION_GRANTED
689
+ googleMap?.isMyLocationEnabled = userLocationEnabled && hasPermission
690
+ googleMap?.uiSettings?.isMyLocationButtonEnabled = userLocationButtonEnabled
691
+ }
692
+
693
+ // endregion
694
+
695
+ companion object {
696
+ const val DEMO_MAP_ID = "DEMO_MAP_ID"
697
+ }
698
+ }