@jacques_gordon/expo-mapbox-navigation 2.0.6 → 2.0.8
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.
|
@@ -8,6 +8,7 @@ class ExpoMapboxNavigationModule : Module() {
|
|
|
8
8
|
Name("ExpoMapboxNavigation")
|
|
9
9
|
|
|
10
10
|
View(ExpoMapboxNavigationView::class) {
|
|
11
|
+
// Events must be declared here — they correspond to EventDispatcher fields in the View
|
|
11
12
|
Events(
|
|
12
13
|
"onRouteProgressChanged",
|
|
13
14
|
"onRoutesReady",
|
|
@@ -17,70 +18,42 @@ class ExpoMapboxNavigationModule : Module() {
|
|
|
17
18
|
"onArrival"
|
|
18
19
|
)
|
|
19
20
|
|
|
20
|
-
// Coordinates: Array of {latitude, longitude} objects
|
|
21
21
|
Prop("coordinates") { view: ExpoMapboxNavigationView, coordinates: List<Map<String, Double>> ->
|
|
22
22
|
view.setCoordinates(coordinates)
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
// Waypoint indices
|
|
26
24
|
Prop("waypointIndices") { view: ExpoMapboxNavigationView, indices: List<Int>? ->
|
|
27
25
|
view.setWaypointIndices(indices)
|
|
28
26
|
}
|
|
29
|
-
|
|
30
|
-
// Language/locale
|
|
31
27
|
Prop("language") { view: ExpoMapboxNavigationView, language: String? ->
|
|
32
28
|
view.setLanguage(language)
|
|
33
29
|
}
|
|
34
|
-
|
|
35
|
-
// ─────────────────────────────────────────────────────────────
|
|
36
|
-
// voiceUnits prop — fixes Issue #31 (metric/imperial selection)
|
|
37
|
-
// Accepts: "metric" | "imperial" | null (auto from locale)
|
|
38
|
-
// ─────────────────────────────────────────────────────────────
|
|
39
30
|
Prop("voiceUnits") { view: ExpoMapboxNavigationView, units: String? ->
|
|
40
31
|
view.setVoiceUnits(units)
|
|
41
32
|
}
|
|
42
|
-
|
|
43
|
-
// Navigation profile
|
|
44
33
|
Prop("navigationProfile") { view: ExpoMapboxNavigationView, profile: String? ->
|
|
45
34
|
view.setNavigationProfile(profile)
|
|
46
35
|
}
|
|
47
|
-
|
|
48
|
-
// Excluded road types
|
|
49
36
|
Prop("excludeTypes") { view: ExpoMapboxNavigationView, types: List<String>? ->
|
|
50
37
|
view.setExcludeTypes(types)
|
|
51
38
|
}
|
|
52
|
-
|
|
53
|
-
// Map style URL
|
|
54
39
|
Prop("mapStyle") { view: ExpoMapboxNavigationView, style: String? ->
|
|
55
40
|
view.setMapStyle(style)
|
|
56
41
|
}
|
|
57
|
-
|
|
58
|
-
// Mute navigation audio
|
|
59
42
|
Prop("mute") { view: ExpoMapboxNavigationView, mute: Boolean ->
|
|
60
43
|
view.setMute(mute)
|
|
61
44
|
}
|
|
62
|
-
|
|
63
|
-
// Vehicle height restriction (meters)
|
|
64
45
|
Prop("maxHeight") { view: ExpoMapboxNavigationView, height: Double? ->
|
|
65
46
|
view.setMaxHeight(height)
|
|
66
47
|
}
|
|
67
|
-
|
|
68
|
-
// Vehicle width restriction (meters)
|
|
69
48
|
Prop("maxWidth") { view: ExpoMapboxNavigationView, width: Double? ->
|
|
70
49
|
view.setMaxWidth(width)
|
|
71
50
|
}
|
|
72
|
-
|
|
73
|
-
// Map matching mode
|
|
74
51
|
Prop("useMapMatching") { view: ExpoMapboxNavigationView, use: Boolean ->
|
|
75
52
|
view.setUseMapMatching(use)
|
|
76
53
|
}
|
|
77
|
-
|
|
78
|
-
// Custom raster tile URL
|
|
79
54
|
Prop("customRasterTileUrl") { view: ExpoMapboxNavigationView, url: String? ->
|
|
80
55
|
view.setCustomRasterTileUrl(url)
|
|
81
56
|
}
|
|
82
|
-
|
|
83
|
-
// Layer ID above which custom raster goes
|
|
84
57
|
Prop("customRasterAboveLayerId") { view: ExpoMapboxNavigationView, layerId: String? ->
|
|
85
58
|
view.setCustomRasterAboveLayerId(layerId)
|
|
86
59
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
package expo.modules.mapboxnavigation
|
|
2
2
|
|
|
3
|
+
import android.annotation.SuppressLint
|
|
3
4
|
import android.content.Context
|
|
4
5
|
import android.util.Log
|
|
5
6
|
import com.mapbox.api.directions.v5.models.RouteOptions
|
|
6
7
|
import com.mapbox.geojson.Point
|
|
7
8
|
import com.mapbox.maps.MapView
|
|
8
9
|
import com.mapbox.maps.Style
|
|
10
|
+
import com.mapbox.maps.plugin.animation.camera
|
|
9
11
|
import com.mapbox.navigation.base.extensions.applyDefaultNavigationOptions
|
|
10
12
|
import com.mapbox.navigation.base.options.NavigationOptions
|
|
11
13
|
import com.mapbox.navigation.base.route.NavigationRoute
|
|
@@ -25,6 +27,7 @@ import com.mapbox.navigation.ui.maps.route.line.api.MapboxRouteLineView
|
|
|
25
27
|
import com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineApiOptions
|
|
26
28
|
import com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineViewOptions
|
|
27
29
|
import expo.modules.kotlin.AppContext
|
|
30
|
+
import expo.modules.kotlin.viewevent.EventDispatcher
|
|
28
31
|
import expo.modules.kotlin.views.ExpoView
|
|
29
32
|
import java.util.Locale
|
|
30
33
|
|
|
@@ -33,6 +36,16 @@ private const val TAG = "ExpoMapboxNavigation"
|
|
|
33
36
|
class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
|
|
34
37
|
ExpoView(context, appContext) {
|
|
35
38
|
|
|
39
|
+
// ── EventDispatchers — the correct Expo Modules API for View events ───────
|
|
40
|
+
// Each event declared here must match the Events(...) list in ExpoMapboxNavigationModule
|
|
41
|
+
private val onRouteProgressChanged by EventDispatcher()
|
|
42
|
+
private val onRoutesReady by EventDispatcher()
|
|
43
|
+
private val onNavigationFinished by EventDispatcher()
|
|
44
|
+
private val onNavigationCancelled by EventDispatcher()
|
|
45
|
+
private val onRoutesFailed by EventDispatcher()
|
|
46
|
+
private val onArrival by EventDispatcher()
|
|
47
|
+
|
|
48
|
+
// ── Map & Navigation ──────────────────────────────────────────────────────
|
|
36
49
|
private val mapView: MapView = MapView(context)
|
|
37
50
|
private var navigationCamera: NavigationCamera? = null
|
|
38
51
|
private var viewportDataSource: MapboxNavigationViewportDataSource? = null
|
|
@@ -40,7 +53,7 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
|
|
|
40
53
|
private var routeLineView: MapboxRouteLineView? = null
|
|
41
54
|
private var mapboxNavigation: MapboxNavigation? = null
|
|
42
55
|
|
|
43
|
-
// Props
|
|
56
|
+
// ── Props ─────────────────────────────────────────────────────────────────
|
|
44
57
|
private var coordinates: List<Map<String, Double>> = emptyList()
|
|
45
58
|
private var waypointIndices: List<Int>? = null
|
|
46
59
|
private var language: String? = null
|
|
@@ -65,13 +78,14 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
|
|
|
65
78
|
val navigationOptions = NavigationOptions.Builder(context).build()
|
|
66
79
|
mapboxNavigation = MapboxNavigationProvider.create(navigationOptions)
|
|
67
80
|
|
|
68
|
-
mapView.
|
|
81
|
+
mapView.mapboxMap.loadStyle(mapStyle ?: Style.MAPBOX_STREETS) { _ ->
|
|
69
82
|
routeLineApi = MapboxRouteLineApi(MapboxRouteLineApiOptions.Builder().build())
|
|
70
|
-
routeLineView = MapboxRouteLineView(
|
|
71
|
-
|
|
72
|
-
|
|
83
|
+
routeLineView = MapboxRouteLineView(
|
|
84
|
+
MapboxRouteLineViewOptions.Builder(context).build()
|
|
85
|
+
)
|
|
86
|
+
viewportDataSource = MapboxNavigationViewportDataSource(mapView.mapboxMap)
|
|
73
87
|
navigationCamera = NavigationCamera(
|
|
74
|
-
mapView.
|
|
88
|
+
mapView.mapboxMap,
|
|
75
89
|
mapView.camera,
|
|
76
90
|
viewportDataSource!!
|
|
77
91
|
)
|
|
@@ -87,33 +101,48 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
|
|
|
87
101
|
val routes = result.navigationRoutes
|
|
88
102
|
if (routes.isNotEmpty()) {
|
|
89
103
|
routeLineApi?.setNavigationRoutes(routes) { value ->
|
|
90
|
-
mapView.
|
|
104
|
+
mapView.mapboxMap.style?.let { style ->
|
|
91
105
|
routeLineView?.renderRouteDrawData(style, value)
|
|
92
106
|
}
|
|
93
107
|
}
|
|
108
|
+
viewportDataSource?.onRouteChanged(routes.first())
|
|
109
|
+
viewportDataSource?.evaluate()
|
|
94
110
|
safeCameraFollowing()
|
|
95
|
-
|
|
111
|
+
|
|
112
|
+
onRoutesReady(mapOf(
|
|
96
113
|
"routeCount" to routes.size,
|
|
97
|
-
"distanceMeters" to (routes.
|
|
98
|
-
"durationSeconds" to (routes.
|
|
114
|
+
"distanceMeters" to (routes.first().directionsRoute.distance() ?: 0.0),
|
|
115
|
+
"durationSeconds" to (routes.first().directionsRoute.duration() ?: 0.0)
|
|
99
116
|
))
|
|
117
|
+
} else {
|
|
118
|
+
routeLineApi?.clearRouteLine { value ->
|
|
119
|
+
mapView.mapboxMap.style?.let { style ->
|
|
120
|
+
routeLineView?.renderClearRouteLineValue(style, value)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
viewportDataSource?.clearRouteData()
|
|
124
|
+
viewportDataSource?.evaluate()
|
|
100
125
|
}
|
|
101
126
|
}
|
|
102
127
|
})
|
|
103
128
|
|
|
104
|
-
nav.registerRouteProgressObserver(RouteProgressObserver {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
"
|
|
129
|
+
nav.registerRouteProgressObserver(RouteProgressObserver { routeProgress ->
|
|
130
|
+
viewportDataSource?.onRouteProgressChanged(routeProgress)
|
|
131
|
+
viewportDataSource?.evaluate()
|
|
132
|
+
|
|
133
|
+
onRouteProgressChanged(mapOf(
|
|
134
|
+
"distanceRemaining" to routeProgress.distanceRemaining,
|
|
135
|
+
"durationRemaining" to routeProgress.durationRemaining,
|
|
136
|
+
"distanceTraveled" to routeProgress.distanceTraveled,
|
|
137
|
+
"fractionTraveled" to routeProgress.fractionTraveled,
|
|
110
138
|
"currentStepDistanceRemaining" to
|
|
111
|
-
(
|
|
139
|
+
(routeProgress.currentLegProgress
|
|
140
|
+
?.currentStepProgress?.distanceRemaining ?: 0f)
|
|
112
141
|
))
|
|
113
142
|
})
|
|
114
143
|
}
|
|
115
144
|
|
|
116
|
-
// ── Issue #43: safe camera
|
|
145
|
+
// ── Issue #43: safe camera ────────────────────────────────────────────────
|
|
117
146
|
private fun safeCameraFollowing() {
|
|
118
147
|
try {
|
|
119
148
|
navigationCamera?.requestNavigationCameraToFollowing(
|
|
@@ -127,18 +156,19 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
|
|
|
127
156
|
}
|
|
128
157
|
|
|
129
158
|
// ── Issue #31: voice units ────────────────────────────────────────────────
|
|
130
|
-
private fun
|
|
159
|
+
private fun resolveVoiceUnits(): String {
|
|
131
160
|
return when (voiceUnits?.lowercase()) {
|
|
132
161
|
"metric" -> "metric"
|
|
133
162
|
"imperial" -> "imperial"
|
|
134
163
|
else -> {
|
|
135
164
|
val locale = language?.let { Locale.forLanguageTag(it) } ?: Locale.getDefault()
|
|
136
|
-
|
|
137
|
-
if (locale.country in imperialCountries) "imperial" else "metric"
|
|
165
|
+
if (locale.country in setOf("US", "GB", "LR", "MM")) "imperial" else "metric"
|
|
138
166
|
}
|
|
139
167
|
}
|
|
140
168
|
}
|
|
141
169
|
|
|
170
|
+
// ── Route request ─────────────────────────────────────────────────────────
|
|
171
|
+
@SuppressLint("MissingPermission")
|
|
142
172
|
private fun fetchRoutes() {
|
|
143
173
|
val nav = mapboxNavigation ?: return
|
|
144
174
|
if (coordinates.size < 2) return
|
|
@@ -146,24 +176,24 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
|
|
|
146
176
|
val points = coordinates.map { coord ->
|
|
147
177
|
Point.fromLngLat(coord["longitude"] ?: 0.0, coord["latitude"] ?: 0.0)
|
|
148
178
|
}
|
|
149
|
-
|
|
150
179
|
val locale = language?.let { Locale.forLanguageTag(it) } ?: Locale.getDefault()
|
|
151
180
|
|
|
152
181
|
val builder = RouteOptions.builder()
|
|
153
182
|
.applyDefaultNavigationOptions()
|
|
154
183
|
.language(locale.toLanguageTag())
|
|
155
|
-
.voiceUnits(
|
|
184
|
+
.voiceUnits(resolveVoiceUnits())
|
|
156
185
|
.coordinatesList(points)
|
|
157
186
|
|
|
158
187
|
waypointIndices?.let { builder.waypointIndicesList(it) }
|
|
159
188
|
navigationProfile?.let {
|
|
160
189
|
builder.profile(if (it.startsWith("mapbox/")) it else "mapbox/$it")
|
|
161
190
|
}
|
|
162
|
-
excludeTypes?.takeIf { it.isNotEmpty() }?.let {
|
|
191
|
+
excludeTypes?.takeIf { it.isNotEmpty() }?.let {
|
|
192
|
+
builder.exclude(it.joinToString(","))
|
|
193
|
+
}
|
|
163
194
|
maxHeight?.let { builder.maxHeight(it) }
|
|
164
195
|
maxWidth?.let { builder.maxWidth(it) }
|
|
165
196
|
|
|
166
|
-
// Nav SDK v3: NavigationRouterCallback uses @RouterOrigin String (not enum)
|
|
167
197
|
nav.requestRoutes(
|
|
168
198
|
builder.build(),
|
|
169
199
|
object : NavigationRouterCallback {
|
|
@@ -172,7 +202,7 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
|
|
|
172
202
|
@RouterOrigin routerOrigin: String
|
|
173
203
|
) {
|
|
174
204
|
if (routes.isEmpty()) {
|
|
175
|
-
|
|
205
|
+
onRoutesFailed(mapOf("message" to "No routes returned"))
|
|
176
206
|
return
|
|
177
207
|
}
|
|
178
208
|
nav.setNavigationRoutes(routes)
|
|
@@ -185,7 +215,7 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
|
|
|
185
215
|
) {
|
|
186
216
|
val msg = reasons.firstOrNull()?.message ?: "Unknown error"
|
|
187
217
|
Log.e(TAG, "Route request failed: $msg")
|
|
188
|
-
|
|
218
|
+
onRoutesFailed(mapOf("message" to msg))
|
|
189
219
|
}
|
|
190
220
|
|
|
191
221
|
override fun onCanceled(
|
|
@@ -216,30 +246,13 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
|
|
|
216
246
|
fun setCustomRasterTileUrl(url: String?) { customRasterTileUrl = url }
|
|
217
247
|
fun setCustomRasterAboveLayerId(layerId: String?) { customRasterAboveLayerId = layerId }
|
|
218
248
|
|
|
219
|
-
// ──
|
|
220
|
-
private fun getAccessToken(): String? {
|
|
221
|
-
return try {
|
|
222
|
-
val resId = context.resources.getIdentifier(
|
|
223
|
-
"mapbox_access_token", "string", context.packageName
|
|
224
|
-
)
|
|
225
|
-
if (resId != 0) context.getString(resId) else null
|
|
226
|
-
} catch (e: Exception) { null }
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Correct Expo SDK v53 event emitter API
|
|
230
|
-
private fun emit(name: String, data: Map<String, Any>) {
|
|
231
|
-
try {
|
|
232
|
-
appContext.eventEmitter?.emit(name, data)
|
|
233
|
-
} catch (e: Exception) {
|
|
234
|
-
Log.e(TAG, "Failed to emit event $name: ${e.message}")
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
249
|
+
// ── Lifecycle ─────────────────────────────────────────────────────────────
|
|
238
250
|
override fun onDetachedFromWindow() {
|
|
239
251
|
super.onDetachedFromWindow()
|
|
252
|
+
routeLineApi?.cancel()
|
|
253
|
+
routeLineView?.cancel()
|
|
240
254
|
mapboxNavigation?.stopTripSession()
|
|
241
255
|
MapboxNavigationProvider.destroy()
|
|
242
|
-
routeLineApi?.cancel()
|
|
243
256
|
mapView.onStop()
|
|
244
257
|
mapView.onDestroy()
|
|
245
258
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jacques_gordon/expo-mapbox-navigation",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.8",
|
|
4
4
|
"description": "Expo module for Mapbox Navigation SDK with 16KB page size support, NDK27, and Mapbox Maps v11.11.0+",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|