@jacques_gordon/expo-mapbox-navigation 2.0.6 → 2.0.7

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.
@@ -1,11 +1,14 @@
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 // ← correct import for .camera extension
11
+ import com.mapbox.maps.plugin.locationcomponent.location
9
12
  import com.mapbox.navigation.base.extensions.applyDefaultNavigationOptions
10
13
  import com.mapbox.navigation.base.options.NavigationOptions
11
14
  import com.mapbox.navigation.base.route.NavigationRoute
@@ -65,16 +68,22 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
65
68
  val navigationOptions = NavigationOptions.Builder(context).build()
66
69
  mapboxNavigation = MapboxNavigationProvider.create(navigationOptions)
67
70
 
68
- mapView.getMapboxMap().loadStyle(mapStyle ?: Style.MAPBOX_STREETS) { style ->
71
+ mapView.mapboxMap.loadStyle(mapStyle ?: Style.MAPBOX_STREETS) { style ->
72
+ // Route line components
69
73
  routeLineApi = MapboxRouteLineApi(MapboxRouteLineApiOptions.Builder().build())
70
- routeLineView = MapboxRouteLineView(MapboxRouteLineViewOptions.Builder(context).build())
74
+ routeLineView = MapboxRouteLineView(
75
+ MapboxRouteLineViewOptions.Builder(context).build()
76
+ )
71
77
 
72
- viewportDataSource = MapboxNavigationViewportDataSource(mapView.getMapboxMap())
78
+ // NavigationCamera — exactly as in official Mapbox example:
79
+ // NavigationCamera(mapboxMap, mapView.camera, viewportDataSource)
80
+ viewportDataSource = MapboxNavigationViewportDataSource(mapView.mapboxMap)
73
81
  navigationCamera = NavigationCamera(
74
- mapView.getMapboxMap(),
75
- mapView.camera,
82
+ mapView.mapboxMap,
83
+ mapView.camera, // ← mapView.camera (Maps plugin extension)
76
84
  viewportDataSource!!
77
85
  )
86
+
78
87
  registerObservers()
79
88
  }
80
89
  }
@@ -82,33 +91,54 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
82
91
  private fun registerObservers() {
83
92
  val nav = mapboxNavigation ?: return
84
93
 
94
+ // Routes observer
85
95
  nav.registerRoutesObserver(object : RoutesObserver {
86
96
  override fun onRoutesChanged(result: RoutesUpdatedResult) {
87
97
  val routes = result.navigationRoutes
88
98
  if (routes.isNotEmpty()) {
89
99
  routeLineApi?.setNavigationRoutes(routes) { value ->
90
- mapView.getMapboxMap().getStyle()?.let { style ->
100
+ mapView.mapboxMap.style?.let { style ->
91
101
  routeLineView?.renderRouteDrawData(style, value)
92
102
  }
93
103
  }
104
+ // Update viewport data source with the route
105
+ viewportDataSource?.onRouteChanged(routes.first())
106
+ viewportDataSource?.evaluate()
107
+
108
+ // Issue #43 fix: safe camera following transition
94
109
  safeCameraFollowing()
95
- emit("onRoutesReady", mapOf(
110
+
111
+ emitEvent("onRoutesReady", mapOf(
96
112
  "routeCount" to routes.size,
97
- "distanceMeters" to (routes.firstOrNull()?.directionsRoute?.distance() ?: 0.0),
98
- "durationSeconds" to (routes.firstOrNull()?.directionsRoute?.duration() ?: 0.0)
113
+ "distanceMeters" to (routes.first().directionsRoute.distance() ?: 0.0),
114
+ "durationSeconds" to (routes.first().directionsRoute.duration() ?: 0.0)
99
115
  ))
116
+ } else {
117
+ routeLineApi?.clearRouteLine { value ->
118
+ mapView.mapboxMap.style?.let { style ->
119
+ routeLineView?.renderClearRouteLineValue(style, value)
120
+ }
121
+ }
122
+ viewportDataSource?.clearRouteData()
123
+ viewportDataSource?.evaluate()
100
124
  }
101
125
  }
102
126
  })
103
127
 
104
- nav.registerRouteProgressObserver(RouteProgressObserver { progress ->
105
- emit("onRouteProgressChanged", mapOf(
106
- "distanceRemaining" to progress.distanceRemaining,
107
- "durationRemaining" to progress.durationRemaining,
108
- "distanceTraveled" to progress.distanceTraveled,
109
- "fractionTraveled" to progress.fractionTraveled,
128
+ // Route progress observer
129
+ nav.registerRouteProgressObserver(RouteProgressObserver { routeProgress ->
130
+ // Update viewport data source with progress
131
+ viewportDataSource?.onRouteProgressChanged(routeProgress)
132
+ viewportDataSource?.evaluate()
133
+
134
+ emitEvent("onRouteProgressChanged", mapOf(
135
+ "distanceRemaining" to routeProgress.distanceRemaining,
136
+ "durationRemaining" to routeProgress.durationRemaining,
137
+ "distanceTraveled" to routeProgress.distanceTraveled,
138
+ "fractionTraveled" to routeProgress.fractionTraveled,
110
139
  "currentStepDistanceRemaining" to
111
- (progress.currentLegProgress?.currentStepProgress?.distanceRemaining ?: 0f)
140
+ (routeProgress.currentLegProgress
141
+ ?.currentStepProgress?.distanceRemaining ?: 0f)
112
142
  ))
113
143
  })
114
144
  }
@@ -127,7 +157,7 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
127
157
  }
128
158
 
129
159
  // ── Issue #31: voice units ────────────────────────────────────────────────
130
- private fun resolveUnitType(): String {
160
+ private fun resolveVoiceUnits(): String {
131
161
  return when (voiceUnits?.lowercase()) {
132
162
  "metric" -> "metric"
133
163
  "imperial" -> "imperial"
@@ -139,6 +169,8 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
139
169
  }
140
170
  }
141
171
 
172
+ // ── Route request ─────────────────────────────────────────────────────────
173
+ @SuppressLint("MissingPermission")
142
174
  private fun fetchRoutes() {
143
175
  val nav = mapboxNavigation ?: return
144
176
  if (coordinates.size < 2) return
@@ -146,24 +178,25 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
146
178
  val points = coordinates.map { coord ->
147
179
  Point.fromLngLat(coord["longitude"] ?: 0.0, coord["latitude"] ?: 0.0)
148
180
  }
149
-
150
181
  val locale = language?.let { Locale.forLanguageTag(it) } ?: Locale.getDefault()
151
182
 
152
183
  val builder = RouteOptions.builder()
153
184
  .applyDefaultNavigationOptions()
154
185
  .language(locale.toLanguageTag())
155
- .voiceUnits(resolveUnitType())
186
+ .voiceUnits(resolveVoiceUnits()) // Issue #31 fix
156
187
  .coordinatesList(points)
157
188
 
158
189
  waypointIndices?.let { builder.waypointIndicesList(it) }
159
190
  navigationProfile?.let {
160
191
  builder.profile(if (it.startsWith("mapbox/")) it else "mapbox/$it")
161
192
  }
162
- excludeTypes?.takeIf { it.isNotEmpty() }?.let { builder.exclude(it.joinToString(",")) }
193
+ excludeTypes?.takeIf { it.isNotEmpty() }?.let {
194
+ builder.exclude(it.joinToString(","))
195
+ }
163
196
  maxHeight?.let { builder.maxHeight(it) }
164
197
  maxWidth?.let { builder.maxWidth(it) }
165
198
 
166
- // Nav SDK v3: NavigationRouterCallback uses @RouterOrigin String (not enum)
199
+ // Exact Nav SDK v3 callback signature from official Mapbox docs
167
200
  nav.requestRoutes(
168
201
  builder.build(),
169
202
  object : NavigationRouterCallback {
@@ -172,7 +205,7 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
172
205
  @RouterOrigin routerOrigin: String
173
206
  ) {
174
207
  if (routes.isEmpty()) {
175
- emit("onRoutesFailed", mapOf("message" to "No routes returned"))
208
+ emitEvent("onRoutesFailed", mapOf("message" to "No routes returned"))
176
209
  return
177
210
  }
178
211
  nav.setNavigationRoutes(routes)
@@ -185,7 +218,7 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
185
218
  ) {
186
219
  val msg = reasons.firstOrNull()?.message ?: "Unknown error"
187
220
  Log.e(TAG, "Route request failed: $msg")
188
- emit("onRoutesFailed", mapOf("message" to msg))
221
+ emitEvent("onRoutesFailed", mapOf("message" to msg))
189
222
  }
190
223
 
191
224
  override fun onCanceled(
@@ -198,6 +231,17 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
198
231
  )
199
232
  }
200
233
 
234
+ // ── Expo event emitter (SDK 53 compatible) ────────────────────────────────
235
+ private fun emitEvent(name: String, data: Map<String, Any>) {
236
+ try {
237
+ // Expo modules core SDK 53: use appContext.eventEmitter
238
+ val emitter = appContext.eventEmitter
239
+ emitter?.emit(name, data)
240
+ } catch (e: Exception) {
241
+ Log.e(TAG, "Failed to emit event $name: ${e.message}")
242
+ }
243
+ }
244
+
201
245
  // ── Prop setters ──────────────────────────────────────────────────────────
202
246
  fun setCoordinates(coords: List<Map<String, Double>>) {
203
247
  coordinates = coords
@@ -216,30 +260,13 @@ class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
216
260
  fun setCustomRasterTileUrl(url: String?) { customRasterTileUrl = url }
217
261
  fun setCustomRasterAboveLayerId(layerId: String?) { customRasterAboveLayerId = layerId }
218
262
 
219
- // ── Helpers ───────────────────────────────────────────────────────────────
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
-
263
+ // ── Lifecycle ─────────────────────────────────────────────────────────────
238
264
  override fun onDetachedFromWindow() {
239
265
  super.onDetachedFromWindow()
266
+ routeLineApi?.cancel()
267
+ routeLineView?.cancel()
240
268
  mapboxNavigation?.stopTripSession()
241
269
  MapboxNavigationProvider.destroy()
242
- routeLineApi?.cancel()
243
270
  mapView.onStop()
244
271
  mapView.onDestroy()
245
272
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jacques_gordon/expo-mapbox-navigation",
3
- "version": "2.0.6",
3
+ "version": "2.0.7",
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",