@lugg/maps 1.1.4 → 1.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint
4
4
  import android.content.ComponentCallbacks2
5
5
  import android.content.Context
6
6
  import android.content.res.Configuration
7
+ import android.os.SystemClock
7
8
  import android.view.View
8
9
  import android.view.ViewTreeObserver
9
10
  import android.widget.ImageView
@@ -11,6 +12,7 @@ import androidx.core.graphics.createBitmap
11
12
  import androidx.core.view.ViewCompat
12
13
  import androidx.core.view.WindowInsetsCompat
13
14
  import com.facebook.react.uimanager.PixelUtil.dpToPx
15
+ import com.google.android.gms.maps.CameraUpdate
14
16
  import com.google.android.gms.maps.CameraUpdateFactory
15
17
  import com.google.android.gms.maps.GoogleMap
16
18
  import com.google.android.gms.maps.GoogleMapOptions
@@ -133,6 +135,9 @@ class GoogleMapProvider(private val context: Context) :
133
135
  // Edge Insets
134
136
  private var edgeInsets: EdgeInsets = EdgeInsets()
135
137
 
138
+ // The moveCamera or fitCoordinates animation in flight, if any
139
+ private var cameraAnimation: CameraAnimation? = null
140
+
136
141
  // Inset adjustment
137
142
  private var insetAdjustment: String = "never"
138
143
  private var systemInsets: EdgeInsets = EdgeInsets()
@@ -212,6 +217,7 @@ class GoogleMapProvider(private val context: Context) :
212
217
  groundOverlayToViewMap.clear()
213
218
  markerToViewMap.clear()
214
219
  wrapperView?.touchEventHandler = null
220
+ cameraAnimation = null
215
221
  // The wrapper outlives the provider on reload; don't leave the dead map
216
222
  // view in it
217
223
  mapView?.let { wrapperView?.removeView(it) }
@@ -603,7 +609,18 @@ class GoogleMapProvider(private val context: Context) :
603
609
  val oldInsets = this.edgeInsets
604
610
  this.edgeInsets = edgeInsets
605
611
 
606
- if (map != null && oldInsets != edgeInsets) {
612
+ val animation = cameraAnimation
613
+ if (map != null && oldInsets != edgeInsets && animation != null) {
614
+ // Any camera update cancels the animation in flight, and its target was resolved against
615
+ // the old insets. Re-apply it for the time it has left so it lands on the new insets.
616
+ applyEdgeInsets(duration)
617
+ val remaining = if (animation.duration > 0) {
618
+ (animation.endTime - SystemClock.uptimeMillis()).toInt().coerceAtLeast(0)
619
+ } else {
620
+ animation.duration
621
+ }
622
+ applyCameraUpdate(animation.update, remaining, animation.padding)
623
+ } else if (map != null && oldInsets != edgeInsets) {
607
624
  val cameraUpdate = CameraUpdateFactory.newCameraPosition(map.cameraPosition)
608
625
  applyEdgeInsets(duration)
609
626
  when {
@@ -1262,12 +1279,7 @@ class GoogleMapProvider(private val context: Context) :
1262
1279
  .target(position)
1263
1280
  .zoom(targetZoom)
1264
1281
  .build()
1265
- val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition)
1266
- when {
1267
- duration < 0 -> map.animateCamera(cameraUpdate)
1268
- duration > 0 -> map.animateCamera(cameraUpdate, duration, null)
1269
- else -> map.moveCamera(cameraUpdate)
1270
- }
1282
+ applyCameraUpdate(CameraUpdateFactory.newCameraPosition(cameraPosition), duration)
1271
1283
  }
1272
1284
 
1273
1285
  override fun fitCoordinates(
@@ -1278,8 +1290,7 @@ class GoogleMapProvider(private val context: Context) :
1278
1290
  edgeInsetsRight: Int,
1279
1291
  duration: Int
1280
1292
  ) {
1281
- val map = googleMap ?: return
1282
- if (coordinates.isEmpty()) return
1293
+ if (googleMap == null || coordinates.isEmpty()) return
1283
1294
 
1284
1295
  val latLongs = coordinates.filterIsInstance<LatLng>()
1285
1296
  if (latLongs.isEmpty()) return
@@ -1298,23 +1309,11 @@ class GoogleMapProvider(private val context: Context) :
1298
1309
  latLongs.forEach { boundsBuilder.include(it) }
1299
1310
  val bounds = boundsBuilder.build()
1300
1311
 
1301
- val combined = combinedEdgeInsets()
1302
- map.setPadding(
1303
- combined.left + left,
1304
- combined.top + top,
1305
- combined.right + right,
1306
- combined.bottom + bottom
1312
+ applyCameraUpdate(
1313
+ CameraUpdateFactory.newLatLngBounds(bounds, 0),
1314
+ duration,
1315
+ EdgeInsets(top, left, bottom, right)
1307
1316
  )
1308
-
1309
- val cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, 0)
1310
-
1311
- when {
1312
- duration < 0 -> map.animateCamera(cameraUpdate)
1313
- duration > 0 -> map.animateCamera(cameraUpdate, duration, null)
1314
- else -> map.moveCamera(cameraUpdate)
1315
- }
1316
-
1317
- map.setPadding(combined.left, combined.top, combined.right, combined.bottom)
1318
1317
  }
1319
1318
 
1320
1319
  // endregion
@@ -1367,6 +1366,39 @@ class GoogleMapProvider(private val context: Context) :
1367
1366
  edgeInsets
1368
1367
  }
1369
1368
 
1369
+ // A camera update resolves against the map's padding when it is applied, so `padding` is added
1370
+ // to the insets only for the call
1371
+ private fun applyCameraUpdate(update: CameraUpdate, duration: Int, padding: EdgeInsets = EdgeInsets()) {
1372
+ val map = googleMap ?: return
1373
+ val combined = combinedEdgeInsets()
1374
+ map.setPadding(
1375
+ combined.left + padding.left,
1376
+ combined.top + padding.top,
1377
+ combined.right + padding.right,
1378
+ combined.bottom + padding.bottom
1379
+ )
1380
+
1381
+ if (duration == 0) {
1382
+ cameraAnimation = null
1383
+ map.moveCamera(update)
1384
+ } else {
1385
+ val animation = CameraAnimation(update, padding, duration, SystemClock.uptimeMillis() + duration)
1386
+ cameraAnimation = animation
1387
+ val callback = object : GoogleMap.CancelableCallback {
1388
+ override fun onFinish() {
1389
+ if (cameraAnimation === animation) cameraAnimation = null
1390
+ }
1391
+
1392
+ override fun onCancel() {
1393
+ if (cameraAnimation === animation) cameraAnimation = null
1394
+ }
1395
+ }
1396
+ if (duration < 0) map.animateCamera(update, callback) else map.animateCamera(update, duration, callback)
1397
+ }
1398
+
1399
+ map.setPadding(combined.left, combined.top, combined.right, combined.bottom)
1400
+ }
1401
+
1370
1402
  private fun applyEdgeInsets(duration: Int = 0) {
1371
1403
  val combined = combinedEdgeInsets()
1372
1404
  // Lite mode misrenders padding: the static image is drawn only within
@@ -1592,6 +1624,8 @@ class GoogleMapProvider(private val context: Context) :
1592
1624
 
1593
1625
  // endregion
1594
1626
 
1627
+ private class CameraAnimation(val update: CameraUpdate, val padding: EdgeInsets, val duration: Int, val endTime: Long)
1628
+
1595
1629
  companion object {
1596
1630
  const val DEMO_MAP_ID = "DEMO_MAP_ID"
1597
1631
 
@@ -533,6 +533,17 @@ MKMapType LuggMKMapTypeFromMapType(LuggMapViewMapType mapType) {
533
533
  _edgeInsetsDisplayLink = nil;
534
534
  }
535
535
 
536
+ // An inset animation re-centers the map on every frame, which stops a camera
537
+ // animation. A camera command moves the map anyway, so it takes the final
538
+ // insets without the re-center.
539
+ - (void)finishEdgeInsetsAnimation {
540
+ if (!_edgeInsetsDisplayLink)
541
+ return;
542
+ [self stopEdgeInsetsAnimation];
543
+ _mapView.layoutMargins = _edgeInsetsTo;
544
+ _edgeInsetsCurrent = _edgeInsetsTo;
545
+ }
546
+
536
547
  - (CLLocationDistance)cameraDistanceForZoomLevel:(double)zoomLevel {
537
548
  return 128000000.0 / pow(2, zoomLevel);
538
549
  }
@@ -1785,6 +1796,7 @@ MKMapType LuggMKMapTypeFromMapType(LuggMapViewMapType mapType) {
1785
1796
  if (!_mapView)
1786
1797
  return;
1787
1798
 
1799
+ [self finishEdgeInsetsAnimation];
1788
1800
  double targetZoom = zoom > 0 ? zoom : _mapView.zoomLevel;
1789
1801
 
1790
1802
  if (duration < 0) {
@@ -1837,6 +1849,7 @@ MKMapType LuggMKMapTypeFromMapType(LuggMapViewMapType mapType) {
1837
1849
  UIEdgeInsets insets = UIEdgeInsetsMake(edgeInsetsTop, edgeInsetsLeft,
1838
1850
  edgeInsetsBottom, edgeInsetsRight);
1839
1851
 
1852
+ [self finishEdgeInsetsAnimation];
1840
1853
  if (duration < 0) {
1841
1854
  [_mapView setVisibleMapRect:mapRect edgePadding:insets animated:YES];
1842
1855
  } else if (duration > 0) {
@@ -1176,18 +1176,26 @@ LuggInterfaceStyleFromTheme(facebook::react::LuggMapViewTheme theme) {
1176
1176
 
1177
1177
  UIEdgeInsets insets = UIEdgeInsetsMake(edgeInsetsTop, edgeInsetsLeft,
1178
1178
  edgeInsetsBottom, edgeInsetsRight);
1179
- GMSCameraUpdate *cameraUpdate = [GMSCameraUpdate fitBounds:bounds
1180
- withEdgeInsets:insets];
1179
+
1180
+ // An inset animation may still be moving the padding toward _edgeInsets.
1181
+ // Fit against the padding it ends at: the camera target stays centered in
1182
+ // the padding while the animation finishes.
1183
+ UIEdgeInsets padding = _mapView.padding;
1184
+ _mapView.padding = _edgeInsets;
1185
+ GMSCameraPosition *camera = [_mapView cameraForBounds:bounds insets:insets];
1186
+ _mapView.padding = padding;
1187
+ if (!camera)
1188
+ return;
1181
1189
 
1182
1190
  if (duration < 0) {
1183
- [_mapView animateWithCameraUpdate:cameraUpdate];
1191
+ [_mapView animateToCameraPosition:camera];
1184
1192
  } else if (duration > 0) {
1185
1193
  [CATransaction begin];
1186
1194
  [CATransaction setAnimationDuration:duration / 1000.0];
1187
- [_mapView animateWithCameraUpdate:cameraUpdate];
1195
+ [_mapView animateToCameraPosition:camera];
1188
1196
  [CATransaction commit];
1189
1197
  } else {
1190
- [_mapView moveCamera:cameraUpdate];
1198
+ [_mapView setCamera:camera];
1191
1199
  }
1192
1200
  }
1193
1201
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lugg/maps",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "Universal maps for React Native.",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",