@sargsyansevak/expo-mapbox-navigation 1.0.0

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.
Files changed (70) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +313 -0
  4. package/android/build.gradle +98 -0
  5. package/android/src/main/AndroidManifest.xml +2 -0
  6. package/android/src/main/java/expo/modules/mapboxnavigation/ExpoMapboxNavigationModule.kt +120 -0
  7. package/android/src/main/java/expo/modules/mapboxnavigation/ExpoMapboxNavigationView.kt +1100 -0
  8. package/android/src/main/res/drawable-hdpi/icon_compass.png +0 -0
  9. package/android/src/main/res/drawable-hdpi/icon_mute.png +0 -0
  10. package/android/src/main/res/drawable-hdpi/icon_overview.png +0 -0
  11. package/android/src/main/res/drawable-hdpi/icon_sound.png +0 -0
  12. package/android/src/main/res/drawable-hdpi/icon_x.png +0 -0
  13. package/android/src/main/res/drawable-ldpi/icon_compass.png +0 -0
  14. package/android/src/main/res/drawable-ldpi/icon_mute.png +0 -0
  15. package/android/src/main/res/drawable-ldpi/icon_overview.png +0 -0
  16. package/android/src/main/res/drawable-ldpi/icon_sound.png +0 -0
  17. package/android/src/main/res/drawable-ldpi/icon_x.png +0 -0
  18. package/android/src/main/res/drawable-mdpi/icon_compass.png +0 -0
  19. package/android/src/main/res/drawable-mdpi/icon_mute.png +0 -0
  20. package/android/src/main/res/drawable-mdpi/icon_overview.png +0 -0
  21. package/android/src/main/res/drawable-mdpi/icon_sound.png +0 -0
  22. package/android/src/main/res/drawable-mdpi/icon_x.png +0 -0
  23. package/android/src/main/res/drawable-xhdpi/icon_compass.png +0 -0
  24. package/android/src/main/res/drawable-xhdpi/icon_mute.png +0 -0
  25. package/android/src/main/res/drawable-xhdpi/icon_overview.png +0 -0
  26. package/android/src/main/res/drawable-xhdpi/icon_sound.png +0 -0
  27. package/android/src/main/res/drawable-xhdpi/icon_x.png +0 -0
  28. package/android/src/main/res/drawable-xxhdpi/icon_compass.png +0 -0
  29. package/android/src/main/res/drawable-xxhdpi/icon_mute.png +0 -0
  30. package/android/src/main/res/drawable-xxhdpi/icon_overview.png +0 -0
  31. package/android/src/main/res/drawable-xxhdpi/icon_sound.png +0 -0
  32. package/android/src/main/res/drawable-xxhdpi/icon_x.png +0 -0
  33. package/android/src/main/res/drawable-xxxhdpi/icon_compass.png +0 -0
  34. package/android/src/main/res/drawable-xxxhdpi/icon_mute.png +0 -0
  35. package/android/src/main/res/drawable-xxxhdpi/icon_overview.png +0 -0
  36. package/android/src/main/res/drawable-xxxhdpi/icon_sound.png +0 -0
  37. package/android/src/main/res/drawable-xxxhdpi/icon_x.png +0 -0
  38. package/android/src/main/res/values/styles.xml +9 -0
  39. package/app.plugin.js +1 -0
  40. package/build/ExpoMapboxNavigation.types.d.ts +90 -0
  41. package/build/ExpoMapboxNavigation.types.d.ts.map +1 -0
  42. package/build/ExpoMapboxNavigation.types.js +2 -0
  43. package/build/ExpoMapboxNavigation.types.js.map +1 -0
  44. package/build/ExpoMapboxNavigationModule.d.ts +3 -0
  45. package/build/ExpoMapboxNavigationModule.d.ts.map +1 -0
  46. package/build/ExpoMapboxNavigationModule.js +3 -0
  47. package/build/ExpoMapboxNavigationModule.js.map +1 -0
  48. package/build/ExpoMapboxNavigationView.d.ts +5 -0
  49. package/build/ExpoMapboxNavigationView.d.ts.map +1 -0
  50. package/build/ExpoMapboxNavigationView.js +7 -0
  51. package/build/ExpoMapboxNavigationView.js.map +1 -0
  52. package/build/ExpoMapboxNavigationView.web.d.ts +4 -0
  53. package/build/ExpoMapboxNavigationView.web.d.ts.map +1 -0
  54. package/build/ExpoMapboxNavigationView.web.js +7 -0
  55. package/build/ExpoMapboxNavigationView.web.js.map +1 -0
  56. package/build/index.d.ts +4 -0
  57. package/build/index.d.ts.map +1 -0
  58. package/build/index.js +3 -0
  59. package/build/index.js.map +1 -0
  60. package/expo-module.config.json +9 -0
  61. package/ios/ExpoMapboxNavigation.podspec +46 -0
  62. package/ios/ExpoMapboxNavigationModule.swift +92 -0
  63. package/ios/ExpoMapboxNavigationView.swift +509 -0
  64. package/package.json +47 -0
  65. package/plugin/config.js +127 -0
  66. package/src/ExpoMapboxNavigation.types.ts +70 -0
  67. package/src/ExpoMapboxNavigationModule.ts +2 -0
  68. package/src/ExpoMapboxNavigationView.tsx +17 -0
  69. package/src/ExpoMapboxNavigationView.web.tsx +13 -0
  70. package/src/index.ts +11 -0
@@ -0,0 +1,509 @@
1
+ import ExpoModulesCore
2
+ import MapboxNavigationCore
3
+ import MapboxMaps
4
+ import MapboxNavigationUIKit
5
+ import MapboxDirections
6
+ import Combine
7
+
8
+
9
+ class ExpoMapboxNavigationView: ExpoView {
10
+ private let onRouteProgressChanged = EventDispatcher()
11
+ private let onCancelNavigation = EventDispatcher()
12
+ private let onWaypointArrival = EventDispatcher()
13
+ private let onFinalDestinationArrival = EventDispatcher()
14
+ private let onRouteChanged = EventDispatcher()
15
+ private let onUserOffRoute = EventDispatcher()
16
+ private let onRoutesLoaded = EventDispatcher()
17
+ private let onRouteFailedToLoad = EventDispatcher()
18
+
19
+ let controller = ExpoMapboxNavigationViewController()
20
+
21
+ required init(appContext: AppContext? = nil) {
22
+ super.init(appContext: appContext)
23
+ clipsToBounds = true
24
+ addSubview(controller.view)
25
+
26
+ controller.onRouteProgressChanged = onRouteProgressChanged
27
+ controller.onCancelNavigation = onCancelNavigation
28
+ controller.onWaypointArrival = onWaypointArrival
29
+ controller.onFinalDestinationArrival = onFinalDestinationArrival
30
+ controller.onRouteChanged = onRouteChanged
31
+ controller.onUserOffRoute = onUserOffRoute
32
+ controller.onRoutesLoaded = onRoutesLoaded
33
+ controller.onRouteFailedToLoad = onRouteFailedToLoad
34
+ }
35
+
36
+ override func layoutSubviews() {
37
+ controller.view.frame = bounds
38
+ }
39
+ }
40
+
41
+
42
+ class ExpoMapboxNavigationViewController: UIViewController {
43
+ static let navigationProvider: MapboxNavigationProvider = MapboxNavigationProvider(coreConfig: CoreConfig(routingConfig: RoutingConfig(fasterRouteDetectionConfig: Optional<FasterRouteDetectionConfig>.none),locationSource: .live ))
44
+ var mapboxNavigation: MapboxNavigation? = nil
45
+ var routingProvider: RoutingProvider? = nil
46
+ var navigation: NavigationController? = nil
47
+ var tripSession: SessionController? = nil
48
+ var navigationViewController: NavigationViewController? = nil
49
+
50
+ var currentCoordinates: Array<CLLocationCoordinate2D>? = nil
51
+ var initialLocation: CLLocationCoordinate2D? = nil
52
+ var initialLocationZoom: Double? = nil
53
+ var currentWaypointIndices: Array<Int>? = nil
54
+ var currentLocale: Locale = Locale.current
55
+ var currentRouteProfile: String? = nil
56
+ var currentRouteExcludeList: Array<String>? = nil
57
+ var currentMapStyle: String? = nil
58
+ var currentCustomRasterSourceUrl: String? = nil
59
+ var currentPlaceCustomRasterLayerAbove: String? = nil
60
+ var currentDisableAlternativeRoutes: Bool? = nil
61
+ var currentFollowingZoom: Double? = nil
62
+ var isUsingRouteMatchingApi: Bool = false
63
+ var vehicleMaxHeight: Double? = nil
64
+ var vehicleMaxWidth: Double? = nil
65
+
66
+ var onRouteProgressChanged: EventDispatcher? = nil
67
+ var onCancelNavigation: EventDispatcher? = nil
68
+ var onWaypointArrival: EventDispatcher? = nil
69
+ var onFinalDestinationArrival: EventDispatcher? = nil
70
+ var onRouteChanged: EventDispatcher? = nil
71
+ var onUserOffRoute: EventDispatcher? = nil
72
+ var onRoutesLoaded: EventDispatcher? = nil
73
+ var onRouteFailedToLoad: EventDispatcher? = nil
74
+
75
+ var calculateRoutesTask: Task<Void, Error>? = nil
76
+ private var routeProgressCancellable: AnyCancellable? = nil
77
+ private var waypointArrivalCancellable: AnyCancellable? = nil
78
+ private var reroutingCancellable: AnyCancellable? = nil
79
+ private var sessionCancellable: AnyCancellable? = nil
80
+
81
+ init() {
82
+ super.init(nibName: nil, bundle: nil)
83
+ mapboxNavigation = ExpoMapboxNavigationViewController.navigationProvider.mapboxNavigation
84
+ routingProvider = mapboxNavigation!.routingProvider()
85
+ navigation = mapboxNavigation!.navigation()
86
+ tripSession = mapboxNavigation!.tripSession()
87
+
88
+ routeProgressCancellable = navigation!.routeProgress.sink { progressState in
89
+ if(progressState != nil){
90
+
91
+
92
+ // For some reason the maneuver arrows sometimes (not consistently) show up the same color as the route line making them invisible.
93
+ // This is a hack to always ensure the arrows are visible.
94
+ try? self.navigationViewController?.navigationMapView?.mapView.mapboxMap.setLayerProperty(
95
+ for: "com.mapbox.navigation.arrow.next",
96
+ property: "line-color",
97
+ value: "#FFFFFF"
98
+ )
99
+ try? self.navigationViewController?.navigationMapView?.mapView.mapboxMap.setLayerProperty(
100
+ for: "com.mapbox.navigation.arrow.next.stroke",
101
+ property: "line-color",
102
+ value: "#FFFFFF"
103
+ )
104
+ try? self.navigationViewController?.navigationMapView?.mapView.mapboxMap.setLayerProperty(
105
+ for: "com.mapbox.navigation.arrow.next.symbol",
106
+ property: "icon-color",
107
+ value: "#FFFFFF"
108
+ )
109
+ try? self.navigationViewController?.navigationMapView?.mapView.mapboxMap.setLayerProperty(
110
+ for: "com.mapbox.navigation.arrow.next.symbol.casing",
111
+ property: "icon-color",
112
+ value: "#FFFFFF"
113
+ )
114
+
115
+ self.onRouteProgressChanged?([
116
+ "distanceRemaining": progressState!.routeProgress.distanceRemaining,
117
+ "distanceTraveled": progressState!.routeProgress.distanceTraveled,
118
+ "durationRemaining": progressState!.routeProgress.durationRemaining,
119
+ "fractionTraveled": progressState!.routeProgress.fractionTraveled,
120
+ ])
121
+ }
122
+ }
123
+
124
+ waypointArrivalCancellable = navigation!.waypointsArrival.sink { arrivalStatus in
125
+ let event = arrivalStatus.event
126
+ if event is WaypointArrivalStatus.Events.ToFinalDestination {
127
+ self.onFinalDestinationArrival?()
128
+ } else if event is WaypointArrivalStatus.Events.ToWaypoint {
129
+ self.onWaypointArrival?()
130
+ }
131
+ }
132
+
133
+ reroutingCancellable = navigation!.rerouting.sink { rerouteStatus in
134
+ self.onRouteChanged?()
135
+ }
136
+
137
+ sessionCancellable = tripSession!.session.sink { session in
138
+ let state = session.state
139
+ switch state {
140
+ case .activeGuidance(let activeGuidanceState):
141
+ switch(activeGuidanceState){
142
+ case .offRoute:
143
+ self.onUserOffRoute?()
144
+ default: break
145
+ }
146
+ default: break
147
+ }
148
+ }
149
+
150
+ }
151
+
152
+ deinit {
153
+ routeProgressCancellable?.cancel()
154
+ waypointArrivalCancellable?.cancel()
155
+ reroutingCancellable?.cancel()
156
+ sessionCancellable?.cancel()
157
+ }
158
+
159
+ override func viewDidDisappear(_ animated: Bool) {
160
+ super.viewDidDisappear(animated)
161
+ Task { @MainActor in tripSession?.setToIdle() } // Stops navigation
162
+ }
163
+
164
+ required init?(coder aDecoder: NSCoder) {
165
+ super.init(coder: aDecoder)
166
+ fatalError("This controller should not be loaded through a story board")
167
+ }
168
+
169
+ func addCustomRasterLayer() {
170
+ let navigationMapView = navigationViewController?.navigationMapView
171
+ let sourceId = "raster-source"
172
+ let layerId = "raster-layer"
173
+
174
+ if(currentCustomRasterSourceUrl == nil){
175
+ if let mapView = navigationMapView?.mapView.mapboxMap {
176
+ if mapView.layerExists(withId: layerId) {
177
+ try? mapView.removeLayer(withId: layerId)
178
+ }
179
+ if mapView.sourceExists(withId: sourceId) {
180
+ try? mapView.removeSource(withId: sourceId)
181
+ }
182
+ }
183
+ return
184
+ }
185
+
186
+ let sourceUrl = currentCustomRasterSourceUrl!
187
+
188
+ var rasterSource = RasterSource(id: sourceId)
189
+
190
+ rasterSource.tiles = [sourceUrl]
191
+ rasterSource.tileSize = 256
192
+
193
+ let rasterLayer = RasterLayer(id: layerId, source: sourceId)
194
+
195
+
196
+ if let mapView = navigationMapView?.mapView.mapboxMap {
197
+ if mapView.layerExists(withId: layerId) {
198
+ try? mapView.removeLayer(withId: layerId)
199
+ }
200
+ if mapView.sourceExists(withId: sourceId) {
201
+ try? mapView.removeSource(withId: sourceId)
202
+ }
203
+
204
+ try? mapView.addSource(rasterSource)
205
+ try? mapView.addLayer(rasterLayer, layerPosition: .above(currentPlaceCustomRasterLayerAbove ?? "water"))
206
+ }
207
+ }
208
+
209
+
210
+ func setCoordinates(coordinates: Array<CLLocationCoordinate2D>) {
211
+ currentCoordinates = coordinates
212
+ update()
213
+ }
214
+
215
+ func setVehicleMaxHeight(maxHeight: Double?) {
216
+ vehicleMaxHeight = maxHeight
217
+ update()
218
+ }
219
+
220
+ func setVehicleMaxWidth(maxWidth: Double?) {
221
+ vehicleMaxWidth = maxWidth
222
+ update()
223
+ }
224
+
225
+ func setLocale(locale: String?) {
226
+ if(locale != nil){
227
+ currentLocale = Locale(identifier: locale!)
228
+ } else {
229
+ currentLocale = Locale.current
230
+ }
231
+ update()
232
+ }
233
+
234
+ func setIsUsingRouteMatchingApi(useRouteMatchingApi: Bool?){
235
+ isUsingRouteMatchingApi = useRouteMatchingApi ?? false
236
+ update()
237
+ }
238
+
239
+ func setWaypointIndices(waypointIndices: Array<Int>?){
240
+ currentWaypointIndices = waypointIndices
241
+ update()
242
+ }
243
+
244
+ func setRouteProfile(profile: String?){
245
+ currentRouteProfile = profile
246
+ update()
247
+ }
248
+
249
+ func setRouteExcludeList(excludeList: Array<String>?){
250
+ currentRouteExcludeList = excludeList
251
+ update()
252
+ }
253
+
254
+ func setMapStyle(style: String?){
255
+ currentMapStyle = style
256
+ update()
257
+ }
258
+
259
+ func setCustomRasterSourceUrl(url: String?){
260
+ currentCustomRasterSourceUrl = url
261
+ update()
262
+ }
263
+
264
+ func setPlaceCustomRasterLayerAbove(layerId: String?){
265
+ currentPlaceCustomRasterLayerAbove = layerId
266
+ update()
267
+ }
268
+
269
+ func setDisableAlternativeRoutes(disableAlternativeRoutes: Bool?){
270
+ currentDisableAlternativeRoutes = disableAlternativeRoutes
271
+ update()
272
+ }
273
+
274
+ func recenterMap(){
275
+ let navigationMapView = navigationViewController?.navigationMapView
276
+ navigationMapView?.navigationCamera.update(cameraState: .following)
277
+ }
278
+
279
+ func setIsMuted(isMuted: Bool?){
280
+ if(isMuted != nil){
281
+ ExpoMapboxNavigationViewController.navigationProvider.routeVoiceController.speechSynthesizer.muted = isMuted!
282
+ }
283
+ }
284
+
285
+ func setInitialLocation(location: CLLocationCoordinate2D, zoom: Double?){
286
+ initialLocation = location
287
+ initialLocationZoom = zoom
288
+ let navigationMapView = navigationViewController?.navigationMapView
289
+ if(initialLocation != nil && navigationMapView != nil){
290
+ navigationMapView!.mapView.mapboxMap.setCamera(to: CameraOptions(center: initialLocation!, zoom: initialLocationZoom ?? 15))
291
+ }
292
+ }
293
+
294
+ func setFollowingZoom(followingZoom: Double?){
295
+ let navigationMapView = navigationViewController?.navigationMapView
296
+ currentFollowingZoom = followingZoom
297
+ if(navigationMapView != nil && followingZoom != nil){
298
+ let newDataSource = MobileViewportDataSource(navigationMapView!.mapView)
299
+ newDataSource.options.followingCameraOptions.zoomRange = followingZoom!...followingZoom!
300
+ navigationMapView?.navigationCamera.viewportDataSource = newDataSource
301
+ }
302
+ }
303
+
304
+ func update(){
305
+ calculateRoutesTask?.cancel()
306
+
307
+ if(currentCoordinates != nil){
308
+ let waypoints = currentCoordinates!.enumerated().map {
309
+ let index = $0
310
+ let coordinate = $1
311
+ var waypoint = Waypoint(coordinate: coordinate)
312
+ waypoint.separatesLegs = currentWaypointIndices == nil ? true : currentWaypointIndices!.contains(index)
313
+ return waypoint
314
+ }
315
+
316
+ if(isUsingRouteMatchingApi){
317
+ calculateMapMatchingRoutes(waypoints: waypoints)
318
+ } else {
319
+ calculateRoutes(waypoints: waypoints)
320
+ }
321
+ }
322
+ }
323
+
324
+ func calculateRoutes(waypoints: Array<Waypoint>){
325
+ let routeOptions = NavigationRouteOptions(
326
+ waypoints: waypoints,
327
+ profileIdentifier: currentRouteProfile != nil ? ProfileIdentifier(rawValue: currentRouteProfile!) : nil,
328
+ queryItems: [
329
+ URLQueryItem(name: "exclude", value: currentRouteExcludeList?.joined(separator: ",")),
330
+ URLQueryItem(name: "max_height", value: String(format: "%.1f", vehicleMaxHeight ?? 0.0)),
331
+ URLQueryItem(name: "max_width", value: String(format: "%.1f", vehicleMaxWidth ?? 0.0))
332
+ ],
333
+ locale: currentLocale,
334
+ distanceUnit: currentLocale.usesMetricSystem ? LengthFormatter.Unit.meter : LengthFormatter.Unit.mile
335
+ )
336
+
337
+ calculateRoutesTask = Task {
338
+ switch await self.routingProvider!.calculateRoutes(options: routeOptions).result {
339
+ case .failure(let error):
340
+ onRouteFailedToLoad?([
341
+ "errorMessage": error.localizedDescription
342
+ ])
343
+ print(error.localizedDescription)
344
+ case .success(let navigationRoutes):
345
+ onRoutesCalculated(navigationRoutes: navigationRoutes)
346
+ }
347
+ }
348
+ }
349
+
350
+ func calculateMapMatchingRoutes(waypoints: Array<Waypoint>){
351
+ let matchOptions = NavigationMatchOptions(
352
+ waypoints: waypoints,
353
+ profileIdentifier: currentRouteProfile != nil ? ProfileIdentifier(rawValue: currentRouteProfile!) : nil,
354
+ queryItems: [URLQueryItem(name: "exclude", value: currentRouteExcludeList?.joined(separator: ","))],
355
+ distanceUnit: currentLocale.usesMetricSystem ? LengthFormatter.Unit.meter : LengthFormatter.Unit.mile
356
+ )
357
+ matchOptions.locale = currentLocale
358
+
359
+
360
+ calculateRoutesTask = Task {
361
+ switch await self.routingProvider!.calculateRoutes(options: matchOptions).result {
362
+ case .failure(let error):
363
+ onRouteFailedToLoad?([
364
+ "errorMessage": error.localizedDescription
365
+ ])
366
+ print(error.localizedDescription)
367
+ case .success(let navigationRoutes):
368
+ onRoutesCalculated(navigationRoutes: navigationRoutes)
369
+ }
370
+ }
371
+ }
372
+
373
+ @objc func cancelButtonClicked(_ sender: AnyObject?) {
374
+ onCancelNavigation?()
375
+ }
376
+
377
+ func convertRoute(route: Route) -> Any {
378
+ return [
379
+ "distance": route.distance,
380
+ "expectedTravelTime": route.expectedTravelTime,
381
+ "legs": route.legs.map { leg in
382
+ return [
383
+ "source": leg.source != nil ? [
384
+ "latitude": leg.source!.coordinate.latitude,
385
+ "longitude": leg.source!.coordinate.longitude
386
+ ] : nil,
387
+ "destination": leg.destination != nil ? [
388
+ "latitude": leg.destination!.coordinate.latitude,
389
+ "longitude": leg.destination!.coordinate.longitude
390
+ ] : nil,
391
+ "steps": leg.steps.map { step in
392
+ return [
393
+ "shape": step.shape != nil ? [
394
+ "coordinates": step.shape!.coordinates.map { coordinate in
395
+ return [
396
+ "latitude": coordinate.latitude,
397
+ "longitude": coordinate.longitude,
398
+ ]
399
+ }
400
+ ] : nil
401
+ ]
402
+ }
403
+ ]
404
+ }
405
+ ]
406
+ }
407
+
408
+ func onRoutesCalculated(navigationRoutes: NavigationRoutes){
409
+ onRoutesLoaded?([
410
+ "routes": [
411
+ "mainRoute": convertRoute(route: navigationRoutes.mainRoute.route),
412
+ "alternativeRoutes": navigationRoutes.alternativeRoutes.map { convertRoute(route: $0.route) }
413
+ ]
414
+ ])
415
+
416
+ let topBanner = TopBannerViewController()
417
+ topBanner.instructionsBannerView.distanceFormatter.locale = currentLocale
418
+ let bottomBanner = BottomBannerViewController()
419
+ bottomBanner.distanceFormatter.locale = currentLocale
420
+ bottomBanner.dateFormatter.locale = currentLocale
421
+
422
+ let navigationOptions = NavigationOptions(
423
+ mapboxNavigation: self.mapboxNavigation!,
424
+ voiceController: ExpoMapboxNavigationViewController.navigationProvider.routeVoiceController,
425
+ eventsManager: ExpoMapboxNavigationViewController.navigationProvider.eventsManager(),
426
+ styles: [DayStyle()],
427
+ topBanner: topBanner,
428
+ bottomBanner: bottomBanner
429
+ )
430
+
431
+ let newNavigationControllerRequired = navigationViewController == nil
432
+
433
+ if(newNavigationControllerRequired){
434
+ navigationViewController = NavigationViewController(
435
+ navigationRoutes: navigationRoutes,
436
+ navigationOptions: navigationOptions
437
+ )
438
+ } else {
439
+ navigationViewController!.prepareViewLoading(
440
+ navigationRoutes: navigationRoutes,
441
+ navigationOptions: navigationOptions
442
+ )
443
+ }
444
+
445
+ let navigationViewController = navigationViewController!
446
+
447
+ navigationViewController.showsContinuousAlternatives = currentDisableAlternativeRoutes != true
448
+ navigationViewController.usesNightStyleWhileInTunnel = false
449
+ navigationViewController.automaticallyAdjustsStyleForTimeOfDay = false
450
+
451
+ let navigationMapView = navigationViewController.navigationMapView
452
+ navigationMapView!.puckType = .puck2D(.navigationDefault)
453
+
454
+ if(initialLocation != nil && newNavigationControllerRequired){
455
+ navigationMapView!.mapView.mapboxMap.setCamera(to: CameraOptions(center: initialLocation!, zoom: initialLocationZoom ?? 15))
456
+ }
457
+
458
+ let style = currentMapStyle != nil ? StyleURI(rawValue: currentMapStyle!) : StyleURI.streets
459
+ navigationMapView!.mapView.mapboxMap.loadStyle(style!, completion: { _ in
460
+ navigationMapView!.localizeLabels(locale: self.currentLocale)
461
+ do{
462
+ try navigationMapView!.mapView.mapboxMap.localizeLabels(into: self.currentLocale)
463
+ } catch {}
464
+ self.addCustomRasterLayer()
465
+ })
466
+
467
+
468
+ let cancelButton = navigationViewController.navigationView.bottomBannerContainerView.findViews(subclassOf: CancelButton.self)[0]
469
+ cancelButton.addTarget(self, action: #selector(cancelButtonClicked), for: .touchUpInside)
470
+
471
+ navigationViewController.delegate = self
472
+ addChild(navigationViewController)
473
+ view.addSubview(navigationViewController.view)
474
+ navigationViewController.view.translatesAutoresizingMaskIntoConstraints = false
475
+ NSLayoutConstraint.activate([
476
+ navigationViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0),
477
+ navigationViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0),
478
+ navigationViewController.view.topAnchor.constraint(equalTo: view.topAnchor, constant: 0),
479
+ navigationViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0),
480
+ ])
481
+ didMove(toParent: self)
482
+ mapboxNavigation!.tripSession().startActiveGuidance(with: navigationRoutes, startLegIndex: 0)
483
+ }
484
+ }
485
+ extension ExpoMapboxNavigationViewController: NavigationViewControllerDelegate {
486
+ func navigationViewController(_ navigationViewController: NavigationViewController, didRerouteAlong route: Route) {
487
+ onRoutesLoaded?([
488
+ "routes": [
489
+ "mainRoute": convertRoute(route: route),
490
+ "alternativeRoutes": []
491
+ ]
492
+ ])
493
+ }
494
+
495
+ func navigationViewControllerDidDismiss(
496
+ _ navigationViewController: NavigationViewController,
497
+ byCanceling canceled: Bool
498
+ ) { }
499
+ }
500
+
501
+ extension UIView {
502
+ func findViews<T: UIView>(subclassOf: T.Type) -> [T] {
503
+ return recursiveSubviews.compactMap { $0 as? T }
504
+ }
505
+
506
+ var recursiveSubviews: [UIView] {
507
+ return subviews + subviews.flatMap { $0.recursiveSubviews }
508
+ }
509
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@sargsyansevak/expo-mapbox-navigation",
3
+ "version": "1.0.0",
4
+ "description": "Expo module for Mapbox's Navigation SDK",
5
+ "readmeFilename": "README.md",
6
+ "main": "build/index.js",
7
+ "types": "build/index.d.ts",
8
+ "scripts": {
9
+ "build": "expo-module build",
10
+ "clean": "expo-module clean",
11
+ "lint": "expo-module lint",
12
+ "test": "expo-module test",
13
+ "prepare": "expo-module prepare",
14
+ "prepublishOnly": "expo-module prepublishOnly",
15
+ "expo-module": "expo-module",
16
+ "open:ios": "xed example/ios",
17
+ "open:android": "open -a \"Android Studio\" example/android"
18
+ },
19
+ "keywords": [
20
+ "react-native",
21
+ "expo",
22
+ "mapbox",
23
+ "navigation",
24
+ "expo-mapbox-navigation",
25
+ "ExpoMapboxNavigation"
26
+ ],
27
+ "repository": "https://github.com/SargsyanSevak/expo-mapbox-navigation",
28
+ "bugs": {
29
+ "url": "https://github.com/SargsyanSevak/expo-mapbox-navigation/issues"
30
+ },
31
+ "author": "sargsyansevak <thesevaksargsyan@gmail.com> (https://github.com/SargsyanSevak",
32
+ "license": "MIT",
33
+ "homepage": "https://github.com/SargsyanSevak/expo-mapbox-navigation#readme",
34
+ "dependencies": {},
35
+ "devDependencies": {
36
+ "@types/react": "^18.0.25",
37
+ "expo-module-scripts": "^3.5.2",
38
+ "expo-modules-core": "~2.5.0",
39
+ "@expo/config-plugins": "~8.0.10"
40
+ },
41
+ "peerDependencies": {
42
+ "expo": "*",
43
+ "react": "*",
44
+ "react-native": "*",
45
+ "@rnmapbox/maps": "*"
46
+ }
47
+ }
@@ -0,0 +1,127 @@
1
+ const {
2
+ withDangerousMod,
3
+ withAndroidColors,
4
+ withAndroidColorsNight,
5
+ AndroidConfig,
6
+ } = require("@expo/config-plugins");
7
+ const fs = require("fs/promises");
8
+ const path = require("path");
9
+
10
+ /**
11
+ * This plugin adds a post-install step to the Podfile that addresses linking issues between pods and xcframeworks.
12
+ * In this case, the navigation library is added as an xcframework, and the maps library is added as a pod
13
+ *
14
+ * See https://github.com/CocoaPods/CocoaPods/issues/11079#issuecomment-984670700
15
+ */
16
+
17
+ const applyPodfilePostInstallModifications = (src, mapboxMapsVersion) => {
18
+ return (
19
+ `ENV['ExpoNavigationMapboxMapsVersion'] = '${mapboxMapsVersion}'\n` +
20
+ src.replace(
21
+ "post_install do |installer|",
22
+ `post_install do |installer|
23
+ installer.pods_project.targets.each do |target|
24
+ if (target.name.include? 'MapboxMaps' or target.name.include? 'Turf')
25
+ target.build_configurations.each do |config|
26
+ config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
27
+ end
28
+ end
29
+ end`
30
+ )
31
+ );
32
+ };
33
+
34
+ const withIosPostInstallStep = (config, mapboxMapsVersion) =>
35
+ withDangerousMod(config, [
36
+ "ios",
37
+ async (exportedConfig) => {
38
+ const file = path.join(
39
+ exportedConfig.modRequest.platformProjectRoot,
40
+ "Podfile"
41
+ );
42
+
43
+ const contents = await fs.readFile(file, "utf8");
44
+ await fs.writeFile(
45
+ file,
46
+ applyPodfilePostInstallModifications(contents, mapboxMapsVersion),
47
+ "utf-8"
48
+ );
49
+
50
+ return exportedConfig;
51
+ },
52
+ ]);
53
+
54
+ const withIosTokenInfoPlist = (config, accessToken) => {
55
+ if (!config.ios) {
56
+ config.ios = {};
57
+ }
58
+ if (!config.ios.infoPlist) {
59
+ config.ios.infoPlist = {};
60
+ }
61
+
62
+ config.ios.infoPlist["MBXAccessToken"] = accessToken;
63
+
64
+ return config;
65
+ };
66
+
67
+ const withIosConfig = (config, { accessToken, mapboxMapsVersion }) => {
68
+ const configWithPostInstallStep = withIosPostInstallStep(
69
+ config,
70
+ mapboxMapsVersion
71
+ );
72
+ const configWithAccessToken = withIosTokenInfoPlist(
73
+ configWithPostInstallStep,
74
+ accessToken
75
+ );
76
+ return configWithAccessToken;
77
+ };
78
+
79
+ const withAndroidConfig = (config, { androidColorOverrides }) => {
80
+ const configWithAndroidColors = withAndroidColors(config, (config) => {
81
+ let currentModResults = config.modResults;
82
+
83
+ for (const [name, value] of Object.entries(androidColorOverrides ?? {})) {
84
+ AndroidConfig.Colors.assignColorValue(currentModResults, { name, value });
85
+ }
86
+
87
+ config.modResults = currentModResults;
88
+
89
+ return config;
90
+ });
91
+
92
+ const configWithAndroidColorsNight = withAndroidColorsNight(
93
+ configWithAndroidColors,
94
+ (config) => {
95
+ let currentModResults = config.modResults;
96
+
97
+ for (const [name, value] of Object.entries(androidColorOverrides ?? {})) {
98
+ AndroidConfig.Colors.assignColorValue(currentModResults, {
99
+ name,
100
+ value,
101
+ });
102
+ }
103
+
104
+ config.modResults = currentModResults;
105
+
106
+ return config;
107
+ }
108
+ );
109
+
110
+ return configWithAndroidColorsNight;
111
+ };
112
+
113
+ const withConfig = (
114
+ config,
115
+ { accessToken, mapboxMapsVersion, androidColorOverrides }
116
+ ) => {
117
+ const configWithIos = withIosConfig(config, {
118
+ accessToken,
119
+ mapboxMapsVersion,
120
+ });
121
+ const configWithAndroid = withAndroidConfig(configWithIos, {
122
+ androidColorOverrides,
123
+ });
124
+ return configWithAndroid;
125
+ };
126
+
127
+ module.exports = withConfig;