@jacques_gordon/expo-mapbox-navigation 1.0.8 → 2.0.1

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 (36) hide show
  1. package/ExpoMapboxNavigation.podspec +39 -0
  2. package/LICENSE +21 -0
  3. package/README.md +103 -209
  4. package/android/build.gradle +101 -39
  5. package/android/src/main/java/expo/modules/mapboxnavigation/ExpoMapboxNavigationModule.kt +81 -77
  6. package/android/src/main/java/expo/modules/mapboxnavigation/ExpoMapboxNavigationView.kt +260 -142
  7. package/app.plugin.js +216 -1
  8. package/build/index.d.ts +121 -2
  9. package/build/index.js +34 -3
  10. package/build/src/index.d.ts +121 -0
  11. package/build/src/index.js +39 -0
  12. package/package.json +37 -37
  13. package/plugin/src/index.js +216 -0
  14. package/src/index.tsx +162 -0
  15. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  16. package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
  17. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  18. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  19. package/android/.gradle/8.9/gc.properties +0 -0
  20. package/android/.gradle/9.2.0/checksums/checksums.lock +0 -0
  21. package/android/.gradle/9.2.0/fileChanges/last-build.bin +0 -0
  22. package/android/.gradle/9.2.0/fileHashes/fileHashes.bin +0 -0
  23. package/android/.gradle/9.2.0/fileHashes/fileHashes.lock +0 -0
  24. package/android/.gradle/9.2.0/gc.properties +0 -0
  25. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  26. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  27. package/android/.gradle/vcs-1/gc.properties +0 -0
  28. package/build/MapboxNavigation.types.d.ts +0 -132
  29. package/build/MapboxNavigation.types.js +0 -2
  30. package/build/MapboxNavigationView.d.ts +0 -30
  31. package/build/MapboxNavigationView.js +0 -41
  32. package/expo-module.config.json +0 -9
  33. package/ios/ExpoMapboxNavigation.podspec +0 -25
  34. package/ios/ExpoMapboxNavigationModule.swift +0 -81
  35. package/ios/ExpoMapboxNavigationView.swift +0 -236
  36. package/plugin/index.js +0 -167
@@ -1,81 +0,0 @@
1
- import ExpoModulesCore
2
-
3
- public class ExpoMapboxNavigationModule: Module {
4
- public func definition() -> ModuleDefinition {
5
- Name("ExpoMapboxNavigation")
6
-
7
- View(ExpoMapboxNavigationView.self) {
8
-
9
- // ─── Props ────────────────────────────────────────────────────────
10
-
11
- Prop("coordinates") { (view: ExpoMapboxNavigationView, value: [[String: Double]]) in
12
- view.coordinates = value
13
- view.startNavigationIfReady()
14
- }
15
-
16
- Prop("waypointIndices") { (view: ExpoMapboxNavigationView, value: [Int]) in
17
- view.waypointIndices = value
18
- }
19
-
20
- Prop("useRouteMatchingApi") { (view: ExpoMapboxNavigationView, value: Bool) in
21
- view.useRouteMatchingApi = value
22
- }
23
-
24
- Prop("locale") { (view: ExpoMapboxNavigationView, value: String) in
25
- view.locale = value
26
- }
27
-
28
- Prop("routeProfile") { (view: ExpoMapboxNavigationView, value: String) in
29
- view.routeProfile = value
30
- }
31
-
32
- Prop("routeExcludeList") { (view: ExpoMapboxNavigationView, value: [String]) in
33
- view.routeExcludeList = value
34
- }
35
-
36
- Prop("mapStyle") { (view: ExpoMapboxNavigationView, value: String) in
37
- view.mapStyle = value
38
- }
39
-
40
- Prop("mute") { (view: ExpoMapboxNavigationView, value: Bool) in
41
- view.mute = value
42
- }
43
-
44
- Prop("vehicleMaxHeight") { (view: ExpoMapboxNavigationView, value: Double) in
45
- view.vehicleMaxHeight = value
46
- }
47
-
48
- Prop("vehicleMaxWidth") { (view: ExpoMapboxNavigationView, value: Double) in
49
- view.vehicleMaxWidth = value
50
- }
51
-
52
- Prop("customRasterSourceUrl") { (view: ExpoMapboxNavigationView, value: String) in
53
- view.customRasterSourceUrl = value
54
- }
55
-
56
- Prop("placeCustomRasterLayerAbove") { (view: ExpoMapboxNavigationView, value: String) in
57
- view.placeCustomRasterLayerAbove = value
58
- }
59
-
60
- Prop("disableAlternativeRoutes") { (view: ExpoMapboxNavigationView, value: Bool) in
61
- view.disableAlternativeRoutes = value
62
- }
63
-
64
- Prop("showsEndOfRouteFeedback") { (view: ExpoMapboxNavigationView, value: Bool) in
65
- view.showsEndOfRouteFeedback = value
66
- }
67
-
68
- // ─── Events ───────────────────────────────────────────────────────
69
-
70
- Events(
71
- "onRouteProgressChanged",
72
- "onWaypointArrival",
73
- "onFinalDestinationArrival",
74
- "onCancelNavigation",
75
- "onRouteChanged",
76
- "onUserOffRoute",
77
- "onRoutesLoaded"
78
- )
79
- }
80
- }
81
- }
@@ -1,236 +0,0 @@
1
- import ExpoModulesCore
2
- import MapboxNavigationCore
3
- import MapboxNavigationUIKit
4
- import MapboxMaps
5
- import MapboxDirections
6
-
7
- class ExpoMapboxNavigationView: ExpoView {
8
-
9
- // ─── Props ──────────────────────────────────────────────────────────────
10
- var coordinates: [[String: Double]] = []
11
- var waypointIndices: [Int] = []
12
- var useRouteMatchingApi: Bool = false
13
- var locale: String = ""
14
- var routeProfile: String = "mapbox/driving-traffic"
15
- var routeExcludeList: [String] = []
16
- var mapStyle: String? = nil
17
- var mute: Bool = false
18
- var vehicleMaxHeight: Double? = nil
19
- var vehicleMaxWidth: Double? = nil
20
- var customRasterSourceUrl: String? = nil
21
- var placeCustomRasterLayerAbove: String? = nil
22
- var disableAlternativeRoutes: Bool = false
23
- var showsEndOfRouteFeedback: Bool = false
24
-
25
- // ─── Events ─────────────────────────────────────────────────────────────
26
- let onRouteProgressChanged = EventDispatcher()
27
- let onWaypointArrival = EventDispatcher()
28
- let onFinalDestinationArrival = EventDispatcher()
29
- let onCancelNavigation = EventDispatcher()
30
- let onRouteChanged = EventDispatcher()
31
- let onUserOffRoute = EventDispatcher()
32
- let onRoutesLoaded = EventDispatcher()
33
-
34
- // ─── Internal ───────────────────────────────────────────────────────────
35
- private var navigationViewController: NavigationViewController?
36
- private var isNavigationStarted = false
37
- private var currentWaypointIndex = 0
38
- private var totalWaypoints = 0
39
-
40
- required init(appContext: AppContext? = nil) {
41
- super.init(appContext: appContext)
42
- }
43
-
44
- func startNavigationIfReady() {
45
- guard coordinates.count >= 2, !isNavigationStarted else { return }
46
- isNavigationStarted = true
47
- setupNavigation()
48
- }
49
-
50
- override func layoutSubviews() {
51
- super.layoutSubviews()
52
- startNavigationIfReady()
53
- }
54
-
55
- // ─── Navigation Setup ───────────────────────────────────────────────────
56
-
57
- private func setupNavigation() {
58
- // Construire les waypoints
59
- var allWaypoints: [Waypoint] = []
60
-
61
- for (index, coord) in coordinates.enumerated() {
62
- guard let lat = coord["latitude"], let lng = coord["longitude"] else { continue }
63
- let location = CLLocationCoordinate2D(latitude: lat, longitude: lng)
64
- let wp = Waypoint(coordinate: location)
65
-
66
- // Marquer les waypoints intermédiaires vs simples pass-through
67
- if !waypointIndices.isEmpty {
68
- wp.separatesLegs = waypointIndices.contains(index)
69
- } else {
70
- wp.separatesLegs = true
71
- }
72
-
73
- allWaypoints.append(wp)
74
- }
75
-
76
- totalWaypoints = allWaypoints.filter { $0.separatesLegs }.count
77
-
78
- // Choisir le profil de routing
79
- let profile: ProfileIdentifier
80
- switch routeProfile {
81
- case "mapbox/driving": profile = .automobile
82
- case "mapbox/walking": profile = .walking
83
- case "mapbox/cycling": profile = .cycling
84
- default: profile = .automobileAvoidingTraffic
85
- }
86
-
87
- // Options de route
88
- let routeOptions = NavigationRouteOptions(waypoints: allWaypoints, profileIdentifier: profile)
89
-
90
- let resolvedLocale = locale.isEmpty ? Locale.current.identifier : locale
91
- routeOptions.locale = Locale(identifier: resolvedLocale)
92
- routeOptions.includesSpokenInstructions = true
93
- routeOptions.includesVisualInstructions = true
94
-
95
- // Exclusions
96
- if !routeExcludeList.isEmpty {
97
- routeOptions.roadClassesToAvoid = buildRoadClassesToAvoid(routeExcludeList)
98
- }
99
-
100
- // Restrictions de véhicule
101
- if let maxH = vehicleMaxHeight {
102
- routeOptions.maximumHeight = Measurement(value: maxH, unit: .meters)
103
- }
104
- if let maxW = vehicleMaxWidth {
105
- routeOptions.maximumWidth = Measurement(value: maxW, unit: .meters)
106
- }
107
-
108
- if disableAlternativeRoutes {
109
- routeOptions.includesAlternativeRoutes = false
110
- }
111
-
112
- // Calcul de la route
113
- Directions.shared.calculate(routeOptions) { [weak self] (_, result) in
114
- guard let self = self else { return }
115
- switch result {
116
- case .failure(let error):
117
- print("[ExpoMapboxNavigation] Erreur calcul route: \(error.localizedDescription)")
118
- case .success(let response):
119
- guard let routes = response.routes, !routes.isEmpty else { return }
120
- self.onRoutesLoaded([:])
121
- self.presentNavigationUI(routes: routes, options: routeOptions)
122
- }
123
- }
124
- }
125
-
126
- private func presentNavigationUI(routes: [Route], options: NavigationRouteOptions) {
127
- DispatchQueue.main.async { [weak self] in
128
- guard let self = self else { return }
129
-
130
- let navigationService = MapboxNavigationService(
131
- routes: routes,
132
- customRoutingProvider: NavigationSettings.shared.directions,
133
- credentials: NavigationSettings.shared.directions.credentials,
134
- simulating: .onPoorGPS
135
- )
136
-
137
- let navigationOptions = NavigationOptions(navigationService: navigationService)
138
- let navVC = NavigationViewController(
139
- for: routes,
140
- routeIndex: 0,
141
- routeOptions: options,
142
- navigationOptions: navigationOptions
143
- )
144
-
145
- navVC.delegate = self
146
- navVC.showsEndOfRouteFeedback = self.showsEndOfRouteFeedback
147
- navVC.showsReportFeedback = false
148
-
149
- if self.mute {
150
- navVC.navigationService.router.reroutesProactively = false
151
- }
152
-
153
- self.navigationViewController = navVC
154
-
155
- guard let parentVC = self.parentViewController else { return }
156
- parentVC.addChild(navVC)
157
- navVC.view.frame = self.bounds
158
- navVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
159
- self.addSubview(navVC.view)
160
- navVC.didMove(toParent: parentVC)
161
- }
162
- }
163
-
164
- private func buildRoadClassesToAvoid(_ list: [String]) -> RoadClasses {
165
- var classes = RoadClasses()
166
- for item in list {
167
- switch item {
168
- case "toll": classes.insert(.toll)
169
- case "ferry": classes.insert(.ferry)
170
- case "motorway": classes.insert(.motorway)
171
- default: break
172
- }
173
- }
174
- return classes
175
- }
176
-
177
- private var parentViewController: UIViewController? {
178
- var responder: UIResponder? = self
179
- while let r = responder {
180
- if let vc = r as? UIViewController { return vc }
181
- responder = r.next
182
- }
183
- return nil
184
- }
185
- }
186
-
187
- // ─── NavigationViewControllerDelegate ───────────────────────────────────────
188
-
189
- extension ExpoMapboxNavigationView: NavigationViewControllerDelegate {
190
-
191
- func navigationViewController(
192
- _ vc: NavigationViewController,
193
- didUpdate progress: RouteProgress,
194
- with location: CLLocation,
195
- rawLocation: CLLocation
196
- ) {
197
- onRouteProgressChanged([
198
- "distanceRemaining": progress.currentLegProgress.distanceRemaining,
199
- "distanceTraveled": progress.distanceTraveled,
200
- "durationRemaining": progress.durationRemaining,
201
- "fractionTraveled": progress.fractionTraveled
202
- ])
203
-
204
- // Détection hors route
205
- if progress.currentLegProgress.userHasArrivedAtWaypoint {
206
- currentWaypointIndex += 1
207
- if currentWaypointIndex < totalWaypoints - 1 {
208
- onWaypointArrival([
209
- "distanceRemaining": progress.currentLegProgress.distanceRemaining,
210
- "distanceTraveled": progress.distanceTraveled,
211
- "durationRemaining": progress.durationRemaining,
212
- "fractionTraveled": progress.fractionTraveled
213
- ])
214
- }
215
- }
216
- }
217
-
218
- func navigationViewControllerDidFinishRouting(_ vc: NavigationViewController) {
219
- onFinalDestinationArrival([:])
220
- }
221
-
222
- func navigationViewControllerDidDismiss(_ vc: NavigationViewController, byCanceling canceled: Bool) {
223
- if canceled {
224
- onCancelNavigation([:])
225
- }
226
- }
227
-
228
- func navigationViewController(_ vc: NavigationViewController, didRerouteAlong route: Route) {
229
- onRouteChanged([:])
230
- }
231
-
232
- func navigationViewController(_ vc: NavigationViewController, shouldRerouteFrom location: CLLocation) -> Bool {
233
- onUserOffRoute([:])
234
- return true
235
- }
236
- }
package/plugin/index.js DELETED
@@ -1,167 +0,0 @@
1
- const {
2
- withInfoPlist,
3
- withAppBuildGradle,
4
- withProjectBuildGradle,
5
- withAndroidManifest,
6
- withDangerousMod,
7
- createRunOncePlugin,
8
- } = require('@expo/config-plugins');
9
-
10
- const fs = require('fs');
11
- const path = require('path');
12
-
13
- // ─── Mapbox Maven block ────────────────────────────────────────────────
14
-
15
- const MAPBOX_MAVEN_BLOCK = `
16
- maven {
17
- url 'https://api.mapbox.com/downloads/v2/releases/maven'
18
- authentication { basic(BasicAuthentication) }
19
- credentials {
20
- username = "mapbox"
21
- password = project.properties['MAPBOX_DOWNLOADS_TOKEN'] ?: System.getenv("MAPBOX_DOWNLOADS_TOKEN") ?: ""
22
- }
23
- }
24
- `;
25
-
26
- // ─── Inject into settings.gradle (SDK 53 FIX) ───────────────────────────
27
-
28
- const withMapboxMavenInSettings = (config) => {
29
- return withDangerousMod(config, [
30
- 'android',
31
- async (cfg) => {
32
- const settingsPath = path.join(
33
- cfg.modRequest.platformProjectRoot,
34
- 'settings.gradle'
35
- );
36
-
37
- if (!fs.existsSync(settingsPath)) return cfg;
38
-
39
- let contents = fs.readFileSync(settingsPath, 'utf8');
40
-
41
- if (contents.includes('api.mapbox.com/downloads')) return cfg;
42
-
43
- if (contents.includes('dependencyResolutionManagement')) {
44
- contents = contents.replace(
45
- /dependencyResolutionManagement\s*\{([\s\S]*?)repositories\s*\{/,
46
- `dependencyResolutionManagement {$1repositories {${MAPBOX_MAVEN_BLOCK}`
47
- );
48
- } else if (contents.includes('allprojects')) {
49
- contents = contents.replace(
50
- /allprojects\s*\{([\s\S]*?)repositories\s*\{/,
51
- `allprojects {$1repositories {${MAPBOX_MAVEN_BLOCK}`
52
- );
53
- } else {
54
- contents += `
55
- allprojects {
56
- repositories {${MAPBOX_MAVEN_BLOCK}
57
- }
58
- }`;
59
- }
60
-
61
- fs.writeFileSync(settingsPath, contents);
62
- return cfg;
63
- },
64
- ]);
65
- };
66
-
67
- // ─── Plugin ─────────────────────────────────────────────────────────────
68
-
69
- const withExpoMapboxNavigation = (config, options) => {
70
- const {
71
- accessToken,
72
- mapboxMapsVersion = '11.11.0',
73
- androidColorOverrides = {},
74
- } = options;
75
-
76
- if (!accessToken) {
77
- throw new Error('[expo-mapbox-navigation] accessToken is required');
78
- }
79
-
80
- // ── iOS ───────────────────────────────────────────────────────────────
81
- config = withInfoPlist(config, (cfg) => {
82
- cfg.modResults.MBXAccessToken = accessToken;
83
-
84
- cfg.modResults.NSLocationWhenInUseUsageDescription =
85
- cfg.modResults.NSLocationWhenInUseUsageDescription ??
86
- 'Navigation GPS requires location access.';
87
-
88
- cfg.modResults.NSLocationAlwaysAndWhenInUseUsageDescription =
89
- cfg.modResults.NSLocationAlwaysAndWhenInUseUsageDescription ??
90
- 'Background navigation requires location access.';
91
-
92
- cfg.modResults.NSMotionUsageDescription =
93
- cfg.modResults.NSMotionUsageDescription ??
94
- 'Motion is used to improve navigation experience.';
95
-
96
- const modes = cfg.modResults.UIBackgroundModes || [];
97
- if (!modes.includes('audio')) modes.push('audio');
98
- if (!modes.includes('location')) modes.push('location');
99
- cfg.modResults.UIBackgroundModes = modes;
100
-
101
- return cfg;
102
- });
103
-
104
- // ── Android settings.gradle FIX (IMPORTANT SDK 53) ────────────────────
105
- config = withMapboxMavenInSettings(config);
106
-
107
- // ── Android Manifest ──────────────────────────────────────────────────
108
- config = withAndroidManifest(config, (cfg) => {
109
- const manifest = cfg.modResults.manifest;
110
- const permissions = manifest['uses-permission'] || [];
111
-
112
- const add = (name) => {
113
- if (!permissions.find((p) => p.$?.['android:name'] === name)) {
114
- permissions.push({ $: { 'android:name': name } });
115
- }
116
- };
117
-
118
- add('android.permission.ACCESS_FINE_LOCATION');
119
- add('android.permission.ACCESS_COARSE_LOCATION');
120
- add('android.permission.FOREGROUND_SERVICE');
121
- add('android.permission.FOREGROUND_SERVICE_LOCATION');
122
-
123
- manifest['uses-permission'] = permissions;
124
-
125
- const app = manifest.application?.[0];
126
- if (app) {
127
- const meta = app['meta-data'] || [];
128
-
129
- if (!meta.find((m) => m.$?.['android:name'] === 'com.mapbox.token')) {
130
- meta.push({
131
- $: {
132
- 'android:name': 'com.mapbox.token',
133
- 'android:value': accessToken,
134
- },
135
- });
136
- }
137
-
138
- app['meta-data'] = meta;
139
- }
140
-
141
- return cfg;
142
- });
143
-
144
- // ── App build.gradle ──────────────────────────────────────────────────
145
- config = withAppBuildGradle(config, (cfg) => {
146
- if (!cfg.modResults.contents.includes('mapbox_access_token')) {
147
- cfg.modResults.contents = cfg.modResults.contents.replace(
148
- /defaultConfig\s*\{/,
149
- `defaultConfig {
150
- resValue "string", "mapbox_access_token", "${accessToken}"`
151
- );
152
- }
153
-
154
- return cfg;
155
- });
156
-
157
- return config;
158
- };
159
-
160
- const plugin = createRunOncePlugin(
161
- withExpoMapboxNavigation,
162
- "expo-mapbox-navigation",
163
- "1.0.8"
164
- );
165
-
166
- module.exports = plugin;
167
- module.exports.default = plugin;