@lugg/maps 1.0.0-beta.0 → 1.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +4 -1
  2. package/android/build.gradle +1 -1
  3. package/android/src/main/java/com/luggmaps/LuggCalloutView.kt +79 -0
  4. package/android/src/main/java/com/luggmaps/LuggCalloutViewManager.kt +39 -0
  5. package/android/src/main/java/com/luggmaps/LuggCircleView.kt +85 -0
  6. package/android/src/main/java/com/luggmaps/LuggCircleViewManager.kt +79 -0
  7. package/android/src/main/java/com/luggmaps/LuggGroundOverlayView.kt +81 -0
  8. package/android/src/main/java/com/luggmaps/LuggGroundOverlayViewManager.kt +82 -0
  9. package/android/src/main/java/com/luggmaps/LuggMapView.kt +49 -0
  10. package/android/src/main/java/com/luggmaps/LuggMapViewManager.kt +31 -0
  11. package/android/src/main/java/com/luggmaps/LuggMapWrapperView.kt +0 -11
  12. package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +99 -51
  13. package/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt +13 -0
  14. package/android/src/main/java/com/luggmaps/LuggPackage.kt +11 -1
  15. package/android/src/main/java/com/luggmaps/LuggPolylineView.kt +1 -22
  16. package/android/src/main/java/com/luggmaps/LuggTileOverlayView.kt +92 -0
  17. package/android/src/main/java/com/luggmaps/LuggTileOverlayViewManager.kt +80 -0
  18. package/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt +628 -18
  19. package/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt +14 -0
  20. package/android/src/main/java/com/luggmaps/core/PolylineAnimator.kt +12 -7
  21. package/android/src/main/java/com/luggmaps/events/CirclePressEvent.kt +12 -0
  22. package/android/src/main/java/com/luggmaps/events/GroundOverlayPressEvent.kt +12 -0
  23. package/android/src/main/java/com/luggmaps/events/TileOverlayPressEvent.kt +12 -0
  24. package/android/src/main/java/com/luggmaps/extensions/ViewExtensions.kt +11 -0
  25. package/ios/LuggCalloutView.h +23 -0
  26. package/ios/LuggCalloutView.mm +155 -0
  27. package/ios/LuggCircleView.h +31 -0
  28. package/ios/LuggCircleView.mm +133 -0
  29. package/ios/LuggGroundOverlayView.h +30 -0
  30. package/ios/LuggGroundOverlayView.mm +125 -0
  31. package/ios/LuggMapView.mm +84 -2
  32. package/ios/LuggMarkerView.h +5 -0
  33. package/ios/LuggMarkerView.mm +40 -2
  34. package/ios/LuggTileOverlayView.h +31 -0
  35. package/ios/LuggTileOverlayView.mm +130 -0
  36. package/ios/core/AppleMapProvider.mm +838 -34
  37. package/ios/core/GMSPolylineAnimator.m +6 -3
  38. package/ios/core/GoogleMapProvider.mm +506 -19
  39. package/ios/core/LuggAnnotationView.h +12 -0
  40. package/ios/core/LuggAnnotationView.mm +55 -0
  41. package/ios/core/MapProviderDelegate.h +20 -1
  42. package/ios/events/CirclePressEvent.h +20 -0
  43. package/ios/events/GroundOverlayPressEvent.h +20 -0
  44. package/ios/events/TileOverlayPressEvent.h +20 -0
  45. package/lib/module/MapProvider.web.js +7 -2
  46. package/lib/module/MapProvider.web.js.map +1 -1
  47. package/lib/module/MapView.js +12 -1
  48. package/lib/module/MapView.js.map +1 -1
  49. package/lib/module/MapView.web.js +36 -20
  50. package/lib/module/MapView.web.js.map +1 -1
  51. package/lib/module/components/Circle.js +46 -0
  52. package/lib/module/components/Circle.js.map +1 -0
  53. package/lib/module/components/Circle.types.js +4 -0
  54. package/lib/module/components/Circle.types.js.map +1 -0
  55. package/lib/module/components/Circle.web.js +79 -0
  56. package/lib/module/components/Circle.web.js.map +1 -0
  57. package/lib/module/components/GeoJson.js +3 -3
  58. package/lib/module/components/GeoJson.js.map +1 -1
  59. package/lib/module/components/GroundOverlay.js +45 -0
  60. package/lib/module/components/GroundOverlay.js.map +1 -0
  61. package/lib/module/components/GroundOverlay.types.js +4 -0
  62. package/lib/module/components/GroundOverlay.types.js.map +1 -0
  63. package/lib/module/components/GroundOverlay.web.js +77 -0
  64. package/lib/module/components/GroundOverlay.web.js.map +1 -0
  65. package/lib/module/components/Marker.js +41 -6
  66. package/lib/module/components/Marker.js.map +1 -1
  67. package/lib/module/components/Marker.web.js +103 -26
  68. package/lib/module/components/Marker.web.js.map +1 -1
  69. package/lib/module/components/Polygon.web.js +3 -3
  70. package/lib/module/components/Polygon.web.js.map +1 -1
  71. package/lib/module/components/Polyline.web.js +6 -12
  72. package/lib/module/components/Polyline.web.js.map +1 -1
  73. package/lib/module/components/TileOverlay.js +44 -0
  74. package/lib/module/components/TileOverlay.js.map +1 -0
  75. package/lib/module/components/TileOverlay.types.js +4 -0
  76. package/lib/module/components/TileOverlay.types.js.map +1 -0
  77. package/lib/module/components/TileOverlay.web.js +69 -0
  78. package/lib/module/components/TileOverlay.web.js.map +1 -0
  79. package/lib/module/components/index.js +3 -0
  80. package/lib/module/components/index.js.map +1 -1
  81. package/lib/module/components/index.web.js +3 -0
  82. package/lib/module/components/index.web.js.map +1 -1
  83. package/lib/module/fabric/LuggCalloutViewNativeComponent.ts +20 -0
  84. package/lib/module/fabric/LuggCircleViewNativeComponent.ts +25 -0
  85. package/lib/module/fabric/LuggGroundOverlayViewNativeComponent.ts +29 -0
  86. package/lib/module/fabric/LuggMapViewNativeComponent.ts +13 -5
  87. package/lib/module/fabric/LuggMarkerViewNativeComponent.ts +14 -2
  88. package/lib/module/fabric/LuggTileOverlayViewNativeComponent.ts +30 -0
  89. package/lib/module/index.js +3 -0
  90. package/lib/module/index.js.map +1 -1
  91. package/lib/module/index.web.js +3 -0
  92. package/lib/module/index.web.js.map +1 -1
  93. package/lib/typescript/src/MapProvider.web.d.ts +9 -2
  94. package/lib/typescript/src/MapProvider.web.d.ts.map +1 -1
  95. package/lib/typescript/src/MapView.d.ts +1 -1
  96. package/lib/typescript/src/MapView.d.ts.map +1 -1
  97. package/lib/typescript/src/MapView.types.d.ts +36 -1
  98. package/lib/typescript/src/MapView.types.d.ts.map +1 -1
  99. package/lib/typescript/src/MapView.web.d.ts +1 -1
  100. package/lib/typescript/src/MapView.web.d.ts.map +1 -1
  101. package/lib/typescript/src/components/Circle.d.ts +10 -0
  102. package/lib/typescript/src/components/Circle.d.ts.map +1 -0
  103. package/lib/typescript/src/components/Circle.types.d.ts +33 -0
  104. package/lib/typescript/src/components/Circle.types.d.ts.map +1 -0
  105. package/lib/typescript/src/components/Circle.web.d.ts +3 -0
  106. package/lib/typescript/src/components/Circle.web.d.ts.map +1 -0
  107. package/lib/typescript/src/components/GeoJson.d.ts +2 -1
  108. package/lib/typescript/src/components/GeoJson.d.ts.map +1 -1
  109. package/lib/typescript/src/components/GeoJson.types.d.ts +11 -1
  110. package/lib/typescript/src/components/GeoJson.types.d.ts.map +1 -1
  111. package/lib/typescript/src/components/GroundOverlay.d.ts +10 -0
  112. package/lib/typescript/src/components/GroundOverlay.d.ts.map +1 -0
  113. package/lib/typescript/src/components/GroundOverlay.types.d.ts +35 -0
  114. package/lib/typescript/src/components/GroundOverlay.types.d.ts.map +1 -0
  115. package/lib/typescript/src/components/GroundOverlay.web.d.ts +3 -0
  116. package/lib/typescript/src/components/GroundOverlay.web.d.ts.map +1 -0
  117. package/lib/typescript/src/components/Marker.d.ts +6 -3
  118. package/lib/typescript/src/components/Marker.d.ts.map +1 -1
  119. package/lib/typescript/src/components/Marker.types.d.ts +40 -2
  120. package/lib/typescript/src/components/Marker.types.d.ts.map +1 -1
  121. package/lib/typescript/src/components/Marker.web.d.ts +3 -2
  122. package/lib/typescript/src/components/Marker.web.d.ts.map +1 -1
  123. package/lib/typescript/src/components/Polygon.web.d.ts +1 -1
  124. package/lib/typescript/src/components/Polygon.web.d.ts.map +1 -1
  125. package/lib/typescript/src/components/Polyline.web.d.ts +1 -1
  126. package/lib/typescript/src/components/Polyline.web.d.ts.map +1 -1
  127. package/lib/typescript/src/components/TileOverlay.d.ts +10 -0
  128. package/lib/typescript/src/components/TileOverlay.d.ts.map +1 -0
  129. package/lib/typescript/src/components/TileOverlay.types.d.ts +36 -0
  130. package/lib/typescript/src/components/TileOverlay.types.d.ts.map +1 -0
  131. package/lib/typescript/src/components/TileOverlay.web.d.ts +3 -0
  132. package/lib/typescript/src/components/TileOverlay.web.d.ts.map +1 -0
  133. package/lib/typescript/src/components/index.d.ts +7 -1
  134. package/lib/typescript/src/components/index.d.ts.map +1 -1
  135. package/lib/typescript/src/components/index.web.d.ts +7 -1
  136. package/lib/typescript/src/components/index.web.d.ts.map +1 -1
  137. package/lib/typescript/src/fabric/LuggCalloutViewNativeComponent.d.ts +13 -0
  138. package/lib/typescript/src/fabric/LuggCalloutViewNativeComponent.d.ts.map +1 -0
  139. package/lib/typescript/src/fabric/LuggCircleViewNativeComponent.d.ts +18 -0
  140. package/lib/typescript/src/fabric/LuggCircleViewNativeComponent.d.ts.map +1 -0
  141. package/lib/typescript/src/fabric/LuggGroundOverlayViewNativeComponent.d.ts +21 -0
  142. package/lib/typescript/src/fabric/LuggGroundOverlayViewNativeComponent.d.ts.map +1 -0
  143. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts +10 -5
  144. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts.map +1 -1
  145. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts +8 -1
  146. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts.map +1 -1
  147. package/lib/typescript/src/fabric/LuggTileOverlayViewNativeComponent.d.ts +21 -0
  148. package/lib/typescript/src/fabric/LuggTileOverlayViewNativeComponent.d.ts.map +1 -0
  149. package/lib/typescript/src/index.d.ts +10 -4
  150. package/lib/typescript/src/index.d.ts.map +1 -1
  151. package/lib/typescript/src/index.web.d.ts +9 -3
  152. package/lib/typescript/src/index.web.d.ts.map +1 -1
  153. package/lib/typescript/src/types.d.ts +0 -4
  154. package/lib/typescript/src/types.d.ts.map +1 -1
  155. package/package.json +6 -2
  156. package/src/MapProvider.web.tsx +15 -3
  157. package/src/MapView.tsx +12 -1
  158. package/src/MapView.types.ts +118 -1
  159. package/src/MapView.web.tsx +379 -322
  160. package/src/components/Circle.tsx +52 -0
  161. package/src/components/Circle.types.ts +33 -0
  162. package/src/components/Circle.web.tsx +95 -0
  163. package/src/components/GeoJson.tsx +3 -3
  164. package/src/components/GeoJson.types.ts +12 -1
  165. package/src/components/GroundOverlay.tsx +47 -0
  166. package/src/components/GroundOverlay.types.ts +36 -0
  167. package/src/components/GroundOverlay.web.tsx +81 -0
  168. package/src/components/Marker.tsx +59 -5
  169. package/src/components/Marker.types.ts +42 -2
  170. package/src/components/Marker.web.tsx +235 -99
  171. package/src/components/Polygon.web.tsx +93 -89
  172. package/src/components/Polyline.web.tsx +235 -235
  173. package/src/components/TileOverlay.tsx +44 -0
  174. package/src/components/TileOverlay.types.ts +37 -0
  175. package/src/components/TileOverlay.web.tsx +97 -0
  176. package/src/components/index.ts +11 -0
  177. package/src/components/index.web.ts +10 -1
  178. package/src/fabric/LuggCalloutViewNativeComponent.ts +20 -0
  179. package/src/fabric/LuggCircleViewNativeComponent.ts +25 -0
  180. package/src/fabric/LuggGroundOverlayViewNativeComponent.ts +29 -0
  181. package/src/fabric/LuggMapViewNativeComponent.ts +13 -5
  182. package/src/fabric/LuggMarkerViewNativeComponent.ts +14 -2
  183. package/src/fabric/LuggTileOverlayViewNativeComponent.ts +30 -0
  184. package/src/index.ts +14 -1
  185. package/src/index.web.ts +17 -1
  186. package/src/types.ts +0 -5
@@ -1,8 +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
8
+ import android.widget.ImageView
9
+ import androidx.core.graphics.createBitmap
10
+ import androidx.core.view.ViewCompat
11
+ import androidx.core.view.WindowInsetsCompat
6
12
  import com.facebook.react.uimanager.PixelUtil.dpToPx
7
13
  import com.google.android.gms.maps.CameraUpdateFactory
8
14
  import com.google.android.gms.maps.GoogleMap
@@ -11,12 +17,25 @@ import com.google.android.gms.maps.MapView
11
17
  import com.google.android.gms.maps.OnMapReadyCallback
12
18
  import com.google.android.gms.maps.model.AdvancedMarker
13
19
  import com.google.android.gms.maps.model.AdvancedMarkerOptions
20
+ import com.google.android.gms.maps.model.BitmapDescriptorFactory
21
+ import com.google.android.gms.maps.model.Circle
22
+ import com.google.android.gms.maps.model.CircleOptions
23
+ import com.google.android.gms.maps.model.GroundOverlay
24
+ import com.google.android.gms.maps.model.GroundOverlayOptions
14
25
  import com.google.android.gms.maps.model.LatLng
26
+ import com.google.android.gms.maps.model.LatLngBounds
15
27
  import com.google.android.gms.maps.model.MapColorScheme
16
28
  import com.google.android.gms.maps.model.Marker
17
29
  import com.google.android.gms.maps.model.Polygon
18
30
  import com.google.android.gms.maps.model.PolygonOptions
19
31
  import com.google.android.gms.maps.model.PolylineOptions
32
+ import com.google.android.gms.maps.model.TileOverlayOptions
33
+ import com.google.android.gms.maps.model.UrlTileProvider
34
+ import com.luggmaps.LuggCalloutView
35
+ import com.luggmaps.LuggCircleView
36
+ import com.luggmaps.LuggCircleViewDelegate
37
+ import com.luggmaps.LuggGroundOverlayView
38
+ import com.luggmaps.LuggGroundOverlayViewDelegate
20
39
  import com.luggmaps.LuggMapWrapperView
21
40
  import com.luggmaps.LuggMarkerView
22
41
  import com.luggmaps.LuggMarkerViewDelegate
@@ -24,6 +43,13 @@ import com.luggmaps.LuggPolygonView
24
43
  import com.luggmaps.LuggPolygonViewDelegate
25
44
  import com.luggmaps.LuggPolylineView
26
45
  import com.luggmaps.LuggPolylineViewDelegate
46
+ import com.luggmaps.LuggTileOverlayView
47
+ import com.luggmaps.LuggTileOverlayViewDelegate
48
+ import com.luggmaps.extensions.findViewByTag
49
+ import java.net.URL
50
+ import kotlin.math.atan
51
+ import kotlin.math.pow
52
+ import kotlin.math.sinh
27
53
 
28
54
  class GoogleMapProvider(private val context: Context) :
29
55
  MapProvider,
@@ -31,14 +57,21 @@ class GoogleMapProvider(private val context: Context) :
31
57
  LuggMarkerViewDelegate,
32
58
  LuggPolylineViewDelegate,
33
59
  LuggPolygonViewDelegate,
60
+ LuggCircleViewDelegate,
61
+ LuggGroundOverlayViewDelegate,
62
+ LuggTileOverlayViewDelegate,
34
63
  GoogleMap.OnCameraMoveStartedListener,
35
64
  GoogleMap.OnCameraMoveListener,
36
65
  GoogleMap.OnCameraIdleListener,
37
66
  GoogleMap.OnMapClickListener,
38
67
  GoogleMap.OnMapLongClickListener,
39
68
  GoogleMap.OnPolygonClickListener,
69
+ GoogleMap.OnCircleClickListener,
70
+ GoogleMap.OnGroundOverlayClickListener,
40
71
  GoogleMap.OnMarkerClickListener,
41
- GoogleMap.OnMarkerDragListener {
72
+ GoogleMap.OnMarkerDragListener,
73
+ GoogleMap.InfoWindowAdapter,
74
+ ComponentCallbacks2 {
42
75
 
43
76
  override var delegate: MapProviderDelegate? = null
44
77
  override val isMapReady: Boolean get() = _isMapReady
@@ -46,6 +79,7 @@ class GoogleMapProvider(private val context: Context) :
46
79
  var mapId: String = DEMO_MAP_ID
47
80
 
48
81
  private var wrapperView: LuggMapWrapperView? = null
82
+ private var currentNightMode: Int = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
49
83
  private var mapView: MapView? = null
50
84
  private var googleMap: GoogleMap? = null
51
85
  private var _isMapReady = false
@@ -53,9 +87,16 @@ class GoogleMapProvider(private val context: Context) :
53
87
  private val pendingMarkerViews = mutableSetOf<LuggMarkerView>()
54
88
  private val pendingPolylineViews = mutableSetOf<LuggPolylineView>()
55
89
  private val pendingPolygonViews = mutableSetOf<LuggPolygonView>()
90
+ private val pendingCircleViews = mutableSetOf<LuggCircleView>()
91
+ private val pendingGroundOverlayViews = mutableSetOf<LuggGroundOverlayView>()
92
+ private val pendingTileOverlayViews = mutableSetOf<LuggTileOverlayView>()
56
93
  private val polylineAnimators = mutableMapOf<LuggPolylineView, PolylineAnimator>()
57
94
  private val polygonToViewMap = mutableMapOf<Polygon, LuggPolygonView>()
95
+ private val circleToViewMap = mutableMapOf<Circle, LuggCircleView>()
96
+ private val groundOverlayToViewMap = mutableMapOf<GroundOverlay, LuggGroundOverlayView>()
58
97
  private val markerToViewMap = mutableMapOf<Marker, LuggMarkerView>()
98
+ private val liveMarkerViews = mutableSetOf<LuggMarkerView>()
99
+ private var activeNonBubbledMarker: Marker? = null
59
100
  private var tapLocation: LatLng? = null
60
101
 
61
102
  // Initial camera settings
@@ -81,6 +122,10 @@ class GoogleMapProvider(private val context: Context) :
81
122
  // Edge Insets
82
123
  private var edgeInsets: EdgeInsets = EdgeInsets()
83
124
 
125
+ // Inset adjustment
126
+ private var insetAdjustment: String = "never"
127
+ private var systemInsets: EdgeInsets = EdgeInsets()
128
+
84
129
  // region MapProvider
85
130
 
86
131
  override fun initializeMap(wrapperView: View, latitude: Double, longitude: Double, zoom: Float) {
@@ -93,6 +138,8 @@ class GoogleMapProvider(private val context: Context) :
93
138
  val wrapper = wrapperView as LuggMapWrapperView
94
139
  this.wrapperView = wrapper
95
140
 
141
+ context.applicationContext.registerComponentCallbacks(this)
142
+
96
143
  val options = GoogleMapOptions().mapId(mapId)
97
144
  mapView = MapView(context, options).also { view ->
98
145
  view.onCreate(null)
@@ -103,12 +150,24 @@ class GoogleMapProvider(private val context: Context) :
103
150
  }
104
151
 
105
152
  override fun destroy() {
153
+ detachWindowInsetsListener()
154
+ context.applicationContext.unregisterComponentCallbacks(this)
155
+ dismissNonBubbledCallout()
156
+ for (markerView in liveMarkerViews) {
157
+ markerView.onUpdate = null
158
+ }
159
+ liveMarkerViews.clear()
106
160
  pendingMarkerViews.clear()
107
161
  pendingPolylineViews.clear()
108
162
  pendingPolygonViews.clear()
163
+ pendingCircleViews.clear()
164
+ pendingGroundOverlayViews.clear()
165
+ pendingTileOverlayViews.clear()
109
166
  polylineAnimators.values.forEach { it.destroy() }
110
167
  polylineAnimators.clear()
111
168
  polygonToViewMap.clear()
169
+ circleToViewMap.clear()
170
+ groundOverlayToViewMap.clear()
112
171
  markerToViewMap.clear()
113
172
  wrapperView?.touchEventHandler = null
114
173
  wrapperView = null
@@ -118,8 +177,11 @@ class GoogleMapProvider(private val context: Context) :
118
177
  googleMap?.setOnMapClickListener(null)
119
178
  googleMap?.setOnMapLongClickListener(null)
120
179
  googleMap?.setOnPolygonClickListener(null)
180
+ googleMap?.setOnCircleClickListener(null)
181
+ googleMap?.setOnGroundOverlayClickListener(null)
121
182
  googleMap?.setOnMarkerClickListener(null)
122
183
  googleMap?.setOnMarkerDragListener(null)
184
+ googleMap?.setInfoWindowAdapter(null)
123
185
  googleMap?.clear()
124
186
  googleMap = null
125
187
  _isMapReady = false
@@ -141,8 +203,11 @@ class GoogleMapProvider(private val context: Context) :
141
203
  map.setOnMapClickListener(this)
142
204
  map.setOnMapLongClickListener(this)
143
205
  map.setOnPolygonClickListener(this)
206
+ map.setOnCircleClickListener(this)
207
+ map.setOnGroundOverlayClickListener(this)
144
208
  map.setOnMarkerClickListener(this)
145
209
  map.setOnMarkerDragListener(this)
210
+ map.setInfoWindowAdapter(this)
146
211
 
147
212
  wrapperView?.touchEventHandler = { event ->
148
213
  if (event.action == android.view.MotionEvent.ACTION_DOWN) {
@@ -153,11 +218,15 @@ class GoogleMapProvider(private val context: Context) :
153
218
  applyUiSettings()
154
219
  applyZoomLimits()
155
220
  applyEdgeInsets()
221
+ applyInsetAdjustment()
156
222
  applyTheme()
157
223
  applyUserLocation()
158
224
  processPendingMarkers()
159
225
  processPendingPolylines()
160
226
  processPendingPolygons()
227
+ processPendingCircles()
228
+ processPendingGroundOverlays()
229
+ processPendingTileOverlays()
161
230
 
162
231
  delegate?.mapProviderDidReady()
163
232
  }
@@ -177,6 +246,8 @@ class GoogleMapProvider(private val context: Context) :
177
246
  val map = googleMap ?: return
178
247
  val position = map.cameraPosition
179
248
  delegate?.mapProviderDidMoveCamera(position.target.latitude, position.target.longitude, position.zoom, isDragging)
249
+ positionLiveMarkers()
250
+ positionNonBubbledCallout()
180
251
  }
181
252
 
182
253
  override fun onCameraIdle() {
@@ -190,6 +261,7 @@ class GoogleMapProvider(private val context: Context) :
190
261
  }
191
262
 
192
263
  override fun onMapClick(latLng: LatLng) {
264
+ dismissNonBubbledCallout()
193
265
  val map = googleMap ?: return
194
266
  val point = map.projection.toScreenLocation(latLng)
195
267
  delegate?.mapProviderDidPress(latLng.latitude, latLng.longitude, point.x.toFloat(), point.y.toFloat())
@@ -210,10 +282,42 @@ class GoogleMapProvider(private val context: Context) :
210
282
  }
211
283
  }
212
284
 
285
+ override fun onCircleClick(circle: Circle) {
286
+ val circleView = circleToViewMap[circle]
287
+ if (circleView?.tappable == true) {
288
+ circleView.emitPressEvent()
289
+ } else {
290
+ onMapClick(tapLocation ?: return)
291
+ }
292
+ }
293
+
294
+ override fun onGroundOverlayClick(groundOverlay: GroundOverlay) {
295
+ val view = groundOverlayToViewMap[groundOverlay]
296
+ if (view?.tappable == true) {
297
+ view.emitPressEvent()
298
+ }
299
+ }
300
+
213
301
  override fun onMarkerClick(marker: Marker): Boolean {
302
+ dismissNonBubbledCallout()
303
+
214
304
  markerToViewMap[marker]?.let { view ->
215
305
  val point = googleMap?.projection?.toScreenLocation(marker.position)
216
306
  view.emitPressEvent(point?.x?.toFloat() ?: 0f, point?.y?.toFloat() ?: 0f)
307
+
308
+ val calloutView = view.calloutView
309
+ if (calloutView != null && !calloutView.bubbled && calloutView.hasCustomContent) {
310
+ if (view.centerOnPress) {
311
+ googleMap?.animateCamera(CameraUpdateFactory.newLatLng(marker.position))
312
+ }
313
+ showNonBubbledCallout(marker, calloutView)
314
+ return true
315
+ }
316
+
317
+ if (!view.centerOnPress) {
318
+ marker.showInfoWindow()
319
+ return true
320
+ }
217
321
  }
218
322
  return false
219
323
  }
@@ -224,6 +328,7 @@ class GoogleMapProvider(private val context: Context) :
224
328
  view.setCoordinate(marker.position.latitude, marker.position.longitude)
225
329
  val point = googleMap?.projection?.toScreenLocation(marker.position)
226
330
  view.emitDragStartEvent(point?.x?.toFloat() ?: 0f, point?.y?.toFloat() ?: 0f)
331
+ if (!view.rasterize) positionLiveMarker(view)
227
332
  }
228
333
  }
229
334
 
@@ -232,6 +337,7 @@ class GoogleMapProvider(private val context: Context) :
232
337
  view.setCoordinate(marker.position.latitude, marker.position.longitude)
233
338
  val point = googleMap?.projection?.toScreenLocation(marker.position)
234
339
  view.emitDragChangeEvent(point?.x?.toFloat() ?: 0f, point?.y?.toFloat() ?: 0f)
340
+ if (!view.rasterize) positionLiveMarker(view)
235
341
  }
236
342
  }
237
343
 
@@ -241,7 +347,92 @@ class GoogleMapProvider(private val context: Context) :
241
347
  view.setCoordinate(marker.position.latitude, marker.position.longitude)
242
348
  val point = googleMap?.projection?.toScreenLocation(marker.position)
243
349
  view.emitDragEndEvent(point?.x?.toFloat() ?: 0f, point?.y?.toFloat() ?: 0f)
350
+ if (!view.rasterize) positionLiveMarker(view)
351
+ }
352
+ }
353
+
354
+ override fun getInfoWindow(marker: Marker): View? {
355
+ // Non-bubbled callouts are rendered as live views, not info windows
356
+ return null
357
+ }
358
+
359
+ override fun getInfoContents(marker: Marker): View? {
360
+ val markerView = markerToViewMap[marker] ?: return null
361
+ val calloutView = markerView.calloutView ?: return null
362
+ if (!calloutView.hasCustomContent || !calloutView.bubbled) return null
363
+
364
+ val bitmap = calloutView.createContentBitmap() ?: return null
365
+ return ImageView(context).apply { setImageBitmap(bitmap) }
366
+ }
367
+
368
+ private fun showNonBubbledCallout(marker: Marker, calloutView: LuggCalloutView) {
369
+ val wrapper = wrapperView ?: return
370
+ val contentView = calloutView.contentView
371
+
372
+ calloutView.onUpdate = {
373
+ layoutNonBubbledCallout()
374
+ positionNonBubbledCallout()
375
+ }
376
+
377
+ dismissInfoWindows()
378
+ wrapper.addView(contentView)
379
+ activeNonBubbledMarker = marker
380
+ layoutNonBubbledCallout()
381
+ positionNonBubbledCallout()
382
+ }
383
+
384
+ private fun dismissInfoWindows() {
385
+ for ((marker, _) in markerToViewMap) {
386
+ if (marker.isInfoWindowShown) {
387
+ marker.hideInfoWindow()
388
+ }
389
+ }
390
+ }
391
+
392
+ private fun dismissNonBubbledCallout() {
393
+ val marker = activeNonBubbledMarker ?: return
394
+ val markerView = markerToViewMap[marker] ?: return
395
+ val calloutView = markerView.calloutView ?: return
396
+ val contentView = calloutView.contentView
397
+
398
+ calloutView.onUpdate = null
399
+ (contentView.parent as? android.view.ViewGroup)?.removeView(contentView)
400
+ activeNonBubbledMarker = null
401
+ }
402
+
403
+ private fun layoutNonBubbledCallout() {
404
+ val marker = activeNonBubbledMarker ?: return
405
+ val markerView = markerToViewMap[marker] ?: return
406
+ val calloutView = markerView.calloutView ?: return
407
+ val contentView = calloutView.contentView
408
+
409
+ var contentWidth = 0
410
+ var contentHeight = 0
411
+ for (i in 0 until contentView.childCount) {
412
+ val child = contentView.getChildAt(i)
413
+ val childRight = child.left + child.width
414
+ val childBottom = child.top + child.height
415
+ if (childRight > contentWidth) contentWidth = childRight
416
+ if (childBottom > contentHeight) contentHeight = childBottom
244
417
  }
418
+
419
+ contentView.measure(
420
+ View.MeasureSpec.makeMeasureSpec(contentWidth, View.MeasureSpec.EXACTLY),
421
+ View.MeasureSpec.makeMeasureSpec(contentHeight, View.MeasureSpec.EXACTLY)
422
+ )
423
+ contentView.layout(0, 0, contentWidth, contentHeight)
424
+ }
425
+
426
+ private fun positionNonBubbledCallout() {
427
+ val marker = activeNonBubbledMarker ?: return
428
+ val markerView = markerToViewMap[marker] ?: return
429
+ val calloutView = markerView.calloutView ?: return
430
+ val contentView = calloutView.contentView
431
+ val map = googleMap ?: return
432
+
433
+ val point = map.projection.toScreenLocation(marker.position)
434
+ contentView.translationX = point.x - contentView.width / 2f + calloutView.offsetX
435
+ contentView.translationY = point.y - markerView.scaledContentHeight * markerView.anchorY - contentView.height + calloutView.offsetY
245
436
  }
246
437
 
247
438
  // endregion
@@ -285,6 +476,15 @@ class GoogleMapProvider(private val context: Context) :
285
476
  googleMap?.uiSettings?.isMyLocationButtonEnabled = enabled
286
477
  }
287
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
+
288
488
  override fun setTheme(value: String) {
289
489
  theme = value
290
490
  applyTheme()
@@ -324,7 +524,7 @@ class GoogleMapProvider(private val context: Context) :
324
524
 
325
525
  if (map != null && oldInsets != edgeInsets) {
326
526
  val cameraUpdate = CameraUpdateFactory.newCameraPosition(map.cameraPosition)
327
- applyEdgeInsets()
527
+ applyEdgeInsets(duration)
328
528
  when {
329
529
  duration < 0 -> map.animateCamera(cameraUpdate)
330
530
  duration > 0 -> map.animateCamera(cameraUpdate, duration, null)
@@ -335,6 +535,17 @@ class GoogleMapProvider(private val context: Context) :
335
535
  }
336
536
  }
337
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
+
338
549
  // endregion
339
550
 
340
551
  // region MarkerViewDelegate
@@ -356,6 +567,30 @@ class GoogleMapProvider(private val context: Context) :
356
567
  syncMarkerView(markerView)
357
568
  }
358
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
+
359
594
  // endregion
360
595
 
361
596
  // region PolylineViewDelegate
@@ -374,6 +609,30 @@ class GoogleMapProvider(private val context: Context) :
374
609
 
375
610
  // endregion
376
611
 
612
+ // region CircleViewDelegate
613
+
614
+ override fun circleViewDidUpdate(circleView: LuggCircleView) {
615
+ syncCircleView(circleView)
616
+ }
617
+
618
+ // endregion
619
+
620
+ // region GroundOverlayViewDelegate
621
+
622
+ override fun groundOverlayViewDidUpdate(groundOverlayView: LuggGroundOverlayView) {
623
+ syncGroundOverlayView(groundOverlayView)
624
+ }
625
+
626
+ // endregion
627
+
628
+ // region TileOverlayViewDelegate
629
+
630
+ override fun tileOverlayViewDidUpdate(tileOverlayView: LuggTileOverlayView) {
631
+ syncTileOverlayView(tileOverlayView)
632
+ }
633
+
634
+ // endregion
635
+
377
636
  // region Marker Management
378
637
 
379
638
  override fun addMarkerView(markerView: LuggMarkerView) {
@@ -382,6 +641,7 @@ class GoogleMapProvider(private val context: Context) :
382
641
  }
383
642
 
384
643
  override fun removeMarkerView(markerView: LuggMarkerView) {
644
+ removeLiveMarker(markerView)
385
645
  markerView.marker?.let { markerToViewMap.remove(it) }
386
646
  markerView.marker?.remove()
387
647
  markerView.marker = null
@@ -411,9 +671,14 @@ class GoogleMapProvider(private val context: Context) :
411
671
  isDraggable = markerView.draggable
412
672
  }
413
673
 
414
- if (markerView.hasCustomView && markerView.scaleChanged) {
415
- markerView.applyScaleToMarker()
416
- markerView.clearScaleChanged()
674
+ if (markerView.hasCustomView) {
675
+ if (markerView.scaleChanged) {
676
+ markerView.applyScaleToMarker()
677
+ markerView.clearScaleChanged()
678
+ }
679
+ if (!markerView.rasterize) {
680
+ positionLiveMarker(markerView)
681
+ }
417
682
  }
418
683
  }
419
684
 
@@ -440,7 +705,64 @@ class GoogleMapProvider(private val context: Context) :
440
705
 
441
706
  markerView.marker = marker
442
707
  markerToViewMap[marker] = markerView
443
- markerView.applyIconToMarker()
708
+
709
+ if (markerView.hasCustomView) {
710
+ if (markerView.rasterize) {
711
+ markerView.applyIconToMarker()
712
+ } else {
713
+ showLiveMarker(markerView)
714
+ }
715
+ }
716
+ }
717
+
718
+ // Workaround: AdvancedMarker.iconView is buggy on Android, so we manually add the custom
719
+ // content view to the wrapper and position it via screen projection instead. The underlying
720
+ // marker uses a transparent bitmap matching the content size so taps still trigger onMarkerClick.
721
+ private fun showLiveMarker(markerView: LuggMarkerView) {
722
+ val wrapper = wrapperView ?: return
723
+
724
+ markerView.onUpdate = {
725
+ updateLiveMarkerHitArea(markerView)
726
+ positionLiveMarker(markerView)
727
+ }
728
+
729
+ val contentView = markerView.contentView
730
+ contentView.pointerEvents = com.facebook.react.uimanager.PointerEvents.NONE
731
+ (contentView.parent as? android.view.ViewGroup)?.removeView(contentView)
732
+ wrapper.addView(contentView)
733
+ liveMarkerViews.add(markerView)
734
+ markerView.layoutContentView()
735
+ updateLiveMarkerHitArea(markerView)
736
+ positionLiveMarker(markerView)
737
+ }
738
+
739
+ private fun updateLiveMarkerHitArea(markerView: LuggMarkerView) {
740
+ val marker = markerView.marker ?: return
741
+ val contentView = markerView.contentView
742
+ val w = contentView.width.coerceAtLeast(1)
743
+ val h = contentView.height.coerceAtLeast(1)
744
+ marker.setIcon(BitmapDescriptorFactory.fromBitmap(createBitmap(w, h)))
745
+ }
746
+
747
+ private fun removeLiveMarker(markerView: LuggMarkerView) {
748
+ markerView.onUpdate = null
749
+ val contentView = markerView.contentView
750
+ (contentView.parent as? android.view.ViewGroup)?.removeView(contentView)
751
+ liveMarkerViews.remove(markerView)
752
+ }
753
+
754
+ private fun positionLiveMarkers() {
755
+ for (markerView in liveMarkerViews) {
756
+ positionLiveMarker(markerView)
757
+ }
758
+ }
759
+
760
+ private fun positionLiveMarker(markerView: LuggMarkerView) {
761
+ val map = googleMap ?: return
762
+ val contentView = markerView.contentView
763
+ val point = map.projection.toScreenLocation(LatLng(markerView.latitude, markerView.longitude))
764
+ contentView.translationX = point.x - contentView.width * markerView.anchorX
765
+ contentView.translationY = point.y - contentView.height * markerView.anchorY
444
766
  }
445
767
 
446
768
  // endregion
@@ -577,6 +899,218 @@ class GoogleMapProvider(private val context: Context) :
577
899
 
578
900
  // endregion
579
901
 
902
+ // region Circle Management
903
+
904
+ override fun addCircleView(circleView: LuggCircleView) {
905
+ circleView.delegate = this
906
+ syncCircleView(circleView)
907
+ }
908
+
909
+ override fun removeCircleView(circleView: LuggCircleView) {
910
+ circleView.circle?.let { circleToViewMap.remove(it) }
911
+ circleView.circle?.remove()
912
+ circleView.circle = null
913
+ }
914
+
915
+ private fun syncCircleView(circleView: LuggCircleView) {
916
+ if (googleMap == null) {
917
+ pendingCircleViews.add(circleView)
918
+ return
919
+ }
920
+
921
+ if (circleView.circle == null) {
922
+ addCircleViewToMap(circleView)
923
+ return
924
+ }
925
+
926
+ circleView.circle?.apply {
927
+ center = circleView.center
928
+ radius = circleView.radius
929
+ fillColor = circleView.fillColor
930
+ strokeColor = circleView.strokeColor
931
+ strokeWidth = circleView.strokeWidth.dpToPx()
932
+ zIndex = circleView.zIndex
933
+ isClickable = true
934
+ }
935
+ }
936
+
937
+ private fun processPendingCircles() {
938
+ if (googleMap == null) return
939
+ pendingCircleViews.forEach { addCircleViewToMap(it) }
940
+ pendingCircleViews.clear()
941
+ }
942
+
943
+ private fun addCircleViewToMap(circleView: LuggCircleView) {
944
+ val map = googleMap ?: return
945
+
946
+ val options = CircleOptions()
947
+ .center(circleView.center)
948
+ .radius(circleView.radius)
949
+ .fillColor(circleView.fillColor)
950
+ .strokeColor(circleView.strokeColor)
951
+ .strokeWidth(circleView.strokeWidth.dpToPx())
952
+ .zIndex(circleView.zIndex)
953
+ .clickable(true)
954
+
955
+ val circle = map.addCircle(options)
956
+ circleView.circle = circle
957
+ circleToViewMap[circle] = circleView
958
+ }
959
+
960
+ // endregion
961
+
962
+ // region Ground Overlay Management
963
+
964
+ override fun addGroundOverlayView(groundOverlayView: LuggGroundOverlayView) {
965
+ groundOverlayView.delegate = this
966
+ syncGroundOverlayView(groundOverlayView)
967
+ }
968
+
969
+ override fun removeGroundOverlayView(groundOverlayView: LuggGroundOverlayView) {
970
+ groundOverlayView.delegate = null
971
+ groundOverlayView.groundOverlay?.let { groundOverlayToViewMap.remove(it) }
972
+ groundOverlayView.groundOverlay?.remove()
973
+ groundOverlayView.groundOverlay = null
974
+ }
975
+
976
+ private fun syncGroundOverlayView(groundOverlayView: LuggGroundOverlayView) {
977
+ if (googleMap == null) {
978
+ pendingGroundOverlayViews.add(groundOverlayView)
979
+ return
980
+ }
981
+
982
+ val imageUri = groundOverlayView.imageUri
983
+ if (imageUri.isEmpty()) return
984
+
985
+ // Remove old overlay
986
+ groundOverlayView.groundOverlay?.let { groundOverlayToViewMap.remove(it) }
987
+ groundOverlayView.groundOverlay?.remove()
988
+ groundOverlayView.groundOverlay = null
989
+
990
+ // Load image async and add overlay
991
+ Thread {
992
+ try {
993
+ val connection = URL(imageUri).openConnection() as java.net.HttpURLConnection
994
+ connection.instanceFollowRedirects = true
995
+ connection.connect()
996
+ val bitmap = android.graphics.BitmapFactory.decodeStream(connection.inputStream)
997
+ connection.disconnect()
998
+ if (bitmap != null) {
999
+ mapView?.post {
1000
+ addGroundOverlayToMap(groundOverlayView, bitmap)
1001
+ }
1002
+ }
1003
+ } catch (_: Exception) {}
1004
+ }.start()
1005
+ }
1006
+
1007
+ private fun addGroundOverlayToMap(groundOverlayView: LuggGroundOverlayView, bitmap: android.graphics.Bitmap) {
1008
+ val map = googleMap ?: return
1009
+
1010
+ val bounds = LatLngBounds(groundOverlayView.southwest, groundOverlayView.northeast)
1011
+ val options = GroundOverlayOptions()
1012
+ .image(BitmapDescriptorFactory.fromBitmap(bitmap))
1013
+ .positionFromBounds(bounds)
1014
+ .transparency(1f - groundOverlayView.overlayOpacity)
1015
+ .bearing(groundOverlayView.bearing)
1016
+ .zIndex(groundOverlayView.zIndex)
1017
+ .clickable(groundOverlayView.tappable)
1018
+
1019
+ val overlay = map.addGroundOverlay(options)
1020
+ if (overlay != null) {
1021
+ groundOverlayView.groundOverlay = overlay
1022
+ groundOverlayToViewMap[overlay] = groundOverlayView
1023
+ }
1024
+ }
1025
+
1026
+ private fun processPendingGroundOverlays() {
1027
+ if (googleMap == null) return
1028
+ pendingGroundOverlayViews.forEach { syncGroundOverlayView(it) }
1029
+ pendingGroundOverlayViews.clear()
1030
+ }
1031
+
1032
+ // endregion
1033
+
1034
+ // region Tile Overlay Management
1035
+
1036
+ override fun addTileOverlayView(tileOverlayView: LuggTileOverlayView) {
1037
+ tileOverlayView.delegate = this
1038
+ syncTileOverlayView(tileOverlayView)
1039
+ }
1040
+
1041
+ override fun removeTileOverlayView(tileOverlayView: LuggTileOverlayView) {
1042
+ tileOverlayView.delegate = null
1043
+ tileOverlayView.tileOverlay?.remove()
1044
+ tileOverlayView.tileOverlay = null
1045
+ }
1046
+
1047
+ private fun syncTileOverlayView(tileOverlayView: LuggTileOverlayView) {
1048
+ if (googleMap == null) {
1049
+ pendingTileOverlayViews.add(tileOverlayView)
1050
+ return
1051
+ }
1052
+
1053
+ val urlTemplate = tileOverlayView.urlTemplate
1054
+ if (urlTemplate.isEmpty()) return
1055
+
1056
+ // Remove old overlay
1057
+ tileOverlayView.tileOverlay?.remove()
1058
+ tileOverlayView.tileOverlay = null
1059
+
1060
+ val tileSize = tileOverlayView.tileSize
1061
+ val hasBounds = tileOverlayView.hasBounds
1062
+ val swLat = tileOverlayView.boundsSwLat
1063
+ val swLng = tileOverlayView.boundsSwLng
1064
+ val neLat = tileOverlayView.boundsNeLat
1065
+ val neLng = tileOverlayView.boundsNeLng
1066
+
1067
+ val tileProvider = object : UrlTileProvider(tileSize, tileSize) {
1068
+ override fun getTileUrl(x: Int, y: Int, zoom: Int): URL? {
1069
+ if (hasBounds) {
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))))
1073
+ val tileSWLng = x / n * 360.0 - 180.0
1074
+ val tileNELng = (x + 1) / n * 360.0 - 180.0
1075
+
1076
+ if (tileNELat < swLat ||
1077
+ tileSWLat > neLat ||
1078
+ tileNELng < swLng ||
1079
+ tileSWLng > neLng
1080
+ ) {
1081
+ return null
1082
+ }
1083
+ }
1084
+
1085
+ val url = urlTemplate
1086
+ .replace("{x}", x.toString())
1087
+ .replace("{y}", y.toString())
1088
+ .replace("{z}", zoom.toString())
1089
+ return try {
1090
+ URL(url)
1091
+ } catch (_: Exception) {
1092
+ null
1093
+ }
1094
+ }
1095
+ }
1096
+
1097
+ val options = TileOverlayOptions()
1098
+ .tileProvider(tileProvider)
1099
+ .transparency(1f - tileOverlayView.overlayOpacity)
1100
+ .zIndex(tileOverlayView.zIndex)
1101
+
1102
+ val overlay = googleMap?.addTileOverlay(options)
1103
+ tileOverlayView.tileOverlay = overlay
1104
+ }
1105
+
1106
+ private fun processPendingTileOverlays() {
1107
+ if (googleMap == null) return
1108
+ pendingTileOverlayViews.forEach { syncTileOverlayView(it) }
1109
+ pendingTileOverlayViews.clear()
1110
+ }
1111
+
1112
+ // endregion
1113
+
580
1114
  // region Lifecycle
581
1115
 
582
1116
  override fun pauseAnimations() {
@@ -614,11 +1148,11 @@ class GoogleMapProvider(private val context: Context) :
614
1148
  val map = googleMap ?: return
615
1149
  if (coordinates.isEmpty()) return
616
1150
 
617
- val latLngs = coordinates.filterIsInstance<LatLng>()
618
- if (latLngs.isEmpty()) return
1151
+ val latLongs = coordinates.filterIsInstance<LatLng>()
1152
+ if (latLongs.isEmpty()) return
619
1153
 
620
- val boundsBuilder = com.google.android.gms.maps.model.LatLngBounds.Builder()
621
- latLngs.forEach { boundsBuilder.include(it) }
1154
+ val boundsBuilder = LatLngBounds.Builder()
1155
+ latLongs.forEach { boundsBuilder.include(it) }
622
1156
  val bounds = boundsBuilder.build()
623
1157
 
624
1158
  val top = edgeInsetsTop.toFloat().dpToPx().toInt()
@@ -626,11 +1160,12 @@ class GoogleMapProvider(private val context: Context) :
626
1160
  val bottom = edgeInsetsBottom.toFloat().dpToPx().toInt()
627
1161
  val right = edgeInsetsRight.toFloat().dpToPx().toInt()
628
1162
 
1163
+ val combined = combinedEdgeInsets()
629
1164
  map.setPadding(
630
- edgeInsets.left + left,
631
- edgeInsets.top + top,
632
- edgeInsets.right + right,
633
- edgeInsets.bottom + bottom
1165
+ combined.left + left,
1166
+ combined.top + top,
1167
+ combined.right + right,
1168
+ combined.bottom + bottom
634
1169
  )
635
1170
 
636
1171
  val cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, 0)
@@ -641,7 +1176,7 @@ class GoogleMapProvider(private val context: Context) :
641
1176
  else -> map.moveCamera(cameraUpdate)
642
1177
  }
643
1178
 
644
- map.setPadding(edgeInsets.left, edgeInsets.top, edgeInsets.right, edgeInsets.bottom)
1179
+ map.setPadding(combined.left, combined.top, combined.right, combined.bottom)
645
1180
  }
646
1181
 
647
1182
  // endregion
@@ -665,17 +1200,92 @@ class GoogleMapProvider(private val context: Context) :
665
1200
  }
666
1201
  }
667
1202
 
668
- private fun applyEdgeInsets() {
669
- 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()
1211
+ }
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
+ }
670
1252
  }
671
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
+
672
1282
  private fun applyTheme() {
673
1283
  val colorScheme = when (theme) {
674
1284
  "dark" -> MapColorScheme.DARK
675
1285
  "light" -> MapColorScheme.LIGHT
676
1286
  else -> MapColorScheme.FOLLOW_SYSTEM
677
1287
  }
678
- googleMap?.setMapColorScheme(colorScheme)
1288
+ googleMap?.mapColorScheme = colorScheme
679
1289
  }
680
1290
 
681
1291
  @SuppressLint("MissingPermission")