@lugg/maps 1.0.0-beta.0 → 1.0.0-beta.2
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 +1 -1
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/luggmaps/LuggCalloutView.kt +74 -0
- package/android/src/main/java/com/luggmaps/LuggCalloutViewManager.kt +38 -0
- package/android/src/main/java/com/luggmaps/LuggMapWrapperView.kt +0 -11
- package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +73 -51
- package/android/src/main/java/com/luggmaps/LuggPackage.kt +8 -1
- package/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt +181 -5
- package/ios/LuggCalloutView.h +23 -0
- package/ios/LuggCalloutView.mm +145 -0
- package/ios/LuggMarkerView.h +2 -0
- package/ios/LuggMarkerView.mm +17 -2
- package/ios/core/AppleMapProvider.mm +211 -24
- package/ios/core/GoogleMapProvider.mm +125 -5
- package/ios/core/LuggAnnotationView.h +12 -0
- package/ios/core/LuggAnnotationView.mm +55 -0
- package/lib/module/MapProvider.web.js +3 -1
- package/lib/module/MapProvider.web.js.map +1 -1
- package/lib/module/MapView.web.js +15 -2
- package/lib/module/MapView.web.js.map +1 -1
- package/lib/module/components/Marker.js +21 -4
- package/lib/module/components/Marker.js.map +1 -1
- package/lib/module/components/Marker.web.js +86 -22
- package/lib/module/components/Marker.web.js.map +1 -1
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/fabric/LuggCalloutViewNativeComponent.ts +20 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/MapProvider.web.d.ts +6 -0
- package/lib/typescript/src/MapProvider.web.d.ts.map +1 -1
- package/lib/typescript/src/MapView.web.d.ts.map +1 -1
- package/lib/typescript/src/components/GeoJson.types.d.ts +11 -1
- package/lib/typescript/src/components/GeoJson.types.d.ts.map +1 -1
- package/lib/typescript/src/components/Marker.d.ts +1 -1
- package/lib/typescript/src/components/Marker.d.ts.map +1 -1
- package/lib/typescript/src/components/Marker.types.d.ts +27 -1
- package/lib/typescript/src/components/Marker.types.d.ts.map +1 -1
- package/lib/typescript/src/components/Marker.web.d.ts +1 -1
- package/lib/typescript/src/components/Marker.web.d.ts.map +1 -1
- package/lib/typescript/src/components/index.d.ts +1 -1
- package/lib/typescript/src/components/index.d.ts.map +1 -1
- package/lib/typescript/src/components/index.web.d.ts +1 -1
- package/lib/typescript/src/components/index.web.d.ts.map +1 -1
- package/lib/typescript/src/fabric/LuggCalloutViewNativeComponent.d.ts +13 -0
- package/lib/typescript/src/fabric/LuggCalloutViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +2 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/index.web.d.ts +2 -2
- package/lib/typescript/src/index.web.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/MapProvider.web.tsx +11 -1
- package/src/MapView.web.tsx +26 -2
- package/src/components/GeoJson.types.ts +12 -1
- package/src/components/Marker.tsx +29 -1
- package/src/components/Marker.types.ts +28 -1
- package/src/components/Marker.web.tsx +113 -23
- package/src/components/index.ts +1 -0
- package/src/components/index.web.ts +1 -1
- package/src/fabric/LuggCalloutViewNativeComponent.ts +20 -0
- package/src/index.ts +2 -0
- package/src/index.web.ts +2 -1
package/README.md
CHANGED
|
@@ -113,7 +113,7 @@ import { MapView, Marker, Polyline, Polygon } from '@lugg/maps';
|
|
|
113
113
|
## Components
|
|
114
114
|
|
|
115
115
|
- [MapView](docs/MAPVIEW.md) - Main map component
|
|
116
|
-
- [Marker](docs/MARKER.md) - Map markers
|
|
116
|
+
- [Marker](docs/MARKER.md) - Map markers with callout support
|
|
117
117
|
- [Polyline](docs/POLYLINE.md) - Draw lines on the map
|
|
118
118
|
- [Polygon](docs/POLYGON.md) - Draw filled shapes on the map
|
|
119
119
|
- [GeoJson](docs/GEOJSON.md) - Render GeoJSON data on the map
|
package/android/build.gradle
CHANGED
|
@@ -74,5 +74,5 @@ def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
|
74
74
|
dependencies {
|
|
75
75
|
implementation "com.facebook.react:react-android"
|
|
76
76
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
77
|
-
implementation "com.google.android.gms:play-services-maps:
|
|
77
|
+
implementation "com.google.android.gms:play-services-maps:20.0.0"
|
|
78
78
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
package com.luggmaps
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Bitmap
|
|
5
|
+
import android.graphics.Canvas
|
|
6
|
+
import android.view.View
|
|
7
|
+
import androidx.core.graphics.createBitmap
|
|
8
|
+
import androidx.core.view.isNotEmpty
|
|
9
|
+
import com.facebook.react.views.view.ReactViewGroup
|
|
10
|
+
|
|
11
|
+
class LuggCalloutView(context: Context) : ReactViewGroup(context) {
|
|
12
|
+
val contentView: ReactViewGroup = ReactViewGroup(context)
|
|
13
|
+
var bubbled: Boolean = true
|
|
14
|
+
var anchorX: Float = 0.5f
|
|
15
|
+
set(value) {
|
|
16
|
+
field = value
|
|
17
|
+
onUpdate?.invoke()
|
|
18
|
+
}
|
|
19
|
+
var anchorY: Float = 1.0f
|
|
20
|
+
set(value) {
|
|
21
|
+
field = value
|
|
22
|
+
onUpdate?.invoke()
|
|
23
|
+
}
|
|
24
|
+
var onUpdate: (() -> Unit)? = null
|
|
25
|
+
|
|
26
|
+
val hasCustomContent: Boolean
|
|
27
|
+
get() = contentView.isNotEmpty()
|
|
28
|
+
|
|
29
|
+
init {
|
|
30
|
+
visibility = GONE
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
override fun addView(child: View, index: Int) {
|
|
34
|
+
contentView.addView(child, index)
|
|
35
|
+
child.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
|
|
36
|
+
onUpdate?.invoke()
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
override fun removeView(child: View) {
|
|
41
|
+
contentView.removeView(child)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override fun removeViewAt(index: Int) {
|
|
45
|
+
contentView.removeViewAt(index)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
override fun getChildCount(): Int = contentView.childCount
|
|
49
|
+
|
|
50
|
+
override fun getChildAt(index: Int): View? = contentView.getChildAt(index)
|
|
51
|
+
|
|
52
|
+
fun createContentBitmap(): Bitmap? {
|
|
53
|
+
var maxWidth = 0
|
|
54
|
+
var maxHeight = 0
|
|
55
|
+
for (i in 0 until contentView.childCount) {
|
|
56
|
+
val child = contentView.getChildAt(i)
|
|
57
|
+
val childRight = child.left + child.width
|
|
58
|
+
val childBottom = child.top + child.height
|
|
59
|
+
if (childRight > maxWidth) maxWidth = childRight
|
|
60
|
+
if (childBottom > maxHeight) maxHeight = childBottom
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (maxWidth <= 0 || maxHeight <= 0) return null
|
|
64
|
+
|
|
65
|
+
val bitmap = createBitmap(maxWidth, maxHeight)
|
|
66
|
+
val canvas = Canvas(bitmap)
|
|
67
|
+
contentView.draw(canvas)
|
|
68
|
+
return bitmap
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
fun onDropViewInstance() {
|
|
72
|
+
contentView.removeAllViews()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
package com.luggmaps
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReadableMap
|
|
4
|
+
import com.facebook.react.module.annotations.ReactModule
|
|
5
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
6
|
+
import com.facebook.react.uimanager.ViewGroupManager
|
|
7
|
+
import com.facebook.react.uimanager.ViewManagerDelegate
|
|
8
|
+
import com.facebook.react.viewmanagers.LuggCalloutViewManagerDelegate
|
|
9
|
+
import com.facebook.react.viewmanagers.LuggCalloutViewManagerInterface
|
|
10
|
+
|
|
11
|
+
@ReactModule(name = LuggCalloutViewManager.NAME)
|
|
12
|
+
class LuggCalloutViewManager :
|
|
13
|
+
ViewGroupManager<LuggCalloutView>(),
|
|
14
|
+
LuggCalloutViewManagerInterface<LuggCalloutView> {
|
|
15
|
+
private val delegate: ViewManagerDelegate<LuggCalloutView> = LuggCalloutViewManagerDelegate(this)
|
|
16
|
+
|
|
17
|
+
override fun getDelegate(): ViewManagerDelegate<LuggCalloutView> = delegate
|
|
18
|
+
override fun getName(): String = NAME
|
|
19
|
+
override fun createViewInstance(context: ThemedReactContext): LuggCalloutView = LuggCalloutView(context)
|
|
20
|
+
|
|
21
|
+
override fun setBubbled(view: LuggCalloutView, value: Boolean) {
|
|
22
|
+
view.bubbled = value
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
override fun setAnchor(view: LuggCalloutView, value: ReadableMap?) {
|
|
26
|
+
view.anchorX = value?.getDouble("x")?.toFloat() ?: 0.5f
|
|
27
|
+
view.anchorY = value?.getDouble("y")?.toFloat() ?: 1.0f
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
override fun onDropViewInstance(view: LuggCalloutView) {
|
|
31
|
+
super.onDropViewInstance(view)
|
|
32
|
+
view.onDropViewInstance()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
companion object {
|
|
36
|
+
const val NAME = "LuggCalloutView"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -15,17 +15,6 @@ class LuggMapWrapperView(context: ThemedReactContext) : ReactViewGroup(context)
|
|
|
15
15
|
return super.dispatchTouchEvent(event)
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
override fun requestLayout() {
|
|
19
|
-
super.requestLayout()
|
|
20
|
-
getChildAt(0)?.let {
|
|
21
|
-
it.measure(
|
|
22
|
-
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
23
|
-
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
|
24
|
-
)
|
|
25
|
-
it.layout(0, 0, width, height)
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
18
|
override fun onLayout(
|
|
30
19
|
changed: Boolean,
|
|
31
20
|
left: Int,
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
package com.luggmaps
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
-
import android.graphics.Bitmap
|
|
5
4
|
import android.graphics.Canvas
|
|
6
5
|
import android.view.View
|
|
7
|
-
import android.view.ViewGroup
|
|
8
6
|
import androidx.core.graphics.createBitmap
|
|
9
7
|
import androidx.core.view.isNotEmpty
|
|
10
8
|
import com.facebook.react.views.view.ReactViewGroup
|
|
@@ -68,15 +66,20 @@ class LuggMarkerView(context: Context) : ReactViewGroup(context) {
|
|
|
68
66
|
private set
|
|
69
67
|
|
|
70
68
|
val hasCustomView: Boolean
|
|
71
|
-
get() =
|
|
69
|
+
get() = contentView.isNotEmpty()
|
|
72
70
|
|
|
73
|
-
val
|
|
71
|
+
val contentView: ReactViewGroup = ReactViewGroup(context)
|
|
74
72
|
|
|
75
|
-
|
|
73
|
+
var onUpdate: (() -> Unit)? = null
|
|
74
|
+
|
|
75
|
+
var calloutView: LuggCalloutView? = null
|
|
76
|
+
private set
|
|
77
|
+
|
|
78
|
+
private fun measureContentBounds(): Pair<Int, Int> {
|
|
76
79
|
var maxWidth = 0
|
|
77
80
|
var maxHeight = 0
|
|
78
|
-
for (i in 0 until
|
|
79
|
-
val child =
|
|
81
|
+
for (i in 0 until contentView.childCount) {
|
|
82
|
+
val child = contentView.getChildAt(i)
|
|
80
83
|
val childRight = child.left + child.width
|
|
81
84
|
val childBottom = child.top + child.height
|
|
82
85
|
if (childRight > maxWidth) maxWidth = childRight
|
|
@@ -85,8 +88,8 @@ class LuggMarkerView(context: Context) : ReactViewGroup(context) {
|
|
|
85
88
|
return Pair(maxWidth, maxHeight)
|
|
86
89
|
}
|
|
87
90
|
|
|
88
|
-
private fun
|
|
89
|
-
val (width, height) =
|
|
91
|
+
private fun createContentBitmap(): BitmapDescriptor? {
|
|
92
|
+
val (width, height) = measureContentBounds()
|
|
90
93
|
if (width <= 0 || height <= 0) return null
|
|
91
94
|
|
|
92
95
|
val scaledWidth = (width * scale).toInt()
|
|
@@ -95,41 +98,31 @@ class LuggMarkerView(context: Context) : ReactViewGroup(context) {
|
|
|
95
98
|
val bitmap = createBitmap(scaledWidth, scaledHeight)
|
|
96
99
|
val canvas = Canvas(bitmap)
|
|
97
100
|
canvas.scale(scale, scale)
|
|
98
|
-
|
|
101
|
+
contentView.draw(canvas)
|
|
99
102
|
return BitmapDescriptorFactory.fromBitmap(bitmap)
|
|
100
103
|
}
|
|
101
104
|
|
|
102
|
-
|
|
103
|
-
val (width, height) =
|
|
105
|
+
fun layoutContentView() {
|
|
106
|
+
val (width, height) = measureContentBounds()
|
|
104
107
|
val scaledWidth = (width * scale).toInt()
|
|
105
108
|
val scaledHeight = (height * scale).toInt()
|
|
106
109
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
iconView.pivotY = 0f
|
|
110
|
+
contentView.scaleX = scale
|
|
111
|
+
contentView.scaleY = scale
|
|
112
|
+
contentView.pivotX = 0f
|
|
113
|
+
contentView.pivotY = 0f
|
|
112
114
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
119
|
-
setMeasuredDimension(scaledWidth, scaledHeight)
|
|
120
|
-
}
|
|
121
|
-
}
|
|
115
|
+
contentView.measure(
|
|
116
|
+
View.MeasureSpec.makeMeasureSpec(scaledWidth, View.MeasureSpec.EXACTLY),
|
|
117
|
+
View.MeasureSpec.makeMeasureSpec(scaledHeight, View.MeasureSpec.EXACTLY)
|
|
118
|
+
)
|
|
119
|
+
contentView.layout(0, 0, scaledWidth, scaledHeight)
|
|
122
120
|
}
|
|
123
121
|
|
|
124
122
|
fun applyIconToMarker() {
|
|
125
123
|
val m = marker ?: return
|
|
126
124
|
if (!hasCustomView) return
|
|
127
|
-
|
|
128
|
-
if (rasterize) {
|
|
129
|
-
createIconBitmap()?.let { m.setIcon(it) }
|
|
130
|
-
} else {
|
|
131
|
-
m.iconView = createIconViewWrapper()
|
|
132
|
-
}
|
|
125
|
+
createContentBitmap()?.let { m.setIcon(it) }
|
|
133
126
|
}
|
|
134
127
|
|
|
135
128
|
fun applyScaleToMarker() {
|
|
@@ -139,9 +132,10 @@ class LuggMarkerView(context: Context) : ReactViewGroup(context) {
|
|
|
139
132
|
scaleUpdateRunnable?.let { removeCallbacks(it) }
|
|
140
133
|
scaleUpdateRunnable = Runnable {
|
|
141
134
|
if (rasterize) {
|
|
142
|
-
|
|
135
|
+
createContentBitmap()?.let { m.setIcon(it) }
|
|
143
136
|
} else {
|
|
144
|
-
|
|
137
|
+
layoutContentView()
|
|
138
|
+
onUpdate?.invoke()
|
|
145
139
|
}
|
|
146
140
|
}
|
|
147
141
|
post(scaleUpdateRunnable)
|
|
@@ -149,18 +143,15 @@ class LuggMarkerView(context: Context) : ReactViewGroup(context) {
|
|
|
149
143
|
|
|
150
144
|
fun updateIcon(onAddMarker: () -> Unit) {
|
|
151
145
|
if (!hasCustomView) return
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
146
|
+
post {
|
|
147
|
+
if (marker == null) {
|
|
148
|
+
onAddMarker()
|
|
149
|
+
} else if (rasterize) {
|
|
150
|
+
applyIconToMarker()
|
|
151
|
+
} else {
|
|
152
|
+
layoutContentView()
|
|
153
|
+
onUpdate?.invoke()
|
|
159
154
|
}
|
|
160
|
-
} else {
|
|
161
|
-
marker?.remove()
|
|
162
|
-
marker = null
|
|
163
|
-
post { onAddMarker() }
|
|
164
155
|
}
|
|
165
156
|
}
|
|
166
157
|
|
|
@@ -169,23 +160,52 @@ class LuggMarkerView(context: Context) : ReactViewGroup(context) {
|
|
|
169
160
|
}
|
|
170
161
|
|
|
171
162
|
override fun addView(child: View, index: Int) {
|
|
172
|
-
|
|
163
|
+
if (child is LuggCalloutView) {
|
|
164
|
+
calloutView = child
|
|
165
|
+
} else {
|
|
166
|
+
contentView.addView(child, index)
|
|
167
|
+
}
|
|
173
168
|
didLayout = false
|
|
174
169
|
}
|
|
175
170
|
|
|
176
171
|
override fun removeView(child: View) {
|
|
177
|
-
|
|
172
|
+
if (child is LuggCalloutView) {
|
|
173
|
+
calloutView = null
|
|
174
|
+
} else {
|
|
175
|
+
contentView.removeView(child)
|
|
176
|
+
}
|
|
178
177
|
didLayout = false
|
|
179
178
|
}
|
|
180
179
|
|
|
181
180
|
override fun removeViewAt(index: Int) {
|
|
182
|
-
|
|
181
|
+
val child = getChildAt(index)
|
|
182
|
+
if (child is LuggCalloutView) {
|
|
183
|
+
calloutView = null
|
|
184
|
+
} else {
|
|
185
|
+
contentView.removeViewAt(index)
|
|
186
|
+
}
|
|
183
187
|
didLayout = false
|
|
184
188
|
}
|
|
185
189
|
|
|
186
|
-
override fun
|
|
190
|
+
override fun removeViews(start: Int, count: Int) {
|
|
191
|
+
for (i in (start until start + count).reversed()) {
|
|
192
|
+
val child = getChildAt(i)
|
|
193
|
+
if (child is LuggCalloutView) {
|
|
194
|
+
calloutView = null
|
|
195
|
+
} else if (i < contentView.childCount) {
|
|
196
|
+
contentView.removeViewAt(i)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
didLayout = false
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
override fun getChildCount(): Int = contentView.childCount + if (calloutView != null) 1 else 0
|
|
187
203
|
|
|
188
|
-
override fun getChildAt(index: Int): View?
|
|
204
|
+
override fun getChildAt(index: Int): View? {
|
|
205
|
+
if (index < contentView.childCount) return contentView.getChildAt(index)
|
|
206
|
+
if (index == contentView.childCount && calloutView != null) return calloutView
|
|
207
|
+
return null
|
|
208
|
+
}
|
|
189
209
|
|
|
190
210
|
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
|
191
211
|
super.onSizeChanged(w, h, oldw, oldh)
|
|
@@ -277,8 +297,10 @@ class LuggMarkerView(context: Context) : ReactViewGroup(context) {
|
|
|
277
297
|
fun onDropViewInstance() {
|
|
278
298
|
scaleUpdateRunnable?.let { removeCallbacks(it) }
|
|
279
299
|
scaleUpdateRunnable = null
|
|
300
|
+
onUpdate = null
|
|
280
301
|
didLayout = false
|
|
302
|
+
calloutView = null
|
|
281
303
|
delegate = null
|
|
282
|
-
|
|
304
|
+
contentView.removeAllViews()
|
|
283
305
|
}
|
|
284
306
|
}
|
|
@@ -6,5 +6,12 @@ import com.facebook.react.uimanager.ViewManager
|
|
|
6
6
|
|
|
7
7
|
class LuggPackage : ReactPackage {
|
|
8
8
|
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> =
|
|
9
|
-
listOf(
|
|
9
|
+
listOf(
|
|
10
|
+
LuggMapViewManager(),
|
|
11
|
+
LuggMarkerViewManager(),
|
|
12
|
+
LuggCalloutViewManager(),
|
|
13
|
+
LuggMapWrapperViewManager(),
|
|
14
|
+
LuggPolylineViewManager(),
|
|
15
|
+
LuggPolygonViewManager()
|
|
16
|
+
)
|
|
10
17
|
}
|
|
@@ -3,6 +3,8 @@ package com.luggmaps.core
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
5
|
import android.view.View
|
|
6
|
+
import android.widget.ImageView
|
|
7
|
+
import androidx.core.graphics.createBitmap
|
|
6
8
|
import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
7
9
|
import com.google.android.gms.maps.CameraUpdateFactory
|
|
8
10
|
import com.google.android.gms.maps.GoogleMap
|
|
@@ -11,12 +13,14 @@ import com.google.android.gms.maps.MapView
|
|
|
11
13
|
import com.google.android.gms.maps.OnMapReadyCallback
|
|
12
14
|
import com.google.android.gms.maps.model.AdvancedMarker
|
|
13
15
|
import com.google.android.gms.maps.model.AdvancedMarkerOptions
|
|
16
|
+
import com.google.android.gms.maps.model.BitmapDescriptorFactory
|
|
14
17
|
import com.google.android.gms.maps.model.LatLng
|
|
15
18
|
import com.google.android.gms.maps.model.MapColorScheme
|
|
16
19
|
import com.google.android.gms.maps.model.Marker
|
|
17
20
|
import com.google.android.gms.maps.model.Polygon
|
|
18
21
|
import com.google.android.gms.maps.model.PolygonOptions
|
|
19
22
|
import com.google.android.gms.maps.model.PolylineOptions
|
|
23
|
+
import com.luggmaps.LuggCalloutView
|
|
20
24
|
import com.luggmaps.LuggMapWrapperView
|
|
21
25
|
import com.luggmaps.LuggMarkerView
|
|
22
26
|
import com.luggmaps.LuggMarkerViewDelegate
|
|
@@ -38,7 +42,8 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
38
42
|
GoogleMap.OnMapLongClickListener,
|
|
39
43
|
GoogleMap.OnPolygonClickListener,
|
|
40
44
|
GoogleMap.OnMarkerClickListener,
|
|
41
|
-
GoogleMap.OnMarkerDragListener
|
|
45
|
+
GoogleMap.OnMarkerDragListener,
|
|
46
|
+
GoogleMap.InfoWindowAdapter {
|
|
42
47
|
|
|
43
48
|
override var delegate: MapProviderDelegate? = null
|
|
44
49
|
override val isMapReady: Boolean get() = _isMapReady
|
|
@@ -56,6 +61,8 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
56
61
|
private val polylineAnimators = mutableMapOf<LuggPolylineView, PolylineAnimator>()
|
|
57
62
|
private val polygonToViewMap = mutableMapOf<Polygon, LuggPolygonView>()
|
|
58
63
|
private val markerToViewMap = mutableMapOf<Marker, LuggMarkerView>()
|
|
64
|
+
private val liveMarkerViews = mutableSetOf<LuggMarkerView>()
|
|
65
|
+
private var activeNonBubbledMarker: Marker? = null
|
|
59
66
|
private var tapLocation: LatLng? = null
|
|
60
67
|
|
|
61
68
|
// Initial camera settings
|
|
@@ -103,6 +110,11 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
override fun destroy() {
|
|
113
|
+
dismissNonBubbledCallout()
|
|
114
|
+
for (markerView in liveMarkerViews) {
|
|
115
|
+
markerView.onUpdate = null
|
|
116
|
+
}
|
|
117
|
+
liveMarkerViews.clear()
|
|
106
118
|
pendingMarkerViews.clear()
|
|
107
119
|
pendingPolylineViews.clear()
|
|
108
120
|
pendingPolygonViews.clear()
|
|
@@ -120,6 +132,7 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
120
132
|
googleMap?.setOnPolygonClickListener(null)
|
|
121
133
|
googleMap?.setOnMarkerClickListener(null)
|
|
122
134
|
googleMap?.setOnMarkerDragListener(null)
|
|
135
|
+
googleMap?.setInfoWindowAdapter(null)
|
|
123
136
|
googleMap?.clear()
|
|
124
137
|
googleMap = null
|
|
125
138
|
_isMapReady = false
|
|
@@ -143,6 +156,7 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
143
156
|
map.setOnPolygonClickListener(this)
|
|
144
157
|
map.setOnMarkerClickListener(this)
|
|
145
158
|
map.setOnMarkerDragListener(this)
|
|
159
|
+
map.setInfoWindowAdapter(this)
|
|
146
160
|
|
|
147
161
|
wrapperView?.touchEventHandler = { event ->
|
|
148
162
|
if (event.action == android.view.MotionEvent.ACTION_DOWN) {
|
|
@@ -177,6 +191,8 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
177
191
|
val map = googleMap ?: return
|
|
178
192
|
val position = map.cameraPosition
|
|
179
193
|
delegate?.mapProviderDidMoveCamera(position.target.latitude, position.target.longitude, position.zoom, isDragging)
|
|
194
|
+
positionLiveMarkers()
|
|
195
|
+
positionNonBubbledCallout()
|
|
180
196
|
}
|
|
181
197
|
|
|
182
198
|
override fun onCameraIdle() {
|
|
@@ -190,6 +206,7 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
190
206
|
}
|
|
191
207
|
|
|
192
208
|
override fun onMapClick(latLng: LatLng) {
|
|
209
|
+
dismissNonBubbledCallout()
|
|
193
210
|
val map = googleMap ?: return
|
|
194
211
|
val point = map.projection.toScreenLocation(latLng)
|
|
195
212
|
delegate?.mapProviderDidPress(latLng.latitude, latLng.longitude, point.x.toFloat(), point.y.toFloat())
|
|
@@ -211,9 +228,18 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
211
228
|
}
|
|
212
229
|
|
|
213
230
|
override fun onMarkerClick(marker: Marker): Boolean {
|
|
231
|
+
dismissNonBubbledCallout()
|
|
232
|
+
|
|
214
233
|
markerToViewMap[marker]?.let { view ->
|
|
215
234
|
val point = googleMap?.projection?.toScreenLocation(marker.position)
|
|
216
235
|
view.emitPressEvent(point?.x?.toFloat() ?: 0f, point?.y?.toFloat() ?: 0f)
|
|
236
|
+
|
|
237
|
+
val calloutView = view.calloutView
|
|
238
|
+
if (calloutView != null && !calloutView.bubbled && calloutView.hasCustomContent) {
|
|
239
|
+
googleMap?.animateCamera(CameraUpdateFactory.newLatLng(marker.position))
|
|
240
|
+
showNonBubbledCallout(marker, calloutView)
|
|
241
|
+
return true
|
|
242
|
+
}
|
|
217
243
|
}
|
|
218
244
|
return false
|
|
219
245
|
}
|
|
@@ -224,6 +250,7 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
224
250
|
view.setCoordinate(marker.position.latitude, marker.position.longitude)
|
|
225
251
|
val point = googleMap?.projection?.toScreenLocation(marker.position)
|
|
226
252
|
view.emitDragStartEvent(point?.x?.toFloat() ?: 0f, point?.y?.toFloat() ?: 0f)
|
|
253
|
+
if (!view.rasterize) positionLiveMarker(view)
|
|
227
254
|
}
|
|
228
255
|
}
|
|
229
256
|
|
|
@@ -232,6 +259,7 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
232
259
|
view.setCoordinate(marker.position.latitude, marker.position.longitude)
|
|
233
260
|
val point = googleMap?.projection?.toScreenLocation(marker.position)
|
|
234
261
|
view.emitDragChangeEvent(point?.x?.toFloat() ?: 0f, point?.y?.toFloat() ?: 0f)
|
|
262
|
+
if (!view.rasterize) positionLiveMarker(view)
|
|
235
263
|
}
|
|
236
264
|
}
|
|
237
265
|
|
|
@@ -241,7 +269,92 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
241
269
|
view.setCoordinate(marker.position.latitude, marker.position.longitude)
|
|
242
270
|
val point = googleMap?.projection?.toScreenLocation(marker.position)
|
|
243
271
|
view.emitDragEndEvent(point?.x?.toFloat() ?: 0f, point?.y?.toFloat() ?: 0f)
|
|
272
|
+
if (!view.rasterize) positionLiveMarker(view)
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
override fun getInfoWindow(marker: Marker): View? {
|
|
277
|
+
// Non-bubbled callouts are rendered as live views, not info windows
|
|
278
|
+
return null
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
override fun getInfoContents(marker: Marker): View? {
|
|
282
|
+
val markerView = markerToViewMap[marker] ?: return null
|
|
283
|
+
val calloutView = markerView.calloutView ?: return null
|
|
284
|
+
if (!calloutView.hasCustomContent || !calloutView.bubbled) return null
|
|
285
|
+
|
|
286
|
+
val bitmap = calloutView.createContentBitmap() ?: return null
|
|
287
|
+
return ImageView(context).apply { setImageBitmap(bitmap) }
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private fun showNonBubbledCallout(marker: Marker, calloutView: LuggCalloutView) {
|
|
291
|
+
val wrapper = wrapperView ?: return
|
|
292
|
+
val contentView = calloutView.contentView
|
|
293
|
+
|
|
294
|
+
calloutView.onUpdate = {
|
|
295
|
+
layoutNonBubbledCallout()
|
|
296
|
+
positionNonBubbledCallout()
|
|
244
297
|
}
|
|
298
|
+
|
|
299
|
+
dismissInfoWindows()
|
|
300
|
+
wrapper.addView(contentView)
|
|
301
|
+
activeNonBubbledMarker = marker
|
|
302
|
+
layoutNonBubbledCallout()
|
|
303
|
+
positionNonBubbledCallout()
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private fun dismissInfoWindows() {
|
|
307
|
+
for ((marker, _) in markerToViewMap) {
|
|
308
|
+
if (marker.isInfoWindowShown) {
|
|
309
|
+
marker.hideInfoWindow()
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private fun dismissNonBubbledCallout() {
|
|
315
|
+
val marker = activeNonBubbledMarker ?: return
|
|
316
|
+
val markerView = markerToViewMap[marker] ?: return
|
|
317
|
+
val calloutView = markerView.calloutView ?: return
|
|
318
|
+
val contentView = calloutView.contentView
|
|
319
|
+
|
|
320
|
+
calloutView.onUpdate = null
|
|
321
|
+
(contentView.parent as? android.view.ViewGroup)?.removeView(contentView)
|
|
322
|
+
activeNonBubbledMarker = null
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private fun layoutNonBubbledCallout() {
|
|
326
|
+
val marker = activeNonBubbledMarker ?: return
|
|
327
|
+
val markerView = markerToViewMap[marker] ?: return
|
|
328
|
+
val calloutView = markerView.calloutView ?: return
|
|
329
|
+
val contentView = calloutView.contentView
|
|
330
|
+
|
|
331
|
+
var contentWidth = 0
|
|
332
|
+
var contentHeight = 0
|
|
333
|
+
for (i in 0 until contentView.childCount) {
|
|
334
|
+
val child = contentView.getChildAt(i)
|
|
335
|
+
val childRight = child.left + child.width
|
|
336
|
+
val childBottom = child.top + child.height
|
|
337
|
+
if (childRight > contentWidth) contentWidth = childRight
|
|
338
|
+
if (childBottom > contentHeight) contentHeight = childBottom
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
contentView.measure(
|
|
342
|
+
View.MeasureSpec.makeMeasureSpec(contentWidth, View.MeasureSpec.EXACTLY),
|
|
343
|
+
View.MeasureSpec.makeMeasureSpec(contentHeight, View.MeasureSpec.EXACTLY)
|
|
344
|
+
)
|
|
345
|
+
contentView.layout(0, 0, contentWidth, contentHeight)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
private fun positionNonBubbledCallout() {
|
|
349
|
+
val marker = activeNonBubbledMarker ?: return
|
|
350
|
+
val markerView = markerToViewMap[marker] ?: return
|
|
351
|
+
val calloutView = markerView.calloutView ?: return
|
|
352
|
+
val contentView = calloutView.contentView
|
|
353
|
+
val map = googleMap ?: return
|
|
354
|
+
|
|
355
|
+
val point = map.projection.toScreenLocation(marker.position)
|
|
356
|
+
contentView.translationX = point.x - contentView.width * calloutView.anchorX
|
|
357
|
+
contentView.translationY = point.y - contentView.height * calloutView.anchorY
|
|
245
358
|
}
|
|
246
359
|
|
|
247
360
|
// endregion
|
|
@@ -382,6 +495,7 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
382
495
|
}
|
|
383
496
|
|
|
384
497
|
override fun removeMarkerView(markerView: LuggMarkerView) {
|
|
498
|
+
removeLiveMarker(markerView)
|
|
385
499
|
markerView.marker?.let { markerToViewMap.remove(it) }
|
|
386
500
|
markerView.marker?.remove()
|
|
387
501
|
markerView.marker = null
|
|
@@ -411,9 +525,14 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
411
525
|
isDraggable = markerView.draggable
|
|
412
526
|
}
|
|
413
527
|
|
|
414
|
-
if (markerView.hasCustomView
|
|
415
|
-
markerView.
|
|
416
|
-
|
|
528
|
+
if (markerView.hasCustomView) {
|
|
529
|
+
if (markerView.scaleChanged) {
|
|
530
|
+
markerView.applyScaleToMarker()
|
|
531
|
+
markerView.clearScaleChanged()
|
|
532
|
+
}
|
|
533
|
+
if (!markerView.rasterize) {
|
|
534
|
+
positionLiveMarker(markerView)
|
|
535
|
+
}
|
|
417
536
|
}
|
|
418
537
|
}
|
|
419
538
|
|
|
@@ -440,7 +559,64 @@ class GoogleMapProvider(private val context: Context) :
|
|
|
440
559
|
|
|
441
560
|
markerView.marker = marker
|
|
442
561
|
markerToViewMap[marker] = markerView
|
|
443
|
-
|
|
562
|
+
|
|
563
|
+
if (markerView.hasCustomView) {
|
|
564
|
+
if (markerView.rasterize) {
|
|
565
|
+
markerView.applyIconToMarker()
|
|
566
|
+
} else {
|
|
567
|
+
showLiveMarker(markerView)
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Workaround: AdvancedMarker.iconView is buggy on Android, so we manually add the custom
|
|
573
|
+
// content view to the wrapper and position it via screen projection instead. The underlying
|
|
574
|
+
// marker uses a transparent bitmap matching the content size so taps still trigger onMarkerClick.
|
|
575
|
+
private fun showLiveMarker(markerView: LuggMarkerView) {
|
|
576
|
+
val wrapper = wrapperView ?: return
|
|
577
|
+
|
|
578
|
+
markerView.onUpdate = {
|
|
579
|
+
updateLiveMarkerHitArea(markerView)
|
|
580
|
+
positionLiveMarker(markerView)
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
val contentView = markerView.contentView
|
|
584
|
+
contentView.pointerEvents = com.facebook.react.uimanager.PointerEvents.NONE
|
|
585
|
+
(contentView.parent as? android.view.ViewGroup)?.removeView(contentView)
|
|
586
|
+
wrapper.addView(contentView)
|
|
587
|
+
liveMarkerViews.add(markerView)
|
|
588
|
+
markerView.layoutContentView()
|
|
589
|
+
updateLiveMarkerHitArea(markerView)
|
|
590
|
+
positionLiveMarker(markerView)
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
private fun updateLiveMarkerHitArea(markerView: LuggMarkerView) {
|
|
594
|
+
val marker = markerView.marker ?: return
|
|
595
|
+
val contentView = markerView.contentView
|
|
596
|
+
val w = contentView.width.coerceAtLeast(1)
|
|
597
|
+
val h = contentView.height.coerceAtLeast(1)
|
|
598
|
+
marker.setIcon(BitmapDescriptorFactory.fromBitmap(createBitmap(w, h)))
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
private fun removeLiveMarker(markerView: LuggMarkerView) {
|
|
602
|
+
markerView.onUpdate = null
|
|
603
|
+
val contentView = markerView.contentView
|
|
604
|
+
(contentView.parent as? android.view.ViewGroup)?.removeView(contentView)
|
|
605
|
+
liveMarkerViews.remove(markerView)
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
private fun positionLiveMarkers() {
|
|
609
|
+
for (markerView in liveMarkerViews) {
|
|
610
|
+
positionLiveMarker(markerView)
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
private fun positionLiveMarker(markerView: LuggMarkerView) {
|
|
615
|
+
val map = googleMap ?: return
|
|
616
|
+
val contentView = markerView.contentView
|
|
617
|
+
val point = map.projection.toScreenLocation(LatLng(markerView.latitude, markerView.longitude))
|
|
618
|
+
contentView.translationX = point.x - contentView.width * markerView.anchorX
|
|
619
|
+
contentView.translationY = point.y - contentView.height * markerView.anchorY
|
|
444
620
|
}
|
|
445
621
|
|
|
446
622
|
// endregion
|