@lugg/maps 1.0.0-beta.2 → 1.0.0-beta.21
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.
- package/README.md +3 -0
- package/android/src/main/java/com/luggmaps/LuggCalloutView.kt +9 -2
- package/android/src/main/java/com/luggmaps/LuggCalloutViewManager.kt +4 -3
- package/android/src/main/java/com/luggmaps/LuggCircleView.kt +85 -0
- package/android/src/main/java/com/luggmaps/LuggCircleViewManager.kt +79 -0
- package/android/src/main/java/com/luggmaps/LuggGroundOverlayView.kt +81 -0
- package/android/src/main/java/com/luggmaps/LuggGroundOverlayViewManager.kt +82 -0
- package/android/src/main/java/com/luggmaps/LuggMapView.kt +90 -0
- package/android/src/main/java/com/luggmaps/LuggMapViewManager.kt +45 -0
- package/android/src/main/java/com/luggmaps/LuggMapWrapperView.kt +27 -2
- package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +30 -2
- package/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt +13 -0
- package/android/src/main/java/com/luggmaps/LuggPackage.kt +4 -1
- package/android/src/main/java/com/luggmaps/LuggPolylineView.kt +3 -23
- package/android/src/main/java/com/luggmaps/LuggTileOverlayView.kt +92 -0
- package/android/src/main/java/com/luggmaps/LuggTileOverlayViewManager.kt +80 -0
- package/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt +737 -37
- package/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt +15 -0
- package/android/src/main/java/com/luggmaps/core/PolylineAnimator.kt +65 -11
- package/android/src/main/java/com/luggmaps/events/CirclePressEvent.kt +12 -0
- package/android/src/main/java/com/luggmaps/events/GroundOverlayPressEvent.kt +12 -0
- package/android/src/main/java/com/luggmaps/events/TileOverlayPressEvent.kt +12 -0
- package/android/src/main/java/com/luggmaps/extensions/ViewExtensions.kt +11 -0
- package/ios/LuggCalloutView.h +1 -1
- package/ios/LuggCalloutView.mm +21 -11
- package/ios/LuggCircleView.h +31 -0
- package/ios/LuggCircleView.mm +133 -0
- package/ios/LuggGroundOverlayView.h +30 -0
- package/ios/LuggGroundOverlayView.mm +125 -0
- package/ios/LuggMapView.mm +254 -4
- package/ios/LuggMapWrapperView.h +5 -0
- package/ios/LuggMapWrapperView.mm +18 -0
- package/ios/LuggMarkerView.h +3 -0
- package/ios/LuggMarkerView.mm +25 -0
- package/ios/LuggTileOverlayView.h +31 -0
- package/ios/LuggTileOverlayView.mm +130 -0
- package/ios/core/AppleMapProvider.h +8 -0
- package/ios/core/AppleMapProvider.mm +663 -29
- package/ios/core/AppleStaticMapProvider.h +14 -0
- package/ios/core/AppleStaticMapProvider.mm +159 -0
- package/ios/core/GMSPolylineAnimator.m +46 -18
- package/ios/core/GoogleMapProvider.h +10 -0
- package/ios/core/GoogleMapProvider.mm +430 -51
- package/ios/core/GoogleStaticMapProvider.h +21 -0
- package/ios/core/GoogleStaticMapProvider.mm +213 -0
- package/ios/core/MKPolylineAnimator.m +67 -43
- package/ios/core/MapProviderDelegate.h +27 -1
- package/ios/core/StaticMapProviderBase.h +75 -0
- package/ios/core/StaticMapProviderBase.mm +845 -0
- package/ios/events/CameraIdleEvent.h +4 -2
- package/ios/events/CameraMoveEvent.h +4 -2
- package/ios/events/CirclePressEvent.h +22 -0
- package/ios/events/GroundOverlayPressEvent.h +22 -0
- package/ios/events/LongPressEvent.h +4 -2
- package/ios/events/MarkerDragEvent.h +12 -6
- package/ios/events/MarkerPressEvent.h +4 -2
- package/ios/events/PolygonPressEvent.h +4 -2
- package/ios/events/PressEvent.h +4 -2
- package/ios/events/ReadyEvent.h +4 -2
- package/ios/events/TileOverlayPressEvent.h +22 -0
- package/ios/extensions/MKMapView+Zoom.h +20 -0
- package/ios/extensions/MKMapView+Zoom.m +28 -18
- package/ios/extensions/UIView+Snapshot.h +16 -0
- package/ios/extensions/UIView+Snapshot.m +27 -0
- package/lib/module/MapProvider.web.js +4 -1
- package/lib/module/MapProvider.web.js.map +1 -1
- package/lib/module/MapView.js +19 -1
- package/lib/module/MapView.js.map +1 -1
- package/lib/module/MapView.web.js +30 -22
- package/lib/module/MapView.web.js.map +1 -1
- package/lib/module/components/Circle.js +46 -0
- package/lib/module/components/Circle.js.map +1 -0
- package/lib/module/components/Circle.types.js +4 -0
- package/lib/module/components/Circle.types.js.map +1 -0
- package/lib/module/components/Circle.web.js +79 -0
- package/lib/module/components/Circle.web.js.map +1 -0
- package/lib/module/components/GeoJson.js +3 -3
- package/lib/module/components/GeoJson.js.map +1 -1
- package/lib/module/components/GroundOverlay.js +45 -0
- package/lib/module/components/GroundOverlay.js.map +1 -0
- package/lib/module/components/GroundOverlay.types.js +4 -0
- package/lib/module/components/GroundOverlay.types.js.map +1 -0
- package/lib/module/components/GroundOverlay.web.js +77 -0
- package/lib/module/components/GroundOverlay.web.js.map +1 -0
- package/lib/module/components/Marker.js +22 -4
- package/lib/module/components/Marker.js.map +1 -1
- package/lib/module/components/Marker.web.js +23 -10
- package/lib/module/components/Marker.web.js.map +1 -1
- package/lib/module/components/Polygon.web.js +3 -3
- package/lib/module/components/Polygon.web.js.map +1 -1
- package/lib/module/components/Polyline.web.js +6 -12
- package/lib/module/components/Polyline.web.js.map +1 -1
- package/lib/module/components/TileOverlay.js +44 -0
- package/lib/module/components/TileOverlay.js.map +1 -0
- package/lib/module/components/TileOverlay.types.js +4 -0
- package/lib/module/components/TileOverlay.types.js.map +1 -0
- package/lib/module/components/TileOverlay.web.js +69 -0
- package/lib/module/components/TileOverlay.web.js.map +1 -0
- package/lib/module/components/index.js +3 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/components/index.web.js +3 -0
- package/lib/module/components/index.web.js.map +1 -1
- package/lib/module/fabric/LuggCalloutViewNativeComponent.ts +1 -1
- package/lib/module/fabric/LuggCircleViewNativeComponent.ts +25 -0
- package/lib/module/fabric/LuggGroundOverlayViewNativeComponent.ts +29 -0
- package/lib/module/fabric/LuggMapViewNativeComponent.ts +16 -5
- package/lib/module/fabric/LuggMarkerViewNativeComponent.ts +14 -2
- package/lib/module/fabric/LuggTileOverlayViewNativeComponent.ts +30 -0
- package/lib/module/index.js +3 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +3 -0
- package/lib/module/index.web.js.map +1 -1
- package/lib/typescript/src/MapProvider.web.d.ts +3 -2
- package/lib/typescript/src/MapProvider.web.d.ts.map +1 -1
- package/lib/typescript/src/MapView.d.ts +1 -1
- package/lib/typescript/src/MapView.d.ts.map +1 -1
- package/lib/typescript/src/MapView.types.d.ts +60 -1
- package/lib/typescript/src/MapView.types.d.ts.map +1 -1
- package/lib/typescript/src/MapView.web.d.ts +1 -1
- package/lib/typescript/src/MapView.web.d.ts.map +1 -1
- package/lib/typescript/src/components/Circle.d.ts +10 -0
- package/lib/typescript/src/components/Circle.d.ts.map +1 -0
- package/lib/typescript/src/components/Circle.types.d.ts +33 -0
- package/lib/typescript/src/components/Circle.types.d.ts.map +1 -0
- package/lib/typescript/src/components/Circle.web.d.ts +3 -0
- package/lib/typescript/src/components/Circle.web.d.ts.map +1 -0
- package/lib/typescript/src/components/GeoJson.d.ts +2 -1
- package/lib/typescript/src/components/GeoJson.d.ts.map +1 -1
- package/lib/typescript/src/components/GroundOverlay.d.ts +10 -0
- package/lib/typescript/src/components/GroundOverlay.d.ts.map +1 -0
- package/lib/typescript/src/components/GroundOverlay.types.d.ts +35 -0
- package/lib/typescript/src/components/GroundOverlay.types.d.ts.map +1 -0
- package/lib/typescript/src/components/GroundOverlay.web.d.ts +3 -0
- package/lib/typescript/src/components/GroundOverlay.web.d.ts.map +1 -0
- package/lib/typescript/src/components/Marker.d.ts +6 -3
- package/lib/typescript/src/components/Marker.d.ts.map +1 -1
- package/lib/typescript/src/components/Marker.types.d.ts +18 -6
- package/lib/typescript/src/components/Marker.types.d.ts.map +1 -1
- package/lib/typescript/src/components/Marker.web.d.ts +3 -2
- package/lib/typescript/src/components/Marker.web.d.ts.map +1 -1
- package/lib/typescript/src/components/Polygon.web.d.ts +1 -1
- package/lib/typescript/src/components/Polygon.web.d.ts.map +1 -1
- package/lib/typescript/src/components/Polyline.web.d.ts +1 -1
- package/lib/typescript/src/components/Polyline.web.d.ts.map +1 -1
- package/lib/typescript/src/components/TileOverlay.d.ts +10 -0
- package/lib/typescript/src/components/TileOverlay.d.ts.map +1 -0
- package/lib/typescript/src/components/TileOverlay.types.d.ts +36 -0
- package/lib/typescript/src/components/TileOverlay.types.d.ts.map +1 -0
- package/lib/typescript/src/components/TileOverlay.web.d.ts +3 -0
- package/lib/typescript/src/components/TileOverlay.web.d.ts.map +1 -0
- package/lib/typescript/src/components/index.d.ts +7 -1
- package/lib/typescript/src/components/index.d.ts.map +1 -1
- package/lib/typescript/src/components/index.web.d.ts +6 -0
- package/lib/typescript/src/components/index.web.d.ts.map +1 -1
- package/lib/typescript/src/fabric/LuggCalloutViewNativeComponent.d.ts +1 -1
- package/lib/typescript/src/fabric/LuggCircleViewNativeComponent.d.ts +18 -0
- package/lib/typescript/src/fabric/LuggCircleViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/fabric/LuggGroundOverlayViewNativeComponent.d.ts +21 -0
- package/lib/typescript/src/fabric/LuggGroundOverlayViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts +13 -5
- package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts +8 -1
- package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/fabric/LuggTileOverlayViewNativeComponent.d.ts +21 -0
- package/lib/typescript/src/fabric/LuggTileOverlayViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +9 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/index.web.d.ts +7 -1
- package/lib/typescript/src/index.web.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +0 -4
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +17 -11
- package/src/MapProvider.web.tsx +5 -3
- package/src/MapView.tsx +19 -1
- package/src/MapView.types.ts +142 -1
- package/src/MapView.web.tsx +379 -340
- package/src/components/Circle.tsx +52 -0
- package/src/components/Circle.types.ts +33 -0
- package/src/components/Circle.web.tsx +95 -0
- package/src/components/GeoJson.tsx +3 -3
- package/src/components/GroundOverlay.tsx +47 -0
- package/src/components/GroundOverlay.types.ts +36 -0
- package/src/components/GroundOverlay.web.tsx +81 -0
- package/src/components/Marker.tsx +32 -6
- package/src/components/Marker.types.ts +19 -6
- package/src/components/Marker.web.tsx +196 -150
- package/src/components/Polygon.web.tsx +93 -89
- package/src/components/Polyline.web.tsx +235 -235
- package/src/components/TileOverlay.tsx +44 -0
- package/src/components/TileOverlay.types.ts +37 -0
- package/src/components/TileOverlay.web.tsx +97 -0
- package/src/components/index.ts +10 -0
- package/src/components/index.web.ts +9 -0
- package/src/fabric/LuggCalloutViewNativeComponent.ts +1 -1
- package/src/fabric/LuggCircleViewNativeComponent.ts +25 -0
- package/src/fabric/LuggGroundOverlayViewNativeComponent.ts +29 -0
- package/src/fabric/LuggMapViewNativeComponent.ts +16 -5
- package/src/fabric/LuggMarkerViewNativeComponent.ts +14 -2
- package/src/fabric/LuggTileOverlayViewNativeComponent.ts +30 -0
- package/src/index.ts +12 -1
- package/src/index.web.ts +15 -0
- package/src/types.ts +0 -5
- package/plugin/build/withMapsAndroid.d.ts +0 -5
- package/plugin/build/withMapsAndroid.js +0 -15
- package/plugin/build/withMapsIOS.d.ts +0 -5
- package/plugin/build/withMapsIOS.js +0 -27
|
@@ -1,26 +1,42 @@
|
|
|
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.view.ViewTreeObserver
|
|
6
9
|
import android.widget.ImageView
|
|
7
10
|
import androidx.core.graphics.createBitmap
|
|
11
|
+
import androidx.core.view.ViewCompat
|
|
12
|
+
import androidx.core.view.WindowInsetsCompat
|
|
8
13
|
import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
9
14
|
import com.google.android.gms.maps.CameraUpdateFactory
|
|
10
15
|
import com.google.android.gms.maps.GoogleMap
|
|
11
16
|
import com.google.android.gms.maps.GoogleMapOptions
|
|
12
17
|
import com.google.android.gms.maps.MapView
|
|
13
18
|
import com.google.android.gms.maps.OnMapReadyCallback
|
|
14
|
-
import com.google.android.gms.maps.model.AdvancedMarker
|
|
15
|
-
import com.google.android.gms.maps.model.AdvancedMarkerOptions
|
|
16
19
|
import com.google.android.gms.maps.model.BitmapDescriptorFactory
|
|
20
|
+
import com.google.android.gms.maps.model.CameraPosition
|
|
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
|
|
17
25
|
import com.google.android.gms.maps.model.LatLng
|
|
26
|
+
import com.google.android.gms.maps.model.LatLngBounds
|
|
18
27
|
import com.google.android.gms.maps.model.MapColorScheme
|
|
19
28
|
import com.google.android.gms.maps.model.Marker
|
|
29
|
+
import com.google.android.gms.maps.model.MarkerOptions
|
|
20
30
|
import com.google.android.gms.maps.model.Polygon
|
|
21
31
|
import com.google.android.gms.maps.model.PolygonOptions
|
|
22
32
|
import com.google.android.gms.maps.model.PolylineOptions
|
|
33
|
+
import com.google.android.gms.maps.model.TileOverlayOptions
|
|
34
|
+
import com.google.android.gms.maps.model.UrlTileProvider
|
|
23
35
|
import com.luggmaps.LuggCalloutView
|
|
36
|
+
import com.luggmaps.LuggCircleView
|
|
37
|
+
import com.luggmaps.LuggCircleViewDelegate
|
|
38
|
+
import com.luggmaps.LuggGroundOverlayView
|
|
39
|
+
import com.luggmaps.LuggGroundOverlayViewDelegate
|
|
24
40
|
import com.luggmaps.LuggMapWrapperView
|
|
25
41
|
import com.luggmaps.LuggMarkerView
|
|
26
42
|
import com.luggmaps.LuggMarkerViewDelegate
|
|
@@ -28,6 +44,16 @@ import com.luggmaps.LuggPolygonView
|
|
|
28
44
|
import com.luggmaps.LuggPolygonViewDelegate
|
|
29
45
|
import com.luggmaps.LuggPolylineView
|
|
30
46
|
import com.luggmaps.LuggPolylineViewDelegate
|
|
47
|
+
import com.luggmaps.LuggTileOverlayView
|
|
48
|
+
import com.luggmaps.LuggTileOverlayViewDelegate
|
|
49
|
+
import com.luggmaps.extensions.findViewByTag
|
|
50
|
+
import java.net.URL
|
|
51
|
+
import kotlin.math.atan
|
|
52
|
+
import kotlin.math.ln
|
|
53
|
+
import kotlin.math.log2
|
|
54
|
+
import kotlin.math.pow
|
|
55
|
+
import kotlin.math.sin
|
|
56
|
+
import kotlin.math.sinh
|
|
31
57
|
|
|
32
58
|
class GoogleMapProvider(private val context: Context) :
|
|
33
59
|
MapProvider,
|
|
@@ -35,22 +61,33 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
35
61
|
LuggMarkerViewDelegate,
|
|
36
62
|
LuggPolylineViewDelegate,
|
|
37
63
|
LuggPolygonViewDelegate,
|
|
64
|
+
LuggCircleViewDelegate,
|
|
65
|
+
LuggGroundOverlayViewDelegate,
|
|
66
|
+
LuggTileOverlayViewDelegate,
|
|
38
67
|
GoogleMap.OnCameraMoveStartedListener,
|
|
39
68
|
GoogleMap.OnCameraMoveListener,
|
|
40
69
|
GoogleMap.OnCameraIdleListener,
|
|
41
70
|
GoogleMap.OnMapClickListener,
|
|
42
71
|
GoogleMap.OnMapLongClickListener,
|
|
43
72
|
GoogleMap.OnPolygonClickListener,
|
|
73
|
+
GoogleMap.OnCircleClickListener,
|
|
74
|
+
GoogleMap.OnGroundOverlayClickListener,
|
|
44
75
|
GoogleMap.OnMarkerClickListener,
|
|
45
76
|
GoogleMap.OnMarkerDragListener,
|
|
46
|
-
GoogleMap.InfoWindowAdapter
|
|
77
|
+
GoogleMap.InfoWindowAdapter,
|
|
78
|
+
ComponentCallbacks2 {
|
|
47
79
|
|
|
48
80
|
override var delegate: MapProviderDelegate? = null
|
|
49
81
|
override val isMapReady: Boolean get() = _isMapReady
|
|
50
82
|
|
|
51
83
|
var mapId: String = DEMO_MAP_ID
|
|
52
84
|
|
|
85
|
+
// Renders a non-interactive static map — lite mode when the view fits
|
|
86
|
+
// the lite bitmap cap, a gesture-less full map otherwise. Creation-time only.
|
|
87
|
+
var staticMode: Boolean = false
|
|
88
|
+
|
|
53
89
|
private var wrapperView: LuggMapWrapperView? = null
|
|
90
|
+
private var currentNightMode: Int = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
|
|
54
91
|
private var mapView: MapView? = null
|
|
55
92
|
private var googleMap: GoogleMap? = null
|
|
56
93
|
private var _isMapReady = false
|
|
@@ -58,10 +95,16 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
58
95
|
private val pendingMarkerViews = mutableSetOf<LuggMarkerView>()
|
|
59
96
|
private val pendingPolylineViews = mutableSetOf<LuggPolylineView>()
|
|
60
97
|
private val pendingPolygonViews = mutableSetOf<LuggPolygonView>()
|
|
98
|
+
private val pendingCircleViews = mutableSetOf<LuggCircleView>()
|
|
99
|
+
private val pendingGroundOverlayViews = mutableSetOf<LuggGroundOverlayView>()
|
|
100
|
+
private val pendingTileOverlayViews = mutableSetOf<LuggTileOverlayView>()
|
|
61
101
|
private val polylineAnimators = mutableMapOf<LuggPolylineView, PolylineAnimator>()
|
|
62
102
|
private val polygonToViewMap = mutableMapOf<Polygon, LuggPolygonView>()
|
|
103
|
+
private val circleToViewMap = mutableMapOf<Circle, LuggCircleView>()
|
|
104
|
+
private val groundOverlayToViewMap = mutableMapOf<GroundOverlay, LuggGroundOverlayView>()
|
|
63
105
|
private val markerToViewMap = mutableMapOf<Marker, LuggMarkerView>()
|
|
64
106
|
private val liveMarkerViews = mutableSetOf<LuggMarkerView>()
|
|
107
|
+
private var staticProjectionReady = false
|
|
65
108
|
private var activeNonBubbledMarker: Marker? = null
|
|
66
109
|
private var tapLocation: LatLng? = null
|
|
67
110
|
|
|
@@ -75,6 +118,7 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
75
118
|
private var scrollEnabled: Boolean = true
|
|
76
119
|
private var rotateEnabled: Boolean = true
|
|
77
120
|
private var pitchEnabled: Boolean = true
|
|
121
|
+
private var compassEnabled: Boolean = true
|
|
78
122
|
private var userLocationEnabled: Boolean = false
|
|
79
123
|
private var userLocationButtonEnabled: Boolean = false
|
|
80
124
|
|
|
@@ -88,6 +132,17 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
88
132
|
// Edge Insets
|
|
89
133
|
private var edgeInsets: EdgeInsets = EdgeInsets()
|
|
90
134
|
|
|
135
|
+
// Inset adjustment
|
|
136
|
+
private var insetAdjustment: String = "never"
|
|
137
|
+
private var systemInsets: EdgeInsets = EdgeInsets()
|
|
138
|
+
|
|
139
|
+
// The maps renderer adds and positions the watermark asynchronously; the
|
|
140
|
+
// layout listener re-evaluates the translation on every layout pass
|
|
141
|
+
private var watermarkLayoutListener: ViewTreeObserver.OnGlobalLayoutListener? = null
|
|
142
|
+
private var watermarkView: View? = null
|
|
143
|
+
private var watermarkTargetX = 0f
|
|
144
|
+
private var watermarkTargetY = 0f
|
|
145
|
+
|
|
91
146
|
// region MapProvider
|
|
92
147
|
|
|
93
148
|
override fun initializeMap(wrapperView: View, latitude: Double, longitude: Double, zoom: Float) {
|
|
@@ -98,18 +153,46 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
98
153
|
initialZoom = zoom
|
|
99
154
|
|
|
100
155
|
val wrapper = wrapperView as LuggMapWrapperView
|
|
156
|
+
wrapper.relayoutChildOnRequest = staticMode
|
|
101
157
|
this.wrapperView = wrapper
|
|
102
158
|
|
|
103
|
-
|
|
159
|
+
context.applicationContext.registerComponentCallbacks(this)
|
|
160
|
+
|
|
161
|
+
// Static mode needs the view size to pick lite vs full map, so wait
|
|
162
|
+
// for the first layout if it hasn't happened yet
|
|
163
|
+
if (staticMode && (wrapper.width == 0 || wrapper.height == 0)) {
|
|
164
|
+
wrapper.onLayoutReady = { createMapView() }
|
|
165
|
+
} else {
|
|
166
|
+
createMapView()
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private fun createMapView() {
|
|
171
|
+
if (mapView != null) return
|
|
172
|
+
val wrapper = wrapperView ?: return
|
|
173
|
+
|
|
174
|
+
// Lite mode renders the map as a single bitmap capped at ~2048px per
|
|
175
|
+
// dimension, centered in the view — bigger views get letterboxed. Fall
|
|
176
|
+
// back to a full map (gestures disabled) when the view exceeds the cap
|
|
177
|
+
val useLite = staticMode && maxOf(wrapper.width, wrapper.height) <= LITE_MODE_MAX_SIZE
|
|
178
|
+
val options = GoogleMapOptions().liteMode(useLite)
|
|
179
|
+
// Lite mode doesn't support map IDs (cloud-based styling); setting one
|
|
180
|
+
// makes the static map render blank
|
|
181
|
+
if (!useLite) options.mapId(mapId)
|
|
104
182
|
mapView = MapView(context, options).also { view ->
|
|
105
183
|
view.onCreate(null)
|
|
106
184
|
view.onResume()
|
|
107
185
|
view.getMapAsync(this)
|
|
108
186
|
wrapper.addView(view)
|
|
109
187
|
}
|
|
188
|
+
wrapper.onLayoutReady = null
|
|
110
189
|
}
|
|
111
190
|
|
|
112
191
|
override fun destroy() {
|
|
192
|
+
wrapperView?.onLayoutReady = null
|
|
193
|
+
detachWindowInsetsListener()
|
|
194
|
+
detachWatermarkLayoutListener()
|
|
195
|
+
context.applicationContext.unregisterComponentCallbacks(this)
|
|
113
196
|
dismissNonBubbledCallout()
|
|
114
197
|
for (markerView in liveMarkerViews) {
|
|
115
198
|
markerView.onUpdate = null
|
|
@@ -118,9 +201,14 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
118
201
|
pendingMarkerViews.clear()
|
|
119
202
|
pendingPolylineViews.clear()
|
|
120
203
|
pendingPolygonViews.clear()
|
|
204
|
+
pendingCircleViews.clear()
|
|
205
|
+
pendingGroundOverlayViews.clear()
|
|
206
|
+
pendingTileOverlayViews.clear()
|
|
121
207
|
polylineAnimators.values.forEach { it.destroy() }
|
|
122
208
|
polylineAnimators.clear()
|
|
123
209
|
polygonToViewMap.clear()
|
|
210
|
+
circleToViewMap.clear()
|
|
211
|
+
groundOverlayToViewMap.clear()
|
|
124
212
|
markerToViewMap.clear()
|
|
125
213
|
wrapperView?.touchEventHandler = null
|
|
126
214
|
wrapperView = null
|
|
@@ -130,6 +218,8 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
130
218
|
googleMap?.setOnMapClickListener(null)
|
|
131
219
|
googleMap?.setOnMapLongClickListener(null)
|
|
132
220
|
googleMap?.setOnPolygonClickListener(null)
|
|
221
|
+
googleMap?.setOnCircleClickListener(null)
|
|
222
|
+
googleMap?.setOnGroundOverlayClickListener(null)
|
|
133
223
|
googleMap?.setOnMarkerClickListener(null)
|
|
134
224
|
googleMap?.setOnMarkerDragListener(null)
|
|
135
225
|
googleMap?.setInfoWindowAdapter(null)
|
|
@@ -145,7 +235,23 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
145
235
|
googleMap = map
|
|
146
236
|
_isMapReady = true
|
|
147
237
|
|
|
148
|
-
|
|
238
|
+
if (staticMode) {
|
|
239
|
+
// Tapping the map otherwise shows an "open in Google Maps" toolbar
|
|
240
|
+
map.uiSettings.isMapToolbarEnabled = false
|
|
241
|
+
// The projection is only valid once the map has rendered, and no
|
|
242
|
+
// camera events fire afterwards to correct live marker positions
|
|
243
|
+
map.setOnMapLoadedCallback {
|
|
244
|
+
staticProjectionReady = true
|
|
245
|
+
positionLiveMarkers()
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Padding must be in place before the initial camera move: setPadding
|
|
250
|
+
// never repositions the current camera, but camera updates after it are
|
|
251
|
+
// relative to the padded center
|
|
252
|
+
applyEdgeInsets()
|
|
253
|
+
|
|
254
|
+
val position = if (staticMode) staticCameraTarget() else LatLng(initialLatitude, initialLongitude)
|
|
149
255
|
map.moveCamera(CameraUpdateFactory.newLatLngZoom(position, initialZoom))
|
|
150
256
|
|
|
151
257
|
map.setOnCameraMoveStartedListener(this)
|
|
@@ -154,6 +260,8 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
154
260
|
map.setOnMapClickListener(this)
|
|
155
261
|
map.setOnMapLongClickListener(this)
|
|
156
262
|
map.setOnPolygonClickListener(this)
|
|
263
|
+
map.setOnCircleClickListener(this)
|
|
264
|
+
map.setOnGroundOverlayClickListener(this)
|
|
157
265
|
map.setOnMarkerClickListener(this)
|
|
158
266
|
map.setOnMarkerDragListener(this)
|
|
159
267
|
map.setInfoWindowAdapter(this)
|
|
@@ -166,12 +274,15 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
166
274
|
|
|
167
275
|
applyUiSettings()
|
|
168
276
|
applyZoomLimits()
|
|
169
|
-
|
|
277
|
+
applyInsetAdjustment()
|
|
170
278
|
applyTheme()
|
|
171
279
|
applyUserLocation()
|
|
172
280
|
processPendingMarkers()
|
|
173
281
|
processPendingPolylines()
|
|
174
282
|
processPendingPolygons()
|
|
283
|
+
processPendingCircles()
|
|
284
|
+
processPendingGroundOverlays()
|
|
285
|
+
processPendingTileOverlays()
|
|
175
286
|
|
|
176
287
|
delegate?.mapProviderDidReady()
|
|
177
288
|
}
|
|
@@ -227,6 +338,22 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
227
338
|
}
|
|
228
339
|
}
|
|
229
340
|
|
|
341
|
+
override fun onCircleClick(circle: Circle) {
|
|
342
|
+
val circleView = circleToViewMap[circle]
|
|
343
|
+
if (circleView?.tappable == true) {
|
|
344
|
+
circleView.emitPressEvent()
|
|
345
|
+
} else {
|
|
346
|
+
onMapClick(tapLocation ?: return)
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
override fun onGroundOverlayClick(groundOverlay: GroundOverlay) {
|
|
351
|
+
val view = groundOverlayToViewMap[groundOverlay]
|
|
352
|
+
if (view?.tappable == true) {
|
|
353
|
+
view.emitPressEvent()
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
230
357
|
override fun onMarkerClick(marker: Marker): Boolean {
|
|
231
358
|
dismissNonBubbledCallout()
|
|
232
359
|
|
|
@@ -236,10 +363,17 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
236
363
|
|
|
237
364
|
val calloutView = view.calloutView
|
|
238
365
|
if (calloutView != null && !calloutView.bubbled && calloutView.hasCustomContent) {
|
|
239
|
-
|
|
366
|
+
if (view.centerOnPress) {
|
|
367
|
+
googleMap?.animateCamera(CameraUpdateFactory.newLatLng(marker.position))
|
|
368
|
+
}
|
|
240
369
|
showNonBubbledCallout(marker, calloutView)
|
|
241
370
|
return true
|
|
242
371
|
}
|
|
372
|
+
|
|
373
|
+
if (!view.centerOnPress) {
|
|
374
|
+
marker.showInfoWindow()
|
|
375
|
+
return true
|
|
376
|
+
}
|
|
243
377
|
}
|
|
244
378
|
return false
|
|
245
379
|
}
|
|
@@ -353,8 +487,8 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
353
487
|
val map = googleMap ?: return
|
|
354
488
|
|
|
355
489
|
val point = map.projection.toScreenLocation(marker.position)
|
|
356
|
-
contentView.translationX = point.x - contentView.width
|
|
357
|
-
contentView.translationY = point.y - contentView.height
|
|
490
|
+
contentView.translationX = point.x - contentView.width / 2f + calloutView.offsetX
|
|
491
|
+
contentView.translationY = point.y - markerView.scaledContentHeight * markerView.anchorY - contentView.height + calloutView.offsetY
|
|
358
492
|
}
|
|
359
493
|
|
|
360
494
|
// endregion
|
|
@@ -363,24 +497,33 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
363
497
|
|
|
364
498
|
override fun setZoomEnabled(enabled: Boolean) {
|
|
365
499
|
zoomEnabled = enabled
|
|
500
|
+
if (staticMode) return
|
|
366
501
|
googleMap?.uiSettings?.isZoomGesturesEnabled = enabled
|
|
367
502
|
}
|
|
368
503
|
|
|
369
504
|
override fun setScrollEnabled(enabled: Boolean) {
|
|
370
505
|
scrollEnabled = enabled
|
|
506
|
+
if (staticMode) return
|
|
371
507
|
googleMap?.uiSettings?.isScrollGesturesEnabled = enabled
|
|
372
508
|
}
|
|
373
509
|
|
|
374
510
|
override fun setRotateEnabled(enabled: Boolean) {
|
|
375
511
|
rotateEnabled = enabled
|
|
512
|
+
if (staticMode) return
|
|
376
513
|
googleMap?.uiSettings?.isRotateGesturesEnabled = enabled
|
|
377
514
|
}
|
|
378
515
|
|
|
379
516
|
override fun setPitchEnabled(enabled: Boolean) {
|
|
380
517
|
pitchEnabled = enabled
|
|
518
|
+
if (staticMode) return
|
|
381
519
|
googleMap?.uiSettings?.isTiltGesturesEnabled = enabled
|
|
382
520
|
}
|
|
383
521
|
|
|
522
|
+
override fun setCompassEnabled(enabled: Boolean) {
|
|
523
|
+
compassEnabled = enabled
|
|
524
|
+
googleMap?.uiSettings?.isCompassEnabled = enabled
|
|
525
|
+
}
|
|
526
|
+
|
|
384
527
|
@SuppressLint("MissingPermission")
|
|
385
528
|
override fun setUserLocationEnabled(enabled: Boolean) {
|
|
386
529
|
userLocationEnabled = enabled
|
|
@@ -398,6 +541,15 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
398
541
|
googleMap?.uiSettings?.isMyLocationButtonEnabled = enabled
|
|
399
542
|
}
|
|
400
543
|
|
|
544
|
+
override fun setMapType(value: String) {
|
|
545
|
+
googleMap?.mapType = when (value) {
|
|
546
|
+
"satellite" -> GoogleMap.MAP_TYPE_SATELLITE
|
|
547
|
+
"terrain" -> GoogleMap.MAP_TYPE_TERRAIN
|
|
548
|
+
"hybrid" -> GoogleMap.MAP_TYPE_HYBRID
|
|
549
|
+
else -> GoogleMap.MAP_TYPE_NORMAL
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
401
553
|
override fun setTheme(value: String) {
|
|
402
554
|
theme = value
|
|
403
555
|
applyTheme()
|
|
@@ -418,6 +570,11 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
418
570
|
}
|
|
419
571
|
|
|
420
572
|
override fun setEdgeInsets(edgeInsets: EdgeInsets) {
|
|
573
|
+
if (staticMode) {
|
|
574
|
+
setStaticEdgeInsets(edgeInsets)
|
|
575
|
+
return
|
|
576
|
+
}
|
|
577
|
+
|
|
421
578
|
val oldInsets = this.edgeInsets
|
|
422
579
|
this.edgeInsets = edgeInsets
|
|
423
580
|
applyEdgeInsets()
|
|
@@ -431,13 +588,18 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
431
588
|
}
|
|
432
589
|
|
|
433
590
|
override fun setEdgeInsets(edgeInsets: EdgeInsets, duration: Int) {
|
|
591
|
+
if (staticMode) {
|
|
592
|
+
setStaticEdgeInsets(edgeInsets)
|
|
593
|
+
return
|
|
594
|
+
}
|
|
595
|
+
|
|
434
596
|
val map = googleMap
|
|
435
597
|
val oldInsets = this.edgeInsets
|
|
436
598
|
this.edgeInsets = edgeInsets
|
|
437
599
|
|
|
438
600
|
if (map != null && oldInsets != edgeInsets) {
|
|
439
601
|
val cameraUpdate = CameraUpdateFactory.newCameraPosition(map.cameraPosition)
|
|
440
|
-
applyEdgeInsets()
|
|
602
|
+
applyEdgeInsets(duration)
|
|
441
603
|
when {
|
|
442
604
|
duration < 0 -> map.animateCamera(cameraUpdate)
|
|
443
605
|
duration > 0 -> map.animateCamera(cameraUpdate, duration, null)
|
|
@@ -448,6 +610,17 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
448
610
|
}
|
|
449
611
|
}
|
|
450
612
|
|
|
613
|
+
override fun setInsetAdjustment(value: String) {
|
|
614
|
+
insetAdjustment = value
|
|
615
|
+
applyInsetAdjustment()
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
override fun setPoiEnabled(enabled: Boolean) {}
|
|
619
|
+
|
|
620
|
+
override fun setPoiFilterMode(mode: String) {}
|
|
621
|
+
|
|
622
|
+
override fun setPoiFilterCategories(categories: List<String>) {}
|
|
623
|
+
|
|
451
624
|
// endregion
|
|
452
625
|
|
|
453
626
|
// region MarkerViewDelegate
|
|
@@ -469,6 +642,30 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
469
642
|
syncMarkerView(markerView)
|
|
470
643
|
}
|
|
471
644
|
|
|
645
|
+
override fun showCalloutForMarkerView(markerView: LuggMarkerView) {
|
|
646
|
+
val marker = markerView.marker ?: return
|
|
647
|
+
val calloutView = markerView.calloutView
|
|
648
|
+
|
|
649
|
+
if (calloutView != null && calloutView.hasCustomContent) {
|
|
650
|
+
dismissNonBubbledCallout()
|
|
651
|
+
if (calloutView.bubbled) {
|
|
652
|
+
marker.showInfoWindow()
|
|
653
|
+
} else {
|
|
654
|
+
showNonBubbledCallout(marker, calloutView)
|
|
655
|
+
}
|
|
656
|
+
return
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
if (!markerView.title.isNullOrEmpty()) {
|
|
660
|
+
marker.showInfoWindow()
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
override fun hideCalloutForMarkerView(markerView: LuggMarkerView) {
|
|
665
|
+
dismissNonBubbledCallout()
|
|
666
|
+
markerView.marker?.hideInfoWindow()
|
|
667
|
+
}
|
|
668
|
+
|
|
472
669
|
// endregion
|
|
473
670
|
|
|
474
671
|
// region PolylineViewDelegate
|
|
@@ -477,6 +674,10 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
477
674
|
syncPolylineView(polylineView)
|
|
478
675
|
}
|
|
479
676
|
|
|
677
|
+
override fun polylineViewDidDrop(polylineView: LuggPolylineView) {
|
|
678
|
+
teardownPolyline(polylineView)
|
|
679
|
+
}
|
|
680
|
+
|
|
480
681
|
// endregion
|
|
481
682
|
|
|
482
683
|
// region PolygonViewDelegate
|
|
@@ -487,6 +688,30 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
487
688
|
|
|
488
689
|
// endregion
|
|
489
690
|
|
|
691
|
+
// region CircleViewDelegate
|
|
692
|
+
|
|
693
|
+
override fun circleViewDidUpdate(circleView: LuggCircleView) {
|
|
694
|
+
syncCircleView(circleView)
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// endregion
|
|
698
|
+
|
|
699
|
+
// region GroundOverlayViewDelegate
|
|
700
|
+
|
|
701
|
+
override fun groundOverlayViewDidUpdate(groundOverlayView: LuggGroundOverlayView) {
|
|
702
|
+
syncGroundOverlayView(groundOverlayView)
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// endregion
|
|
706
|
+
|
|
707
|
+
// region TileOverlayViewDelegate
|
|
708
|
+
|
|
709
|
+
override fun tileOverlayViewDidUpdate(tileOverlayView: LuggTileOverlayView) {
|
|
710
|
+
syncTileOverlayView(tileOverlayView)
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// endregion
|
|
714
|
+
|
|
490
715
|
// region Marker Management
|
|
491
716
|
|
|
492
717
|
override fun addMarkerView(markerView: LuggMarkerView) {
|
|
@@ -546,12 +771,12 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
546
771
|
val map = googleMap ?: return
|
|
547
772
|
|
|
548
773
|
val position = LatLng(markerView.latitude, markerView.longitude)
|
|
549
|
-
val options =
|
|
774
|
+
val options = MarkerOptions()
|
|
550
775
|
.position(position)
|
|
551
776
|
.title(markerView.title)
|
|
552
777
|
.snippet(markerView.description)
|
|
553
778
|
|
|
554
|
-
val marker = map.addMarker(options)
|
|
779
|
+
val marker = map.addMarker(options) ?: return
|
|
555
780
|
marker.setAnchor(markerView.anchorX, markerView.anchorY)
|
|
556
781
|
marker.zIndex = markerView.zIndex
|
|
557
782
|
marker.rotation = markerView.rotate
|
|
@@ -569,9 +794,8 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
569
794
|
}
|
|
570
795
|
}
|
|
571
796
|
|
|
572
|
-
//
|
|
573
|
-
//
|
|
574
|
-
// marker uses a transparent bitmap matching the content size so taps still trigger onMarkerClick.
|
|
797
|
+
// Live marker: content view is added to the wrapper and positioned via screen projection.
|
|
798
|
+
// The underlying marker uses a transparent bitmap matching the content size so taps still trigger onMarkerClick.
|
|
575
799
|
private fun showLiveMarker(markerView: LuggMarkerView) {
|
|
576
800
|
val wrapper = wrapperView ?: return
|
|
577
801
|
|
|
@@ -582,6 +806,10 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
582
806
|
|
|
583
807
|
val contentView = markerView.contentView
|
|
584
808
|
contentView.pointerEvents = com.facebook.react.uimanager.PointerEvents.NONE
|
|
809
|
+
// Until the lite map's projection is valid, hide the marker instead of
|
|
810
|
+
// flashing it at a wrong position; onMapLoaded positions and shows it
|
|
811
|
+
contentView.visibility =
|
|
812
|
+
if (staticMode && !staticProjectionReady) View.INVISIBLE else View.VISIBLE
|
|
585
813
|
(contentView.parent as? android.view.ViewGroup)?.removeView(contentView)
|
|
586
814
|
wrapper.addView(contentView)
|
|
587
815
|
liveMarkerViews.add(markerView)
|
|
@@ -613,10 +841,12 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
613
841
|
|
|
614
842
|
private fun positionLiveMarker(markerView: LuggMarkerView) {
|
|
615
843
|
val map = googleMap ?: return
|
|
844
|
+
if (staticMode && !staticProjectionReady) return
|
|
616
845
|
val contentView = markerView.contentView
|
|
617
846
|
val point = map.projection.toScreenLocation(LatLng(markerView.latitude, markerView.longitude))
|
|
618
847
|
contentView.translationX = point.x - contentView.width * markerView.anchorX
|
|
619
848
|
contentView.translationY = point.y - contentView.height * markerView.anchorY
|
|
849
|
+
contentView.visibility = View.VISIBLE
|
|
620
850
|
}
|
|
621
851
|
|
|
622
852
|
// endregion
|
|
@@ -629,8 +859,12 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
629
859
|
}
|
|
630
860
|
|
|
631
861
|
override fun removePolylineView(polylineView: LuggPolylineView) {
|
|
632
|
-
|
|
633
|
-
|
|
862
|
+
teardownPolyline(polylineView)
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
private fun teardownPolyline(polylineView: LuggPolylineView) {
|
|
866
|
+
pendingPolylineViews.remove(polylineView)
|
|
867
|
+
polylineAnimators.remove(polylineView)?.destroy()
|
|
634
868
|
polylineView.polyline?.remove()
|
|
635
869
|
polylineView.polyline = null
|
|
636
870
|
}
|
|
@@ -646,15 +880,13 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
646
880
|
return
|
|
647
881
|
}
|
|
648
882
|
|
|
649
|
-
polylineView.polyline?.width = polylineView.strokeWidth.dpToPx()
|
|
650
|
-
polylineView.polyline?.zIndex = polylineView.zIndex
|
|
651
|
-
|
|
652
883
|
polylineAnimators[polylineView]?.apply {
|
|
653
884
|
coordinates = polylineView.coordinates
|
|
654
885
|
strokeColors = polylineView.strokeColors
|
|
655
886
|
strokeWidth = polylineView.strokeWidth.dpToPx()
|
|
887
|
+
zIndex = polylineView.zIndex
|
|
656
888
|
animatedOptions = polylineView.animatedOptions
|
|
657
|
-
animated = polylineView.animated
|
|
889
|
+
animated = polylineView.animated && !staticMode
|
|
658
890
|
update()
|
|
659
891
|
}
|
|
660
892
|
}
|
|
@@ -668,6 +900,8 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
668
900
|
private fun addPolylineViewToMap(polylineView: LuggPolylineView) {
|
|
669
901
|
val map = googleMap ?: return
|
|
670
902
|
|
|
903
|
+
polylineAnimators.remove(polylineView)?.destroy()
|
|
904
|
+
|
|
671
905
|
val options = PolylineOptions()
|
|
672
906
|
.width(polylineView.strokeWidth.dpToPx())
|
|
673
907
|
.zIndex(polylineView.zIndex)
|
|
@@ -680,8 +914,10 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
680
914
|
coordinates = polylineView.coordinates
|
|
681
915
|
strokeColors = polylineView.strokeColors
|
|
682
916
|
strokeWidth = polylineView.strokeWidth.dpToPx()
|
|
917
|
+
zIndex = polylineView.zIndex
|
|
683
918
|
animatedOptions = polylineView.animatedOptions
|
|
684
|
-
|
|
919
|
+
// Static maps render once; show the full polyline instead of animating
|
|
920
|
+
animated = polylineView.animated && !staticMode
|
|
685
921
|
update()
|
|
686
922
|
}
|
|
687
923
|
|
|
@@ -753,6 +989,218 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
753
989
|
|
|
754
990
|
// endregion
|
|
755
991
|
|
|
992
|
+
// region Circle Management
|
|
993
|
+
|
|
994
|
+
override fun addCircleView(circleView: LuggCircleView) {
|
|
995
|
+
circleView.delegate = this
|
|
996
|
+
syncCircleView(circleView)
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
override fun removeCircleView(circleView: LuggCircleView) {
|
|
1000
|
+
circleView.circle?.let { circleToViewMap.remove(it) }
|
|
1001
|
+
circleView.circle?.remove()
|
|
1002
|
+
circleView.circle = null
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
private fun syncCircleView(circleView: LuggCircleView) {
|
|
1006
|
+
if (googleMap == null) {
|
|
1007
|
+
pendingCircleViews.add(circleView)
|
|
1008
|
+
return
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
if (circleView.circle == null) {
|
|
1012
|
+
addCircleViewToMap(circleView)
|
|
1013
|
+
return
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
circleView.circle?.apply {
|
|
1017
|
+
center = circleView.center
|
|
1018
|
+
radius = circleView.radius
|
|
1019
|
+
fillColor = circleView.fillColor
|
|
1020
|
+
strokeColor = circleView.strokeColor
|
|
1021
|
+
strokeWidth = circleView.strokeWidth.dpToPx()
|
|
1022
|
+
zIndex = circleView.zIndex
|
|
1023
|
+
isClickable = true
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
private fun processPendingCircles() {
|
|
1028
|
+
if (googleMap == null) return
|
|
1029
|
+
pendingCircleViews.forEach { addCircleViewToMap(it) }
|
|
1030
|
+
pendingCircleViews.clear()
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
private fun addCircleViewToMap(circleView: LuggCircleView) {
|
|
1034
|
+
val map = googleMap ?: return
|
|
1035
|
+
|
|
1036
|
+
val options = CircleOptions()
|
|
1037
|
+
.center(circleView.center)
|
|
1038
|
+
.radius(circleView.radius)
|
|
1039
|
+
.fillColor(circleView.fillColor)
|
|
1040
|
+
.strokeColor(circleView.strokeColor)
|
|
1041
|
+
.strokeWidth(circleView.strokeWidth.dpToPx())
|
|
1042
|
+
.zIndex(circleView.zIndex)
|
|
1043
|
+
.clickable(true)
|
|
1044
|
+
|
|
1045
|
+
val circle = map.addCircle(options)
|
|
1046
|
+
circleView.circle = circle
|
|
1047
|
+
circleToViewMap[circle] = circleView
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// endregion
|
|
1051
|
+
|
|
1052
|
+
// region Ground Overlay Management
|
|
1053
|
+
|
|
1054
|
+
override fun addGroundOverlayView(groundOverlayView: LuggGroundOverlayView) {
|
|
1055
|
+
groundOverlayView.delegate = this
|
|
1056
|
+
syncGroundOverlayView(groundOverlayView)
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
override fun removeGroundOverlayView(groundOverlayView: LuggGroundOverlayView) {
|
|
1060
|
+
groundOverlayView.delegate = null
|
|
1061
|
+
groundOverlayView.groundOverlay?.let { groundOverlayToViewMap.remove(it) }
|
|
1062
|
+
groundOverlayView.groundOverlay?.remove()
|
|
1063
|
+
groundOverlayView.groundOverlay = null
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
private fun syncGroundOverlayView(groundOverlayView: LuggGroundOverlayView) {
|
|
1067
|
+
if (googleMap == null) {
|
|
1068
|
+
pendingGroundOverlayViews.add(groundOverlayView)
|
|
1069
|
+
return
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
val imageUri = groundOverlayView.imageUri
|
|
1073
|
+
if (imageUri.isEmpty()) return
|
|
1074
|
+
|
|
1075
|
+
// Remove old overlay
|
|
1076
|
+
groundOverlayView.groundOverlay?.let { groundOverlayToViewMap.remove(it) }
|
|
1077
|
+
groundOverlayView.groundOverlay?.remove()
|
|
1078
|
+
groundOverlayView.groundOverlay = null
|
|
1079
|
+
|
|
1080
|
+
// Load image async and add overlay
|
|
1081
|
+
Thread {
|
|
1082
|
+
try {
|
|
1083
|
+
val connection = URL(imageUri).openConnection() as java.net.HttpURLConnection
|
|
1084
|
+
connection.instanceFollowRedirects = true
|
|
1085
|
+
connection.connect()
|
|
1086
|
+
val bitmap = android.graphics.BitmapFactory.decodeStream(connection.inputStream)
|
|
1087
|
+
connection.disconnect()
|
|
1088
|
+
if (bitmap != null) {
|
|
1089
|
+
mapView?.post {
|
|
1090
|
+
addGroundOverlayToMap(groundOverlayView, bitmap)
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
} catch (_: Exception) {}
|
|
1094
|
+
}.start()
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
private fun addGroundOverlayToMap(groundOverlayView: LuggGroundOverlayView, bitmap: android.graphics.Bitmap) {
|
|
1098
|
+
val map = googleMap ?: return
|
|
1099
|
+
|
|
1100
|
+
val bounds = LatLngBounds(groundOverlayView.southwest, groundOverlayView.northeast)
|
|
1101
|
+
val options = GroundOverlayOptions()
|
|
1102
|
+
.image(BitmapDescriptorFactory.fromBitmap(bitmap))
|
|
1103
|
+
.positionFromBounds(bounds)
|
|
1104
|
+
.transparency(1f - groundOverlayView.overlayOpacity)
|
|
1105
|
+
.bearing(groundOverlayView.bearing)
|
|
1106
|
+
.zIndex(groundOverlayView.zIndex)
|
|
1107
|
+
.clickable(groundOverlayView.tappable)
|
|
1108
|
+
|
|
1109
|
+
val overlay = map.addGroundOverlay(options)
|
|
1110
|
+
if (overlay != null) {
|
|
1111
|
+
groundOverlayView.groundOverlay = overlay
|
|
1112
|
+
groundOverlayToViewMap[overlay] = groundOverlayView
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
private fun processPendingGroundOverlays() {
|
|
1117
|
+
if (googleMap == null) return
|
|
1118
|
+
pendingGroundOverlayViews.forEach { syncGroundOverlayView(it) }
|
|
1119
|
+
pendingGroundOverlayViews.clear()
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// endregion
|
|
1123
|
+
|
|
1124
|
+
// region Tile Overlay Management
|
|
1125
|
+
|
|
1126
|
+
override fun addTileOverlayView(tileOverlayView: LuggTileOverlayView) {
|
|
1127
|
+
tileOverlayView.delegate = this
|
|
1128
|
+
syncTileOverlayView(tileOverlayView)
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
override fun removeTileOverlayView(tileOverlayView: LuggTileOverlayView) {
|
|
1132
|
+
tileOverlayView.delegate = null
|
|
1133
|
+
tileOverlayView.tileOverlay?.remove()
|
|
1134
|
+
tileOverlayView.tileOverlay = null
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
private fun syncTileOverlayView(tileOverlayView: LuggTileOverlayView) {
|
|
1138
|
+
if (googleMap == null) {
|
|
1139
|
+
pendingTileOverlayViews.add(tileOverlayView)
|
|
1140
|
+
return
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
val urlTemplate = tileOverlayView.urlTemplate
|
|
1144
|
+
if (urlTemplate.isEmpty()) return
|
|
1145
|
+
|
|
1146
|
+
// Remove old overlay
|
|
1147
|
+
tileOverlayView.tileOverlay?.remove()
|
|
1148
|
+
tileOverlayView.tileOverlay = null
|
|
1149
|
+
|
|
1150
|
+
val tileSize = tileOverlayView.tileSize
|
|
1151
|
+
val hasBounds = tileOverlayView.hasBounds
|
|
1152
|
+
val swLat = tileOverlayView.boundsSwLat
|
|
1153
|
+
val swLng = tileOverlayView.boundsSwLng
|
|
1154
|
+
val neLat = tileOverlayView.boundsNeLat
|
|
1155
|
+
val neLng = tileOverlayView.boundsNeLng
|
|
1156
|
+
|
|
1157
|
+
val tileProvider = object : UrlTileProvider(tileSize, tileSize) {
|
|
1158
|
+
override fun getTileUrl(x: Int, y: Int, zoom: Int): URL? {
|
|
1159
|
+
if (hasBounds) {
|
|
1160
|
+
val n = 2.0.pow(zoom.toDouble())
|
|
1161
|
+
val tileSWLat = Math.toDegrees(atan(sinh(Math.PI * (1 - 2.0 * (y + 1) / n))))
|
|
1162
|
+
val tileNELat = Math.toDegrees(atan(sinh(Math.PI * (1 - 2.0 * y / n))))
|
|
1163
|
+
val tileSWLng = x / n * 360.0 - 180.0
|
|
1164
|
+
val tileNELng = (x + 1) / n * 360.0 - 180.0
|
|
1165
|
+
|
|
1166
|
+
if (tileNELat < swLat ||
|
|
1167
|
+
tileSWLat > neLat ||
|
|
1168
|
+
tileNELng < swLng ||
|
|
1169
|
+
tileSWLng > neLng
|
|
1170
|
+
) {
|
|
1171
|
+
return null
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
val url = urlTemplate
|
|
1176
|
+
.replace("{x}", x.toString())
|
|
1177
|
+
.replace("{y}", y.toString())
|
|
1178
|
+
.replace("{z}", zoom.toString())
|
|
1179
|
+
return try {
|
|
1180
|
+
URL(url)
|
|
1181
|
+
} catch (_: Exception) {
|
|
1182
|
+
null
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
val options = TileOverlayOptions()
|
|
1188
|
+
.tileProvider(tileProvider)
|
|
1189
|
+
.transparency(1f - tileOverlayView.overlayOpacity)
|
|
1190
|
+
.zIndex(tileOverlayView.zIndex)
|
|
1191
|
+
|
|
1192
|
+
val overlay = googleMap?.addTileOverlay(options)
|
|
1193
|
+
tileOverlayView.tileOverlay = overlay
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
private fun processPendingTileOverlays() {
|
|
1197
|
+
if (googleMap == null) return
|
|
1198
|
+
pendingTileOverlayViews.forEach { syncTileOverlayView(it) }
|
|
1199
|
+
pendingTileOverlayViews.clear()
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// endregion
|
|
1203
|
+
|
|
756
1204
|
// region Lifecycle
|
|
757
1205
|
|
|
758
1206
|
override fun pauseAnimations() {
|
|
@@ -768,10 +1216,25 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
768
1216
|
// region Commands
|
|
769
1217
|
|
|
770
1218
|
override fun moveCamera(latitude: Double, longitude: Double, zoom: Double, duration: Int) {
|
|
1219
|
+
if (staticMode) {
|
|
1220
|
+
// Static maps don't animate; re-center at the final camera
|
|
1221
|
+
initialLatitude = latitude
|
|
1222
|
+
initialLongitude = longitude
|
|
1223
|
+
if (zoom > 0) initialZoom = zoom.toFloat()
|
|
1224
|
+
val map = googleMap ?: return
|
|
1225
|
+
map.moveCamera(CameraUpdateFactory.newLatLngZoom(staticCameraTarget(), initialZoom))
|
|
1226
|
+
positionLiveMarkers()
|
|
1227
|
+
return
|
|
1228
|
+
}
|
|
1229
|
+
|
|
771
1230
|
val map = googleMap ?: return
|
|
772
1231
|
val position = LatLng(latitude, longitude)
|
|
773
1232
|
val targetZoom = if (zoom > 0) zoom.toFloat() else map.cameraPosition.zoom
|
|
774
|
-
val
|
|
1233
|
+
val cameraPosition = CameraPosition.Builder(map.cameraPosition)
|
|
1234
|
+
.target(position)
|
|
1235
|
+
.zoom(targetZoom)
|
|
1236
|
+
.build()
|
|
1237
|
+
val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition)
|
|
775
1238
|
when {
|
|
776
1239
|
duration < 0 -> map.animateCamera(cameraUpdate)
|
|
777
1240
|
duration > 0 -> map.animateCamera(cameraUpdate, duration, null)
|
|
@@ -790,23 +1253,29 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
790
1253
|
val map = googleMap ?: return
|
|
791
1254
|
if (coordinates.isEmpty()) return
|
|
792
1255
|
|
|
793
|
-
val
|
|
794
|
-
if (
|
|
795
|
-
|
|
796
|
-
val boundsBuilder = com.google.android.gms.maps.model.LatLngBounds.Builder()
|
|
797
|
-
latLngs.forEach { boundsBuilder.include(it) }
|
|
798
|
-
val bounds = boundsBuilder.build()
|
|
1256
|
+
val latLongs = coordinates.filterIsInstance<LatLng>()
|
|
1257
|
+
if (latLongs.isEmpty()) return
|
|
799
1258
|
|
|
800
1259
|
val top = edgeInsetsTop.toFloat().dpToPx().toInt()
|
|
801
1260
|
val left = edgeInsetsLeft.toFloat().dpToPx().toInt()
|
|
802
1261
|
val bottom = edgeInsetsBottom.toFloat().dpToPx().toInt()
|
|
803
1262
|
val right = edgeInsetsRight.toFloat().dpToPx().toInt()
|
|
804
1263
|
|
|
1264
|
+
if (staticMode) {
|
|
1265
|
+
fitStaticCoordinates(latLongs, top, left, bottom, right)
|
|
1266
|
+
return
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
val boundsBuilder = LatLngBounds.Builder()
|
|
1270
|
+
latLongs.forEach { boundsBuilder.include(it) }
|
|
1271
|
+
val bounds = boundsBuilder.build()
|
|
1272
|
+
|
|
1273
|
+
val combined = combinedEdgeInsets()
|
|
805
1274
|
map.setPadding(
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
1275
|
+
combined.left + left,
|
|
1276
|
+
combined.top + top,
|
|
1277
|
+
combined.right + right,
|
|
1278
|
+
combined.bottom + bottom
|
|
810
1279
|
)
|
|
811
1280
|
|
|
812
1281
|
val cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, 0)
|
|
@@ -817,7 +1286,7 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
817
1286
|
else -> map.moveCamera(cameraUpdate)
|
|
818
1287
|
}
|
|
819
1288
|
|
|
820
|
-
map.setPadding(
|
|
1289
|
+
map.setPadding(combined.left, combined.top, combined.right, combined.bottom)
|
|
821
1290
|
}
|
|
822
1291
|
|
|
823
1292
|
// endregion
|
|
@@ -826,10 +1295,17 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
826
1295
|
|
|
827
1296
|
private fun applyUiSettings() {
|
|
828
1297
|
googleMap?.uiSettings?.apply {
|
|
1298
|
+
if (staticMode) {
|
|
1299
|
+
// The non-lite fallback is a full map; keep it behaving like a
|
|
1300
|
+
// static image (lite mode ignores gestures anyway)
|
|
1301
|
+
setAllGesturesEnabled(false)
|
|
1302
|
+
return
|
|
1303
|
+
}
|
|
829
1304
|
isZoomGesturesEnabled = zoomEnabled
|
|
830
1305
|
isScrollGesturesEnabled = scrollEnabled
|
|
831
1306
|
isRotateGesturesEnabled = rotateEnabled
|
|
832
1307
|
isTiltGesturesEnabled = pitchEnabled
|
|
1308
|
+
isCompassEnabled = compassEnabled
|
|
833
1309
|
isMyLocationButtonEnabled = userLocationButtonEnabled
|
|
834
1310
|
}
|
|
835
1311
|
}
|
|
@@ -841,17 +1317,237 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
841
1317
|
}
|
|
842
1318
|
}
|
|
843
1319
|
|
|
844
|
-
private fun
|
|
845
|
-
|
|
1320
|
+
private fun applyInsetAdjustment() {
|
|
1321
|
+
if (insetAdjustment == "automatic") {
|
|
1322
|
+
attachWindowInsetsListener()
|
|
1323
|
+
} else {
|
|
1324
|
+
detachWindowInsetsListener()
|
|
1325
|
+
systemInsets = EdgeInsets()
|
|
1326
|
+
}
|
|
1327
|
+
applyEdgeInsets()
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
private fun combinedEdgeInsets(): EdgeInsets =
|
|
1331
|
+
if (insetAdjustment == "automatic") {
|
|
1332
|
+
EdgeInsets(
|
|
1333
|
+
edgeInsets.top + systemInsets.top,
|
|
1334
|
+
edgeInsets.left + systemInsets.left,
|
|
1335
|
+
edgeInsets.bottom + systemInsets.bottom,
|
|
1336
|
+
edgeInsets.right + systemInsets.right
|
|
1337
|
+
)
|
|
1338
|
+
} else {
|
|
1339
|
+
edgeInsets
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
private fun applyEdgeInsets(duration: Int = 0) {
|
|
1343
|
+
val combined = combinedEdgeInsets()
|
|
1344
|
+
// Lite mode misrenders padding: the static image is drawn only within
|
|
1345
|
+
// the padded viewport instead of the full view. The camera target shift
|
|
1346
|
+
// (staticCameraTarget) compensates for insets instead.
|
|
1347
|
+
if (!staticMode) {
|
|
1348
|
+
googleMap?.setPadding(combined.left, combined.top, combined.right, combined.bottom)
|
|
1349
|
+
}
|
|
1350
|
+
applyWatermarkTranslation(combined, duration)
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
// Insets are applied by shifting the camera target so the coordinate
|
|
1354
|
+
// centers in the inset viewport like a live map would (see
|
|
1355
|
+
// applyEdgeInsets for why padding can't be used)
|
|
1356
|
+
private fun setStaticEdgeInsets(edgeInsets: EdgeInsets) {
|
|
1357
|
+
val oldInsets = this.edgeInsets
|
|
1358
|
+
this.edgeInsets = edgeInsets
|
|
1359
|
+
applyEdgeInsets()
|
|
1360
|
+
|
|
1361
|
+
val map = googleMap
|
|
1362
|
+
if (map != null && oldInsets != edgeInsets) {
|
|
1363
|
+
map.moveCamera(CameraUpdateFactory.newLatLngZoom(staticCameraTarget(), initialZoom))
|
|
1364
|
+
positionLiveMarkers()
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
private fun staticCameraTarget(): LatLng {
|
|
1369
|
+
val offsetX = (edgeInsets.left - edgeInsets.right) / 2.0
|
|
1370
|
+
val offsetY = (edgeInsets.top - edgeInsets.bottom) / 2.0
|
|
1371
|
+
if (offsetX == 0.0 && offsetY == 0.0) return LatLng(initialLatitude, initialLongitude)
|
|
1372
|
+
|
|
1373
|
+
val worldSize = mercatorWorldSize(initialZoom)
|
|
1374
|
+
return latLngFromMercator(
|
|
1375
|
+
mercatorX(initialLongitude) - offsetX / worldSize,
|
|
1376
|
+
mercatorY(initialLatitude) - offsetY / worldSize
|
|
1377
|
+
)
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
// A static camera can't fit with map padding (lite mode misrenders it);
|
|
1381
|
+
// compute the fitted camera in mercator space instead, shifting the
|
|
1382
|
+
// center for asymmetric padding like edge insets
|
|
1383
|
+
private fun fitStaticCoordinates(
|
|
1384
|
+
coordinates: List<LatLng>,
|
|
1385
|
+
top: Int,
|
|
1386
|
+
left: Int,
|
|
1387
|
+
bottom: Int,
|
|
1388
|
+
right: Int
|
|
1389
|
+
) {
|
|
1390
|
+
val map = googleMap ?: return
|
|
1391
|
+
val wrapper = wrapperView ?: return
|
|
1392
|
+
if (wrapper.width == 0 || wrapper.height == 0) return
|
|
1393
|
+
|
|
1394
|
+
var minX = Double.MAX_VALUE
|
|
1395
|
+
var minY = Double.MAX_VALUE
|
|
1396
|
+
var maxX = -Double.MAX_VALUE
|
|
1397
|
+
var maxY = -Double.MAX_VALUE
|
|
1398
|
+
for (coordinate in coordinates) {
|
|
1399
|
+
val x = mercatorX(coordinate.longitude)
|
|
1400
|
+
val y = mercatorY(coordinate.latitude)
|
|
1401
|
+
minX = minOf(minX, x)
|
|
1402
|
+
maxX = maxOf(maxX, x)
|
|
1403
|
+
minY = minOf(minY, y)
|
|
1404
|
+
maxY = maxOf(maxY, y)
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
val insets = combinedEdgeInsets()
|
|
1408
|
+
val availWidth = (wrapper.width - insets.left - insets.right - left - right).coerceAtLeast(1)
|
|
1409
|
+
val availHeight = (wrapper.height - insets.top - insets.bottom - top - bottom).coerceAtLeast(1)
|
|
1410
|
+
|
|
1411
|
+
// World size in px at which the bounds fit the padded viewport;
|
|
1412
|
+
// infinite for coincident coordinates - keep the current zoom then
|
|
1413
|
+
val fitWorldSize = minOf(availWidth / (maxX - minX), availHeight / (maxY - minY))
|
|
1414
|
+
if (fitWorldSize.isFinite()) {
|
|
1415
|
+
initialZoom = log2(fitWorldSize / 256f.dpToPx())
|
|
1416
|
+
.toFloat()
|
|
1417
|
+
.coerceIn(map.minZoomLevel, map.maxZoomLevel)
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
val worldSize = mercatorWorldSize(initialZoom)
|
|
1421
|
+
val center = latLngFromMercator(
|
|
1422
|
+
(minX + maxX) / 2.0 - (left - right) / 2.0 / worldSize,
|
|
1423
|
+
(minY + maxY) / 2.0 - (top - bottom) / 2.0 / worldSize
|
|
1424
|
+
)
|
|
1425
|
+
initialLatitude = center.latitude
|
|
1426
|
+
initialLongitude = center.longitude
|
|
1427
|
+
|
|
1428
|
+
map.moveCamera(CameraUpdateFactory.newLatLngZoom(staticCameraTarget(), initialZoom))
|
|
1429
|
+
positionLiveMarkers()
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
// Web mercator world space in [0, 1]
|
|
1433
|
+
private fun mercatorX(longitude: Double): Double = (longitude + 180.0) / 360.0
|
|
1434
|
+
|
|
1435
|
+
private fun mercatorY(latitude: Double): Double {
|
|
1436
|
+
val sinLat = sin(Math.toRadians(latitude))
|
|
1437
|
+
return 0.5 - ln((1.0 + sinLat) / (1.0 - sinLat)) / (4.0 * Math.PI)
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
private fun latLngFromMercator(x: Double, y: Double): LatLng =
|
|
1441
|
+
LatLng(Math.toDegrees(atan(sinh(Math.PI * (1.0 - 2.0 * y)))), x * 360.0 - 180.0)
|
|
1442
|
+
|
|
1443
|
+
// World size in px at the given zoom
|
|
1444
|
+
private fun mercatorWorldSize(zoom: Float): Double = 256f.dpToPx().toDouble() * 2.0.pow(zoom.toDouble())
|
|
1445
|
+
|
|
1446
|
+
// Whether the renderer repositions the watermark for setPadding varies by
|
|
1447
|
+
// renderer version and by whether the watermark existed when padding was
|
|
1448
|
+
// set (and lite mode never applies padding). Measure where layout actually
|
|
1449
|
+
// placed it and translate only for the remainder.
|
|
1450
|
+
private fun applyWatermarkTranslation(insets: EdgeInsets, duration: Int = 0) {
|
|
1451
|
+
val view = mapView ?: return
|
|
1452
|
+
attachWatermarkLayoutListener()
|
|
1453
|
+
|
|
1454
|
+
if (view.height == 0) return
|
|
1455
|
+
val watermark = view.findViewByTag("GoogleWatermark") ?: return
|
|
1456
|
+
|
|
1457
|
+
// Layout position relative to the map view, excluding translation
|
|
1458
|
+
var left = 0
|
|
1459
|
+
var top = 0
|
|
1460
|
+
var current: View? = watermark
|
|
1461
|
+
while (current != null && current !== view) {
|
|
1462
|
+
left += current.left
|
|
1463
|
+
top += current.top
|
|
1464
|
+
current = current.parent as? View
|
|
1465
|
+
}
|
|
1466
|
+
if (current == null) return
|
|
1467
|
+
|
|
1468
|
+
val bottomGap = view.height - (top + watermark.height)
|
|
1469
|
+
val targetY = if (bottomGap >= insets.bottom) 0f else -insets.bottom.toFloat()
|
|
1470
|
+
val targetX = if (left >= insets.left) 0f else insets.left.toFloat()
|
|
1471
|
+
|
|
1472
|
+
// Skip when already applied (or animating) toward this target so layout
|
|
1473
|
+
// passes don't snap an in-flight animation
|
|
1474
|
+
if (watermark === watermarkView && targetX == watermarkTargetX && targetY == watermarkTargetY) {
|
|
1475
|
+
return
|
|
1476
|
+
}
|
|
1477
|
+
watermarkView = watermark
|
|
1478
|
+
watermarkTargetX = targetX
|
|
1479
|
+
watermarkTargetY = targetY
|
|
1480
|
+
|
|
1481
|
+
if (duration > 0) {
|
|
1482
|
+
watermark.animate()
|
|
1483
|
+
.translationY(targetY)
|
|
1484
|
+
.translationX(targetX)
|
|
1485
|
+
.setDuration(duration.toLong())
|
|
1486
|
+
.start()
|
|
1487
|
+
} else if (duration < 0) {
|
|
1488
|
+
watermark.animate()
|
|
1489
|
+
.translationY(targetY)
|
|
1490
|
+
.translationX(targetX)
|
|
1491
|
+
.start()
|
|
1492
|
+
} else {
|
|
1493
|
+
watermark.translationY = targetY
|
|
1494
|
+
watermark.translationX = targetX
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
private fun attachWatermarkLayoutListener() {
|
|
1499
|
+
if (watermarkLayoutListener != null) return
|
|
1500
|
+
val view = mapView ?: return
|
|
1501
|
+
|
|
1502
|
+
val listener = ViewTreeObserver.OnGlobalLayoutListener {
|
|
1503
|
+
applyWatermarkTranslation(combinedEdgeInsets())
|
|
1504
|
+
}
|
|
1505
|
+
watermarkLayoutListener = listener
|
|
1506
|
+
view.viewTreeObserver.addOnGlobalLayoutListener(listener)
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
private fun detachWatermarkLayoutListener() {
|
|
1510
|
+
watermarkView = null
|
|
1511
|
+
val listener = watermarkLayoutListener ?: return
|
|
1512
|
+
watermarkLayoutListener = null
|
|
1513
|
+
mapView?.viewTreeObserver?.takeIf { it.isAlive }?.removeOnGlobalLayoutListener(listener)
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
private fun attachWindowInsetsListener() {
|
|
1517
|
+
val view = mapView ?: return
|
|
1518
|
+
ViewCompat.setOnApplyWindowInsetsListener(view) { _, windowInsets ->
|
|
1519
|
+
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
|
1520
|
+
systemInsets = EdgeInsets(insets.top, insets.left, insets.bottom, insets.right)
|
|
1521
|
+
applyEdgeInsets()
|
|
1522
|
+
windowInsets
|
|
1523
|
+
}
|
|
1524
|
+
ViewCompat.requestApplyInsets(view)
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
private fun detachWindowInsetsListener() {
|
|
1528
|
+
mapView?.let { ViewCompat.setOnApplyWindowInsetsListener(it, null) }
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
override fun onConfigurationChanged(newConfig: Configuration) {
|
|
1532
|
+
val newNightMode = newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
|
|
1533
|
+
if (newNightMode != currentNightMode) {
|
|
1534
|
+
currentNightMode = newNightMode
|
|
1535
|
+
applyTheme()
|
|
1536
|
+
}
|
|
846
1537
|
}
|
|
847
1538
|
|
|
1539
|
+
@Deprecated("Deprecated in Java")
|
|
1540
|
+
override fun onLowMemory() {}
|
|
1541
|
+
|
|
1542
|
+
override fun onTrimMemory(level: Int) {}
|
|
1543
|
+
|
|
848
1544
|
private fun applyTheme() {
|
|
849
1545
|
val colorScheme = when (theme) {
|
|
850
1546
|
"dark" -> MapColorScheme.DARK
|
|
851
1547
|
"light" -> MapColorScheme.LIGHT
|
|
852
1548
|
else -> MapColorScheme.FOLLOW_SYSTEM
|
|
853
1549
|
}
|
|
854
|
-
googleMap?.
|
|
1550
|
+
googleMap?.mapColorScheme = colorScheme
|
|
855
1551
|
}
|
|
856
1552
|
|
|
857
1553
|
@SuppressLint("MissingPermission")
|
|
@@ -870,5 +1566,9 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
870
1566
|
|
|
871
1567
|
companion object {
|
|
872
1568
|
const val DEMO_MAP_ID = "DEMO_MAP_ID"
|
|
1569
|
+
|
|
1570
|
+
// Undocumented cap on the lite mode bitmap (2048 minus a 2px border,
|
|
1571
|
+
// measured empirically)
|
|
1572
|
+
private const val LITE_MODE_MAX_SIZE = 2046
|
|
873
1573
|
}
|
|
874
1574
|
}
|