@jadamsbit/react-native-mapbox-navigation 1.0.18 → 1.0.20
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/.vscode/settings.json +3 -0
- package/.yarn/releases/yarn-3.6.1.cjs +0 -0
- package/android/.classpath +6 -0
- package/android/.project +34 -0
- package/android/.settings/org.eclipse.buildship.core.prefs +13 -0
- package/android/.settings/org.eclipse.jdt.core.prefs +4 -0
- package/android/build.gradle +1 -1
- package/android/gradlew +0 -0
- package/android/src/main/java/com/jadamsbit/mapboxnavigation/MapboxNavigationModule.kt +47 -22
- package/android/src/main/java/com/jadamsbit/mapboxnavigation/MapboxNavigationView.kt +268 -406
- package/android/src/main/res/layout/navigation_view.xml +16 -6
- package/package.json +2 -2
- package/react-native-mapbox-navigation.podspec +1 -1
- package/.github/actions/setup/action.yml +0 -27
- package/.github/workflows/ci.yml +0 -154
- package/example/.buckconfig +0 -6
- package/example/.bundle/config +0 -2
- package/example/.eslintrc.js +0 -4
- package/example/.flowconfig +0 -65
- package/example/.prettierrc.js +0 -7
- package/example/.watchmanconfig +0 -1
- package/example/Gemfile +0 -9
- package/example/NavigationComponent.js +0 -55
- package/example/README.md +0 -79
- package/example/android/app/build.gradle +0 -119
- package/example/android/app/debug.keystore +0 -0
- package/example/android/app/proguard-rules.pro +0 -10
- package/example/android/app/src/debug/AndroidManifest.xml +0 -9
- package/example/android/app/src/main/AndroidManifest.xml +0 -26
- package/example/android/app/src/main/java/com/mapboxnavigationexample/MainActivity.kt +0 -22
- package/example/android/app/src/main/java/com/mapboxnavigationexample/MainApplication.kt +0 -45
- package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +0 -36
- package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/values/strings.xml +0 -3
- package/example/android/app/src/main/res/values/styles.xml +0 -9
- package/example/android/build.gradle +0 -39
- package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/example/android/gradle/wrapper/gradle-wrapper.properties +0 -7
- package/example/android/gradle.properties +0 -46
- package/example/android/gradlew +0 -249
- package/example/android/gradlew.bat +0 -92
- package/example/android/settings.gradle +0 -6
- package/example/app.json +0 -4
- package/example/babel.config.js +0 -17
- package/example/index.js +0 -5
- package/example/ios/.xcode.env +0 -1
- package/example/ios/.xcode.env.local +0 -2
- package/example/ios/BasicApp/AppDelegate.h +0 -8
- package/example/ios/BasicApp/AppDelegate.m +0 -62
- package/example/ios/BasicApp/Images.xcassets/AppIcon.appiconset/Contents.json +0 -38
- package/example/ios/BasicApp/Images.xcassets/Contents.json +0 -6
- package/example/ios/BasicApp/Info.plist +0 -62
- package/example/ios/BasicApp/LaunchScreen.storyboard +0 -47
- package/example/ios/BasicApp/main.m +0 -9
- package/example/ios/BasicApp-Bridging-Header.h +0 -4
- package/example/ios/BasicApp.xcodeproj/project.pbxproj +0 -716
- package/example/ios/BasicApp.xcodeproj/xcshareddata/xcschemes/BasicApp.xcscheme +0 -88
- package/example/ios/BasicApp.xcworkspace/contents.xcworkspacedata +0 -10
- package/example/ios/BasicApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/example/ios/BasicAppTests/BasicAppTests.m +0 -65
- package/example/ios/BasicAppTests/Info.plist +0 -24
- package/example/ios/BridgeHeader.swift +0 -8
- package/example/ios/Podfile +0 -67
- package/example/ios/Podfile.lock +0 -1431
- package/example/jest.config.js +0 -3
- package/example/metro.config.js +0 -45
- package/example/package.json +0 -28
- package/example/react-native.config.js +0 -10
- package/example/src/App.js +0 -51
- package/lefthook.yml +0 -35
|
@@ -12,12 +12,18 @@ import android.widget.Toast
|
|
|
12
12
|
import androidx.core.content.ContextCompat
|
|
13
13
|
import com.facebook.react.bridge.Arguments
|
|
14
14
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
15
|
+
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
16
|
+
import com.jadamsbit.mapboxnavigation.databinding.NavigationViewBinding
|
|
17
|
+
import com.mapbox.api.directions.v5.DirectionsCriteria
|
|
15
18
|
import com.mapbox.api.directions.v5.models.DirectionsRoute
|
|
16
19
|
import com.mapbox.api.directions.v5.models.RouteOptions
|
|
17
20
|
import com.mapbox.bindgen.Expected
|
|
21
|
+
import com.mapbox.geojson.Feature
|
|
22
|
+
import com.mapbox.geojson.FeatureCollection
|
|
23
|
+
import com.mapbox.geojson.LineString
|
|
18
24
|
import com.mapbox.geojson.Point
|
|
25
|
+
import com.mapbox.geojson.utils.PolylineUtils
|
|
19
26
|
import com.mapbox.maps.EdgeInsets
|
|
20
|
-
import com.mapbox.maps.MapView
|
|
21
27
|
import com.mapbox.maps.MapboxMap
|
|
22
28
|
import com.mapbox.maps.Style
|
|
23
29
|
import com.mapbox.maps.plugin.LocationPuck2D
|
|
@@ -30,9 +36,11 @@ import com.mapbox.navigation.base.options.NavigationOptions
|
|
|
30
36
|
import com.mapbox.navigation.base.route.RouterCallback
|
|
31
37
|
import com.mapbox.navigation.base.route.RouterFailure
|
|
32
38
|
import com.mapbox.navigation.base.route.RouterOrigin
|
|
39
|
+
import com.mapbox.navigation.base.trip.model.RouteLegProgress
|
|
40
|
+
import com.mapbox.navigation.base.trip.model.RouteProgress
|
|
33
41
|
import com.mapbox.navigation.core.MapboxNavigation
|
|
34
42
|
import com.mapbox.navigation.core.MapboxNavigationProvider
|
|
35
|
-
import com.mapbox.navigation.core.
|
|
43
|
+
import com.mapbox.navigation.core.arrival.ArrivalObserver
|
|
36
44
|
import com.mapbox.navigation.core.formatter.MapboxDistanceFormatter
|
|
37
45
|
import com.mapbox.navigation.core.replay.MapboxReplayer
|
|
38
46
|
import com.mapbox.navigation.core.replay.ReplayLocationEngine
|
|
@@ -42,14 +50,9 @@ import com.mapbox.navigation.core.trip.session.LocationMatcherResult
|
|
|
42
50
|
import com.mapbox.navigation.core.trip.session.LocationObserver
|
|
43
51
|
import com.mapbox.navigation.core.trip.session.RouteProgressObserver
|
|
44
52
|
import com.mapbox.navigation.core.trip.session.VoiceInstructionsObserver
|
|
45
|
-
import com.
|
|
46
|
-
import com.mapbox.api.directions.v5.DirectionsCriteria
|
|
47
|
-
import com.mapbox.navigation.base.trip.model.RouteLegProgress
|
|
48
|
-
import com.mapbox.navigation.base.trip.model.RouteProgress
|
|
49
|
-
import com.mapbox.navigation.core.arrival.ArrivalObserver
|
|
53
|
+
import com.mapbox.navigation.core.directions.session.RoutesObserver
|
|
50
54
|
import com.mapbox.navigation.ui.base.util.MapboxNavigationConsumer
|
|
51
55
|
import com.mapbox.navigation.ui.maneuver.api.MapboxManeuverApi
|
|
52
|
-
import com.mapbox.navigation.ui.maneuver.view.MapboxManeuverView
|
|
53
56
|
import com.mapbox.navigation.ui.maps.camera.NavigationCamera
|
|
54
57
|
import com.mapbox.navigation.ui.maps.camera.data.MapboxNavigationViewportDataSource
|
|
55
58
|
import com.mapbox.navigation.ui.maps.camera.lifecycle.NavigationBasicGesturesHandler
|
|
@@ -59,229 +62,126 @@ import com.mapbox.navigation.ui.maps.location.NavigationLocationProvider
|
|
|
59
62
|
import com.mapbox.navigation.ui.maps.route.arrow.api.MapboxRouteArrowApi
|
|
60
63
|
import com.mapbox.navigation.ui.maps.route.arrow.api.MapboxRouteArrowView
|
|
61
64
|
import com.mapbox.navigation.ui.maps.route.arrow.model.RouteArrowOptions
|
|
62
|
-
import com.mapbox.navigation.ui.maps.route.line.api.MapboxRouteLineApi
|
|
63
|
-
import com.mapbox.navigation.ui.maps.route.line.api.MapboxRouteLineView
|
|
64
|
-
import com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineOptions
|
|
65
|
-
import com.mapbox.navigation.ui.maps.route.line.model.RouteLine
|
|
66
65
|
import com.mapbox.navigation.ui.tripprogress.api.MapboxTripProgressApi
|
|
67
66
|
import com.mapbox.navigation.ui.tripprogress.model.DistanceRemainingFormatter
|
|
68
67
|
import com.mapbox.navigation.ui.tripprogress.model.EstimatedTimeToArrivalFormatter
|
|
69
68
|
import com.mapbox.navigation.ui.tripprogress.model.PercentDistanceTraveledFormatter
|
|
70
69
|
import com.mapbox.navigation.ui.tripprogress.model.TimeRemainingFormatter
|
|
71
70
|
import com.mapbox.navigation.ui.tripprogress.model.TripProgressUpdateFormatter
|
|
72
|
-
import com.mapbox.navigation.ui.tripprogress.view.MapboxTripProgressView
|
|
73
71
|
import com.mapbox.navigation.ui.voice.api.MapboxSpeechApi
|
|
74
72
|
import com.mapbox.navigation.ui.voice.api.MapboxVoiceInstructionsPlayer
|
|
75
73
|
import com.mapbox.navigation.ui.voice.model.SpeechAnnouncement
|
|
76
74
|
import com.mapbox.navigation.ui.voice.model.SpeechError
|
|
77
75
|
import com.mapbox.navigation.ui.voice.model.SpeechValue
|
|
78
76
|
import com.mapbox.navigation.ui.voice.model.SpeechVolume
|
|
77
|
+
import com.mapbox.maps.extension.style.layers.addLayer
|
|
78
|
+
import com.mapbox.maps.extension.style.layers.addLayerBelow
|
|
79
|
+
import com.mapbox.maps.extension.style.layers.generated.lineLayer
|
|
80
|
+
import com.mapbox.maps.extension.style.layers.properties.generated.LineCap
|
|
81
|
+
import com.mapbox.maps.extension.style.layers.properties.generated.LineJoin
|
|
82
|
+
import com.mapbox.maps.extension.style.sources.addSource
|
|
83
|
+
import com.mapbox.maps.extension.style.sources.generated.geoJsonSource
|
|
84
|
+
import com.mapbox.maps.extension.style.sources.getSourceAs
|
|
79
85
|
import java.util.Locale
|
|
80
|
-
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
81
86
|
|
|
82
|
-
class MapboxNavigationView(
|
|
83
|
-
|
|
87
|
+
class MapboxNavigationView(
|
|
88
|
+
private val reactContext: ThemedReactContext,
|
|
89
|
+
private val accessToken: String?
|
|
90
|
+
) : FrameLayout(reactContext.baseContext) {
|
|
84
91
|
|
|
85
92
|
private companion object {
|
|
86
93
|
private const val BUTTON_ANIMATION_DURATION = 1500L
|
|
94
|
+
|
|
95
|
+
// Our stable route line IDs (Maps v10)
|
|
96
|
+
private const val ROUTE_SOURCE_ID = "jadams_route_source"
|
|
97
|
+
private const val ROUTE_LAYER_ID = "jadams_route_layer"
|
|
87
98
|
}
|
|
88
99
|
|
|
89
100
|
private var origin: Point? = null
|
|
90
101
|
private var destination: Point? = null
|
|
91
102
|
private var shouldSimulateRoute = false
|
|
92
103
|
private var showsEndOfRouteFeedback = false
|
|
93
|
-
/**
|
|
94
|
-
* Debug tool used to play, pause and seek route progress events that can be used to produce mocked location updates along the route.
|
|
95
|
-
*/
|
|
96
|
-
private val mapboxReplayer = MapboxReplayer()
|
|
97
104
|
|
|
98
|
-
|
|
99
|
-
* Debug tool that mocks location updates with an input from the [mapboxReplayer].
|
|
100
|
-
*/
|
|
105
|
+
private val mapboxReplayer = MapboxReplayer()
|
|
101
106
|
private val replayLocationEngine = ReplayLocationEngine(mapboxReplayer)
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Debug observer that makes sure the replayer has always an up-to-date information to generate mock updates.
|
|
105
|
-
*/
|
|
106
107
|
private val replayProgressObserver = ReplayProgressObserver(mapboxReplayer)
|
|
107
108
|
|
|
108
|
-
/**
|
|
109
|
-
* Bindings to the example layout.
|
|
110
|
-
*/
|
|
111
109
|
private var binding: NavigationViewBinding =
|
|
112
|
-
NavigationViewBinding.inflate(LayoutInflater.from(
|
|
110
|
+
NavigationViewBinding.inflate(LayoutInflater.from(reactContext), this, true)
|
|
113
111
|
|
|
114
|
-
/**
|
|
115
|
-
* Mapbox Maps entry point obtained from the [MapView].
|
|
116
|
-
* You need to get a new reference to this object whenever the [MapView] is recreated.
|
|
117
|
-
*/
|
|
118
112
|
private lateinit var mapboxMap: MapboxMap
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Mapbox Navigation entry point. There should only be one instance of this object for the app.
|
|
122
|
-
* You can use [MapboxNavigationProvider] to help create and obtain that instance.
|
|
123
|
-
*/
|
|
124
113
|
private lateinit var mapboxNavigation: MapboxNavigation
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Used to execute camera transitions based on the data generated by the [viewportDataSource].
|
|
128
|
-
* This includes transitions from route overview to route following and continuously updating the camera as the location changes.
|
|
129
|
-
*/
|
|
130
114
|
private lateinit var navigationCamera: NavigationCamera
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Produces the camera frames based on the location and routing data for the [navigationCamera] to execute.
|
|
134
|
-
*/
|
|
135
115
|
private lateinit var viewportDataSource: MapboxNavigationViewportDataSource
|
|
136
116
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
117
|
+
private lateinit var maneuverApi: MapboxManeuverApi
|
|
118
|
+
private lateinit var tripProgressApi: MapboxTripProgressApi
|
|
119
|
+
|
|
120
|
+
// We KEEP arrows (they are not your crash)
|
|
121
|
+
private val routeArrowApi: MapboxRouteArrowApi = MapboxRouteArrowApi()
|
|
122
|
+
private lateinit var routeArrowView: MapboxRouteArrowView
|
|
123
|
+
|
|
124
|
+
private lateinit var speechApi: MapboxSpeechApi
|
|
125
|
+
private lateinit var voiceInstructionsPlayer: MapboxVoiceInstructionsPlayer
|
|
126
|
+
|
|
127
|
+
private val navigationLocationProvider = NavigationLocationProvider()
|
|
128
|
+
|
|
129
|
+
// style state (we wait for style load before drawing route line)
|
|
130
|
+
private var styleReady = false
|
|
131
|
+
private var pendingRoute: DirectionsRoute? = null
|
|
132
|
+
|
|
133
|
+
// prevent double requesting routes
|
|
134
|
+
private var routeRequestInFlight = false
|
|
135
|
+
|
|
141
136
|
private val pixelDensity = Resources.getSystem().displayMetrics.density
|
|
142
137
|
private val overviewPadding: EdgeInsets by lazy {
|
|
143
|
-
EdgeInsets(
|
|
144
|
-
140.0 * pixelDensity,
|
|
145
|
-
40.0 * pixelDensity,
|
|
146
|
-
120.0 * pixelDensity,
|
|
147
|
-
40.0 * pixelDensity
|
|
148
|
-
)
|
|
138
|
+
EdgeInsets(140.0 * pixelDensity, 40.0 * pixelDensity, 120.0 * pixelDensity, 40.0 * pixelDensity)
|
|
149
139
|
}
|
|
150
140
|
private val landscapeOverviewPadding: EdgeInsets by lazy {
|
|
151
|
-
EdgeInsets(
|
|
152
|
-
30.0 * pixelDensity,
|
|
153
|
-
380.0 * pixelDensity,
|
|
154
|
-
110.0 * pixelDensity,
|
|
155
|
-
20.0 * pixelDensity
|
|
156
|
-
)
|
|
141
|
+
EdgeInsets(30.0 * pixelDensity, 380.0 * pixelDensity, 110.0 * pixelDensity, 20.0 * pixelDensity)
|
|
157
142
|
}
|
|
158
143
|
private val followingPadding: EdgeInsets by lazy {
|
|
159
|
-
EdgeInsets(
|
|
160
|
-
180.0 * pixelDensity,
|
|
161
|
-
40.0 * pixelDensity,
|
|
162
|
-
150.0 * pixelDensity,
|
|
163
|
-
40.0 * pixelDensity
|
|
164
|
-
)
|
|
144
|
+
EdgeInsets(180.0 * pixelDensity, 40.0 * pixelDensity, 150.0 * pixelDensity, 40.0 * pixelDensity)
|
|
165
145
|
}
|
|
166
146
|
private val landscapeFollowingPadding: EdgeInsets by lazy {
|
|
167
|
-
EdgeInsets(
|
|
168
|
-
30.0 * pixelDensity,
|
|
169
|
-
380.0 * pixelDensity,
|
|
170
|
-
110.0 * pixelDensity,
|
|
171
|
-
40.0 * pixelDensity
|
|
172
|
-
)
|
|
147
|
+
EdgeInsets(30.0 * pixelDensity, 380.0 * pixelDensity, 110.0 * pixelDensity, 40.0 * pixelDensity)
|
|
173
148
|
}
|
|
174
149
|
|
|
175
|
-
/**
|
|
176
|
-
* Generates updates for the [MapboxManeuverView] to display the upcoming maneuver instructions
|
|
177
|
-
* and remaining distance to the maneuver point.
|
|
178
|
-
*/
|
|
179
|
-
private lateinit var maneuverApi: MapboxManeuverApi
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Generates updates for the [MapboxTripProgressView] that include remaining time and distance to the destination.
|
|
183
|
-
*/
|
|
184
|
-
private lateinit var tripProgressApi: MapboxTripProgressApi
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Generates updates for the [routeLineView] with the geometries and properties of the routes that should be drawn on the map.
|
|
188
|
-
*/
|
|
189
|
-
private lateinit var routeLineApi: MapboxRouteLineApi
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Draws route lines on the map based on the data from the [routeLineApi]
|
|
193
|
-
*/
|
|
194
|
-
private lateinit var routeLineView: MapboxRouteLineView
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Generates updates for the [routeArrowView] with the geometries and properties of maneuver arrows that should be drawn on the map.
|
|
198
|
-
*/
|
|
199
|
-
private val routeArrowApi: MapboxRouteArrowApi = MapboxRouteArrowApi()
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Draws maneuver arrows on the map based on the data [routeArrowApi].
|
|
203
|
-
*/
|
|
204
|
-
private lateinit var routeArrowView: MapboxRouteArrowView
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Stores and updates the state of whether the voice instructions should be played as they come or muted.
|
|
208
|
-
*/
|
|
209
150
|
private var isVoiceInstructionsMuted = false
|
|
210
151
|
set(value) {
|
|
211
152
|
field = value
|
|
212
|
-
if (
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
153
|
+
if (::voiceInstructionsPlayer.isInitialized) {
|
|
154
|
+
if (value) {
|
|
155
|
+
binding.soundButton.muteAndExtend(BUTTON_ANIMATION_DURATION)
|
|
156
|
+
voiceInstructionsPlayer.volume(SpeechVolume(0f))
|
|
157
|
+
} else {
|
|
158
|
+
binding.soundButton.unmuteAndExtend(BUTTON_ANIMATION_DURATION)
|
|
159
|
+
voiceInstructionsPlayer.volume(SpeechVolume(1f))
|
|
160
|
+
}
|
|
218
161
|
}
|
|
219
162
|
}
|
|
220
163
|
|
|
221
|
-
/**
|
|
222
|
-
* Extracts message that should be communicated to the driver about the upcoming maneuver.
|
|
223
|
-
* When possible, downloads a synthesized audio file that can be played back to the driver.
|
|
224
|
-
*/
|
|
225
|
-
private lateinit var speechApi: MapboxSpeechApi
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Plays the synthesized audio files with upcoming maneuver instructions
|
|
229
|
-
* or uses an on-device Text-To-Speech engine to communicate the message to the driver.
|
|
230
|
-
*/
|
|
231
|
-
private lateinit var voiceInstructionsPlayer: MapboxVoiceInstructionsPlayer
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Observes when a new voice instruction should be played.
|
|
235
|
-
*/
|
|
236
164
|
private val voiceInstructionsObserver = VoiceInstructionsObserver { voiceInstructions ->
|
|
237
165
|
speechApi.generate(voiceInstructions, speechCallback)
|
|
238
166
|
}
|
|
239
167
|
|
|
240
|
-
/**
|
|
241
|
-
* Based on whether the synthesized audio file is available, the callback plays the file
|
|
242
|
-
* or uses the fall back which is played back using the on-device Text-To-Speech engine.
|
|
243
|
-
*/
|
|
244
168
|
private val speechCallback =
|
|
245
169
|
MapboxNavigationConsumer<Expected<SpeechError, SpeechValue>> { expected ->
|
|
246
170
|
expected.fold(
|
|
247
171
|
{ error ->
|
|
248
|
-
|
|
249
|
-
voiceInstructionsPlayer.play(
|
|
250
|
-
error.fallback,
|
|
251
|
-
voiceInstructionsPlayerCallback
|
|
252
|
-
)
|
|
172
|
+
voiceInstructionsPlayer.play(error.fallback, voiceInstructionsPlayerCallback)
|
|
253
173
|
},
|
|
254
174
|
{ value ->
|
|
255
|
-
|
|
256
|
-
voiceInstructionsPlayer.play(
|
|
257
|
-
value.announcement,
|
|
258
|
-
voiceInstructionsPlayerCallback
|
|
259
|
-
)
|
|
175
|
+
voiceInstructionsPlayer.play(value.announcement, voiceInstructionsPlayerCallback)
|
|
260
176
|
}
|
|
261
177
|
)
|
|
262
178
|
}
|
|
263
179
|
|
|
264
|
-
/**
|
|
265
|
-
* When a synthesized audio file was downloaded, this callback cleans up the disk after it was played.
|
|
266
|
-
*/
|
|
267
180
|
private val voiceInstructionsPlayerCallback =
|
|
268
181
|
MapboxNavigationConsumer<SpeechAnnouncement> { value ->
|
|
269
|
-
// remove already consumed file to free-up space
|
|
270
182
|
speechApi.clean(value)
|
|
271
183
|
}
|
|
272
184
|
|
|
273
|
-
/**
|
|
274
|
-
* [NavigationLocationProvider] is a utility class that helps to provide location updates generated by the Navigation SDK
|
|
275
|
-
* to the Maps SDK in order to update the user location indicator on the map.
|
|
276
|
-
*/
|
|
277
|
-
private val navigationLocationProvider = NavigationLocationProvider()
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Gets notified with location updates.
|
|
281
|
-
*
|
|
282
|
-
* Exposes raw updates coming directly from the location services
|
|
283
|
-
* and the updates enhanced by the Navigation SDK (cleaned up and matched to the road).
|
|
284
|
-
*/
|
|
285
185
|
private val locationObserver = object : LocationObserver {
|
|
286
186
|
override fun onNewRawLocation(rawLocation: Location) {
|
|
287
187
|
// not handled
|
|
@@ -289,49 +189,40 @@ class MapboxNavigationView(private val context: ThemedReactContext, private val
|
|
|
289
189
|
|
|
290
190
|
override fun onNewLocationMatcherResult(locationMatcherResult: LocationMatcherResult) {
|
|
291
191
|
val enhancedLocation = locationMatcherResult.enhancedLocation
|
|
292
|
-
|
|
192
|
+
|
|
293
193
|
navigationLocationProvider.changePosition(
|
|
294
194
|
location = enhancedLocation,
|
|
295
195
|
keyPoints = locationMatcherResult.keyPoints,
|
|
296
196
|
)
|
|
297
197
|
|
|
298
|
-
// update camera position to account for new location
|
|
299
198
|
viewportDataSource.onLocationChanged(enhancedLocation)
|
|
300
199
|
viewportDataSource.evaluate()
|
|
301
200
|
|
|
302
|
-
val event = Arguments.createMap()
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
201
|
+
val event = Arguments.createMap().apply {
|
|
202
|
+
putDouble("longitude", enhancedLocation.longitude)
|
|
203
|
+
putDouble("latitude", enhancedLocation.latitude)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
reactContext
|
|
306
207
|
.getJSModule(RCTEventEmitter::class.java)
|
|
307
208
|
.receiveEvent(id, "onLocationChange", event)
|
|
308
209
|
}
|
|
309
210
|
}
|
|
310
211
|
|
|
311
|
-
/**
|
|
312
|
-
* Gets notified with progress along the currently active route.
|
|
313
|
-
*/
|
|
314
212
|
private val routeProgressObserver = RouteProgressObserver { routeProgress ->
|
|
315
|
-
// update the camera position to account for the progressed fragment of the route
|
|
316
213
|
viewportDataSource.onRouteProgressChanged(routeProgress)
|
|
317
214
|
viewportDataSource.evaluate()
|
|
318
215
|
|
|
319
|
-
//
|
|
320
|
-
|
|
321
|
-
if (style != null) {
|
|
216
|
+
// arrow rendering is OK
|
|
217
|
+
mapboxMap.getStyle()?.let { style ->
|
|
322
218
|
val maneuverArrowResult = routeArrowApi.addUpcomingManeuverArrow(routeProgress)
|
|
323
219
|
routeArrowView.renderManeuverUpdate(style, maneuverArrowResult)
|
|
324
220
|
}
|
|
325
221
|
|
|
326
|
-
// update top banner with maneuver instructions
|
|
327
222
|
val maneuvers = maneuverApi.getManeuvers(routeProgress)
|
|
328
223
|
maneuvers.fold(
|
|
329
224
|
{ error ->
|
|
330
|
-
Toast.makeText(
|
|
331
|
-
context,
|
|
332
|
-
error.errorMessage,
|
|
333
|
-
Toast.LENGTH_SHORT
|
|
334
|
-
).show()
|
|
225
|
+
Toast.makeText(reactContext, error.errorMessage, Toast.LENGTH_SHORT).show()
|
|
335
226
|
},
|
|
336
227
|
{
|
|
337
228
|
binding.maneuverView.visibility = View.VISIBLE
|
|
@@ -343,84 +234,59 @@ class MapboxNavigationView(private val context: ThemedReactContext, private val
|
|
|
343
234
|
}
|
|
344
235
|
)
|
|
345
236
|
|
|
346
|
-
|
|
347
|
-
binding.tripProgressView.render(
|
|
348
|
-
tripProgressApi.getTripProgress(routeProgress)
|
|
349
|
-
)
|
|
237
|
+
binding.tripProgressView.render(tripProgressApi.getTripProgress(routeProgress))
|
|
350
238
|
|
|
351
|
-
val event = Arguments.createMap()
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
239
|
+
val event = Arguments.createMap().apply {
|
|
240
|
+
putDouble("distanceTraveled", routeProgress.distanceTraveled.toDouble())
|
|
241
|
+
putDouble("durationRemaining", routeProgress.durationRemaining.toDouble())
|
|
242
|
+
putDouble("fractionTraveled", routeProgress.fractionTraveled.toDouble())
|
|
243
|
+
putDouble("distanceRemaining", routeProgress.distanceRemaining.toDouble())
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
reactContext
|
|
357
247
|
.getJSModule(RCTEventEmitter::class.java)
|
|
358
248
|
.receiveEvent(id, "onRouteProgressChange", event)
|
|
359
249
|
}
|
|
360
250
|
|
|
361
|
-
|
|
362
|
-
* Gets notified whenever the tracked routes change.
|
|
363
|
-
*
|
|
364
|
-
* A change can mean:
|
|
365
|
-
* - routes get changed with [MapboxNavigation.setRoutes]
|
|
366
|
-
* - routes annotations get refreshed (for example, congestion annotation that indicate the live traffic along the route)
|
|
367
|
-
* - driver got off route and a reroute was executed
|
|
368
|
-
*/
|
|
251
|
+
// ✅ routes observer now draws route via GeoJson + LineLayer (no RouteLineView => no native crash)
|
|
369
252
|
private val routesObserver = RoutesObserver { routeUpdateResult ->
|
|
370
|
-
if (
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
routeLineApi.setRoutes(
|
|
375
|
-
routeLines
|
|
376
|
-
) { value ->
|
|
377
|
-
mapboxMap.getStyle()?.apply {
|
|
378
|
-
routeLineView.renderRouteDrawData(this, value)
|
|
379
|
-
}
|
|
380
|
-
}
|
|
253
|
+
if (!isAttachedToWindow) return@RoutesObserver
|
|
254
|
+
|
|
255
|
+
val primary = routeUpdateResult.routes.firstOrNull()
|
|
381
256
|
|
|
382
|
-
|
|
383
|
-
|
|
257
|
+
// camera updates
|
|
258
|
+
if (primary != null) {
|
|
259
|
+
viewportDataSource.onRouteChanged(primary)
|
|
384
260
|
viewportDataSource.evaluate()
|
|
385
261
|
} else {
|
|
386
|
-
// remove the route line and route arrow from the map
|
|
387
|
-
val style = mapboxMap.getStyle()
|
|
388
|
-
if (style != null) {
|
|
389
|
-
routeLineApi.clearRouteLine { value ->
|
|
390
|
-
routeLineView.renderClearRouteLineValue(
|
|
391
|
-
style,
|
|
392
|
-
value
|
|
393
|
-
)
|
|
394
|
-
}
|
|
395
|
-
routeArrowView.render(style, routeArrowApi.clearArrows())
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// remove the route reference from camera position evaluations
|
|
399
262
|
viewportDataSource.clearRouteData()
|
|
400
263
|
viewportDataSource.evaluate()
|
|
401
264
|
}
|
|
402
|
-
}
|
|
403
265
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
266
|
+
// draw the route line safely
|
|
267
|
+
binding.mapView.post {
|
|
268
|
+
if (!isAttachedToWindow) return@post
|
|
269
|
+
val style = mapboxMap.getStyle()
|
|
270
|
+
if (style == null || !styleReady) {
|
|
271
|
+
pendingRoute = primary
|
|
272
|
+
return@post
|
|
273
|
+
}
|
|
274
|
+
renderRouteLine(style, primary)
|
|
408
275
|
}
|
|
276
|
+
}
|
|
409
277
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
}
|
|
278
|
+
private val arrivalObserver = object : ArrivalObserver {
|
|
279
|
+
override fun onWaypointArrival(routeProgress: RouteProgress) {}
|
|
280
|
+
override fun onNextRouteLegStart(routeLegProgress: RouteLegProgress) {}
|
|
413
281
|
|
|
414
282
|
override fun onFinalDestinationArrival(routeProgress: RouteProgress) {
|
|
415
|
-
val event = Arguments.createMap()
|
|
416
|
-
|
|
417
|
-
context
|
|
283
|
+
val event = Arguments.createMap().apply { putString("message", "Arrived") }
|
|
284
|
+
reactContext
|
|
418
285
|
.getJSModule(RCTEventEmitter::class.java)
|
|
419
|
-
.receiveEvent(id, "
|
|
286
|
+
.receiveEvent(id, "onArrive", event) // ✅ correct event name
|
|
420
287
|
}
|
|
421
288
|
}
|
|
422
289
|
|
|
423
|
-
|
|
424
290
|
override fun onAttachedToWindow() {
|
|
425
291
|
super.onAttachedToWindow()
|
|
426
292
|
onCreate()
|
|
@@ -432,20 +298,79 @@ class MapboxNavigationView(private val context: ThemedReactContext, private val
|
|
|
432
298
|
}
|
|
433
299
|
|
|
434
300
|
private val measureAndLayout = Runnable {
|
|
435
|
-
measure(
|
|
436
|
-
MeasureSpec.makeMeasureSpec(
|
|
301
|
+
measure(
|
|
302
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
303
|
+
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
|
304
|
+
)
|
|
437
305
|
layout(left, top, right, bottom)
|
|
438
306
|
}
|
|
439
307
|
|
|
308
|
+
private fun sendErrorToReact(error: String?) {
|
|
309
|
+
val event = Arguments.createMap().apply { putString("error", error) }
|
|
310
|
+
reactContext
|
|
311
|
+
.getJSModule(RCTEventEmitter::class.java)
|
|
312
|
+
.receiveEvent(id, "onError", event)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private fun ensureStyleHasRouteLine(style: Style) {
|
|
316
|
+
if (!style.styleSourceExists(ROUTE_SOURCE_ID)) {
|
|
317
|
+
style.addSource(
|
|
318
|
+
geoJsonSource(ROUTE_SOURCE_ID) {
|
|
319
|
+
featureCollection(FeatureCollection.fromFeatures(emptyArray()))
|
|
320
|
+
}
|
|
321
|
+
)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (!style.styleLayerExists(ROUTE_LAYER_ID)) {
|
|
325
|
+
val routeLayer = lineLayer(ROUTE_LAYER_ID, ROUTE_SOURCE_ID) {
|
|
326
|
+
lineJoin(LineJoin.ROUND)
|
|
327
|
+
lineCap(LineCap.ROUND)
|
|
328
|
+
lineWidth(12.0)
|
|
329
|
+
lineColor("#1E88E5")
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Find first location layer
|
|
333
|
+
val locationLayerId = style.styleLayers
|
|
334
|
+
.firstOrNull { it.id.contains("mapbox-location") }
|
|
335
|
+
?.id
|
|
336
|
+
|
|
337
|
+
if (locationLayerId != null) {
|
|
338
|
+
style.addLayerBelow(routeLayer, locationLayerId)
|
|
339
|
+
} else {
|
|
340
|
+
// fallback
|
|
341
|
+
style.addLayer(routeLayer)
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
private fun renderRouteLine(style: Style, route: DirectionsRoute?) {
|
|
347
|
+
// clear
|
|
348
|
+
if (route?.geometry().isNullOrEmpty()) {
|
|
349
|
+
val src = style.getSourceAs<com.mapbox.maps.extension.style.sources.generated.GeoJsonSource>(ROUTE_SOURCE_ID)
|
|
350
|
+
src?.featureCollection(FeatureCollection.fromFeatures(emptyArray()))
|
|
351
|
+
return
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
val points = PolylineUtils.decode(route!!.geometry()!!, 6) // polyline6
|
|
355
|
+
val lineString = LineString.fromLngLats(points)
|
|
356
|
+
val feature = Feature.fromGeometry(lineString)
|
|
357
|
+
val fc = FeatureCollection.fromFeature(feature)
|
|
358
|
+
|
|
359
|
+
val src = style.getSourceAs<com.mapbox.maps.extension.style.sources.generated.GeoJsonSource>(ROUTE_SOURCE_ID)
|
|
360
|
+
src?.featureCollection(fc)
|
|
361
|
+
}
|
|
362
|
+
|
|
440
363
|
private fun setCameraPositionToOrigin() {
|
|
441
|
-
val
|
|
442
|
-
startingLocation
|
|
443
|
-
|
|
364
|
+
val o = origin ?: return
|
|
365
|
+
val startingLocation = Location(LocationManager.GPS_PROVIDER).apply {
|
|
366
|
+
latitude = o.latitude()
|
|
367
|
+
longitude = o.longitude()
|
|
368
|
+
}
|
|
444
369
|
viewportDataSource.onLocationChanged(startingLocation)
|
|
445
370
|
|
|
446
371
|
navigationCamera.requestNavigationCameraToFollowing(
|
|
447
372
|
stateTransitionOptions = NavigationCameraTransitionOptions.Builder()
|
|
448
|
-
.maxDuration(0)
|
|
373
|
+
.maxDuration(0)
|
|
449
374
|
.build()
|
|
450
375
|
)
|
|
451
376
|
}
|
|
@@ -456,7 +381,6 @@ class MapboxNavigationView(private val context: ThemedReactContext, private val
|
|
|
456
381
|
sendErrorToReact("Mapbox access token is not set")
|
|
457
382
|
return
|
|
458
383
|
}
|
|
459
|
-
|
|
460
384
|
if (origin == null || destination == null) {
|
|
461
385
|
sendErrorToReact("origin and destination are required")
|
|
462
386
|
return
|
|
@@ -464,53 +388,46 @@ class MapboxNavigationView(private val context: ThemedReactContext, private val
|
|
|
464
388
|
|
|
465
389
|
mapboxMap = binding.mapView.getMapboxMap()
|
|
466
390
|
|
|
467
|
-
//
|
|
391
|
+
// location puck
|
|
468
392
|
binding.mapView.location.apply {
|
|
469
393
|
this.locationPuck = LocationPuck2D(
|
|
470
394
|
bearingImage = ContextCompat.getDrawable(
|
|
471
|
-
|
|
395
|
+
reactContext,
|
|
472
396
|
com.mapbox.navigation.ui.maps.R.drawable.mapbox_navigation_puck_icon
|
|
473
|
-
|
|
474
397
|
)
|
|
475
398
|
)
|
|
476
399
|
setLocationProvider(navigationLocationProvider)
|
|
477
400
|
enabled = true
|
|
478
401
|
}
|
|
479
402
|
|
|
480
|
-
//
|
|
403
|
+
// Mapbox Navigation
|
|
481
404
|
mapboxNavigation = if (MapboxNavigationProvider.isCreated()) {
|
|
482
405
|
MapboxNavigationProvider.retrieve()
|
|
483
406
|
} else if (shouldSimulateRoute) {
|
|
484
407
|
MapboxNavigationProvider.create(
|
|
485
|
-
NavigationOptions.Builder(
|
|
408
|
+
NavigationOptions.Builder(reactContext)
|
|
486
409
|
.accessToken(accessToken)
|
|
487
410
|
.locationEngine(replayLocationEngine)
|
|
488
411
|
.build()
|
|
489
412
|
)
|
|
490
413
|
} else {
|
|
491
414
|
MapboxNavigationProvider.create(
|
|
492
|
-
NavigationOptions.Builder(
|
|
415
|
+
NavigationOptions.Builder(reactContext)
|
|
493
416
|
.accessToken(accessToken)
|
|
494
417
|
.build()
|
|
495
418
|
)
|
|
496
419
|
}
|
|
497
420
|
|
|
498
|
-
//
|
|
421
|
+
// camera setup
|
|
499
422
|
viewportDataSource = MapboxNavigationViewportDataSource(mapboxMap)
|
|
423
|
+
navigationCamera = NavigationCamera(mapboxMap, binding.mapView.camera, viewportDataSource)
|
|
500
424
|
|
|
501
|
-
navigationCamera = NavigationCamera(
|
|
502
|
-
mapboxMap,
|
|
503
|
-
binding.mapView.camera,
|
|
504
|
-
viewportDataSource
|
|
505
|
-
)
|
|
506
|
-
// set the animations lifecycle listener to ensure the NavigationCamera stops
|
|
507
|
-
// automatically following the user location when the map is interacted with
|
|
508
425
|
binding.mapView.camera.addCameraAnimationsLifecycleListener(
|
|
509
426
|
NavigationBasicGesturesHandler(navigationCamera)
|
|
510
427
|
)
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
when (
|
|
428
|
+
|
|
429
|
+
navigationCamera.registerNavigationCameraStateChangeObserver { state ->
|
|
430
|
+
when (state) {
|
|
514
431
|
NavigationCameraState.TRANSITION_TO_FOLLOWING,
|
|
515
432
|
NavigationCameraState.FOLLOWING -> binding.recenter.visibility = View.INVISIBLE
|
|
516
433
|
NavigationCameraState.TRANSITION_TO_OVERVIEW,
|
|
@@ -518,109 +435,76 @@ class MapboxNavigationView(private val context: ThemedReactContext, private val
|
|
|
518
435
|
NavigationCameraState.IDLE -> binding.recenter.visibility = View.VISIBLE
|
|
519
436
|
}
|
|
520
437
|
}
|
|
521
|
-
|
|
522
|
-
if (
|
|
438
|
+
|
|
439
|
+
if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
|
523
440
|
viewportDataSource.overviewPadding = landscapeOverviewPadding
|
|
524
|
-
} else {
|
|
525
|
-
viewportDataSource.overviewPadding = overviewPadding
|
|
526
|
-
}
|
|
527
|
-
if (this.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
|
528
441
|
viewportDataSource.followingPadding = landscapeFollowingPadding
|
|
529
442
|
} else {
|
|
443
|
+
viewportDataSource.overviewPadding = overviewPadding
|
|
530
444
|
viewportDataSource.followingPadding = followingPadding
|
|
531
445
|
}
|
|
532
446
|
|
|
533
|
-
// make sure to use the same DistanceFormatterOptions across different features
|
|
534
447
|
val distanceFormatterOptions = mapboxNavigation.navigationOptions.distanceFormatterOptions
|
|
535
448
|
|
|
536
|
-
|
|
537
|
-
maneuverApi = MapboxManeuverApi(
|
|
538
|
-
MapboxDistanceFormatter(distanceFormatterOptions)
|
|
539
|
-
)
|
|
540
|
-
|
|
541
|
-
// initialize bottom progress view
|
|
449
|
+
maneuverApi = MapboxManeuverApi(MapboxDistanceFormatter(distanceFormatterOptions))
|
|
542
450
|
tripProgressApi = MapboxTripProgressApi(
|
|
543
|
-
TripProgressUpdateFormatter.Builder(
|
|
544
|
-
.distanceRemainingFormatter(
|
|
545
|
-
|
|
546
|
-
)
|
|
547
|
-
.timeRemainingFormatter(
|
|
548
|
-
TimeRemainingFormatter(context)
|
|
549
|
-
)
|
|
550
|
-
.percentRouteTraveledFormatter(
|
|
551
|
-
PercentDistanceTraveledFormatter()
|
|
552
|
-
)
|
|
451
|
+
TripProgressUpdateFormatter.Builder(reactContext)
|
|
452
|
+
.distanceRemainingFormatter(DistanceRemainingFormatter(distanceFormatterOptions))
|
|
453
|
+
.timeRemainingFormatter(TimeRemainingFormatter(reactContext))
|
|
454
|
+
.percentRouteTraveledFormatter(PercentDistanceTraveledFormatter())
|
|
553
455
|
.estimatedTimeToArrivalFormatter(
|
|
554
|
-
EstimatedTimeToArrivalFormatter(
|
|
456
|
+
EstimatedTimeToArrivalFormatter(reactContext, TimeFormat.NONE_SPECIFIED)
|
|
555
457
|
)
|
|
556
458
|
.build()
|
|
557
459
|
)
|
|
558
460
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
context,
|
|
562
|
-
accessToken,
|
|
563
|
-
Locale.US.language
|
|
564
|
-
)
|
|
565
|
-
voiceInstructionsPlayer = MapboxVoiceInstructionsPlayer(
|
|
566
|
-
context,
|
|
567
|
-
accessToken,
|
|
568
|
-
Locale.US.language
|
|
569
|
-
)
|
|
461
|
+
speechApi = MapboxSpeechApi(reactContext, accessToken, Locale.US.language)
|
|
462
|
+
voiceInstructionsPlayer = MapboxVoiceInstructionsPlayer(reactContext, accessToken, Locale.US.language)
|
|
570
463
|
|
|
571
|
-
|
|
572
|
-
// the route line below road labels layer on the map
|
|
573
|
-
// the value of this option will depend on the style that you are using
|
|
574
|
-
// and under which layer the route line should be placed on the map layers stack
|
|
575
|
-
val mapboxRouteLineOptions = MapboxRouteLineOptions.Builder(context)
|
|
576
|
-
.withRouteLineBelowLayerId("road-label")
|
|
577
|
-
.build()
|
|
578
|
-
routeLineApi = MapboxRouteLineApi(mapboxRouteLineOptions)
|
|
579
|
-
routeLineView = MapboxRouteLineView(mapboxRouteLineOptions)
|
|
580
|
-
|
|
581
|
-
// initialize maneuver arrow view to draw arrows on the map
|
|
582
|
-
val routeArrowOptions = RouteArrowOptions.Builder(context).build()
|
|
464
|
+
val routeArrowOptions = RouteArrowOptions.Builder(reactContext).build()
|
|
583
465
|
routeArrowView = MapboxRouteArrowView(routeArrowOptions)
|
|
584
466
|
|
|
467
|
+
// load style and install our stable route line layer
|
|
468
|
+
styleReady = false
|
|
469
|
+
mapboxMap.loadStyleUri(Style.MAPBOX_STREETS) { style ->
|
|
470
|
+
styleReady = true
|
|
471
|
+
ensureStyleHasRouteLine(style)
|
|
472
|
+
pendingRoute?.let {
|
|
473
|
+
renderRouteLine(style, it)
|
|
474
|
+
pendingRoute = null
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
585
478
|
setCameraPositionToOrigin()
|
|
586
|
-
// load map style
|
|
587
|
-
mapboxMap.loadStyleUri(
|
|
588
|
-
Style.MAPBOX_STREETS
|
|
589
|
-
)
|
|
590
479
|
|
|
591
|
-
//
|
|
480
|
+
// UI interactions
|
|
592
481
|
binding.stop.setOnClickListener {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
event.putString("onCancelNavigation", "Navigation Closed")
|
|
596
|
-
context
|
|
597
|
-
.getJSModule(RCTEventEmitter::class.java)
|
|
598
|
-
.receiveEvent(id, "onCancelNavigation", event)
|
|
482
|
+
val event = Arguments.createMap().apply { putString("onCancelNavigation", "Navigation Closed") }
|
|
483
|
+
reactContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(id, "onCancelNavigation", event)
|
|
599
484
|
}
|
|
485
|
+
|
|
600
486
|
binding.recenter.setOnClickListener {
|
|
601
487
|
navigationCamera.requestNavigationCameraToFollowing()
|
|
602
488
|
binding.routeOverview.showTextAndExtend(BUTTON_ANIMATION_DURATION)
|
|
603
489
|
}
|
|
490
|
+
|
|
604
491
|
binding.routeOverview.setOnClickListener {
|
|
605
492
|
navigationCamera.requestNavigationCameraToOverview()
|
|
606
493
|
binding.recenter.showTextAndExtend(BUTTON_ANIMATION_DURATION)
|
|
607
494
|
}
|
|
495
|
+
|
|
608
496
|
binding.soundButton.setOnClickListener {
|
|
609
|
-
// mute/unmute voice instructions
|
|
610
497
|
isVoiceInstructionsMuted = !isVoiceInstructionsMuted
|
|
611
498
|
}
|
|
612
499
|
|
|
613
|
-
// set initial sounds button state
|
|
614
500
|
binding.soundButton.unmute()
|
|
615
501
|
|
|
616
|
-
// start
|
|
617
|
-
// and later when a route is set also receiving route progress updates
|
|
502
|
+
// start trip session + route
|
|
618
503
|
mapboxNavigation.startTripSession()
|
|
619
504
|
startRoute()
|
|
620
505
|
}
|
|
621
506
|
|
|
622
507
|
private fun startRoute() {
|
|
623
|
-
// register event listeners
|
|
624
508
|
mapboxNavigation.registerRoutesObserver(routesObserver)
|
|
625
509
|
mapboxNavigation.registerArrivalObserver(arrivalObserver)
|
|
626
510
|
mapboxNavigation.registerRouteProgressObserver(routeProgressObserver)
|
|
@@ -628,109 +512,62 @@ class MapboxNavigationView(private val context: ThemedReactContext, private val
|
|
|
628
512
|
mapboxNavigation.registerVoiceInstructionsObserver(voiceInstructionsObserver)
|
|
629
513
|
mapboxNavigation.registerRouteProgressObserver(replayProgressObserver)
|
|
630
514
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
mapboxNavigation.unregisterRoutesObserver(routesObserver)
|
|
637
|
-
mapboxNavigation.unregisterRouteProgressObserver(routeProgressObserver)
|
|
638
|
-
mapboxNavigation.unregisterLocationObserver(locationObserver)
|
|
639
|
-
mapboxNavigation.unregisterVoiceInstructionsObserver(voiceInstructionsObserver)
|
|
640
|
-
mapboxNavigation.unregisterRouteProgressObserver(replayProgressObserver)
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
private fun onDestroy() {
|
|
644
|
-
MapboxNavigationProvider.destroy()
|
|
645
|
-
mapboxReplayer.finish()
|
|
646
|
-
maneuverApi.cancel()
|
|
647
|
-
routeLineApi.cancel()
|
|
648
|
-
routeLineView.cancel()
|
|
649
|
-
speechApi.cancel()
|
|
650
|
-
voiceInstructionsPlayer.shutdown()
|
|
515
|
+
val o = origin
|
|
516
|
+
val d = destination
|
|
517
|
+
if (o != null && d != null) {
|
|
518
|
+
findRouteOnce(o, d)
|
|
519
|
+
}
|
|
651
520
|
}
|
|
652
521
|
|
|
653
|
-
private fun
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
override fun onFailure(
|
|
672
|
-
reasons: List<RouterFailure>,
|
|
673
|
-
routeOptions: RouteOptions
|
|
674
|
-
) {
|
|
675
|
-
sendErrorToReact("Error finding route $reasons")
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
override fun onCanceled(routeOptions: RouteOptions, routerOrigin: RouterOrigin) {
|
|
679
|
-
// no impl
|
|
680
|
-
}
|
|
522
|
+
private fun findRouteOnce(o: Point, d: Point) {
|
|
523
|
+
if (routeRequestInFlight) return
|
|
524
|
+
routeRequestInFlight = true
|
|
525
|
+
|
|
526
|
+
mapboxNavigation.requestRoutes(
|
|
527
|
+
RouteOptions.builder()
|
|
528
|
+
.applyDefaultNavigationOptions()
|
|
529
|
+
.applyLanguageAndVoiceUnitOptions(reactContext)
|
|
530
|
+
.coordinatesList(listOf(o, d))
|
|
531
|
+
.profile(DirectionsCriteria.PROFILE_DRIVING)
|
|
532
|
+
.steps(true)
|
|
533
|
+
.build(),
|
|
534
|
+
object : RouterCallback {
|
|
535
|
+
override fun onRoutesReady(routes: List<DirectionsRoute>, routerOrigin: RouterOrigin) {
|
|
536
|
+
routeRequestInFlight = false
|
|
537
|
+
setRouteAndStartNavigation(routes)
|
|
681
538
|
}
|
|
682
|
-
)
|
|
683
|
-
} catch (ex: Exception) {
|
|
684
|
-
sendErrorToReact(ex.toString())
|
|
685
|
-
}
|
|
686
539
|
|
|
687
|
-
|
|
540
|
+
override fun onFailure(reasons: List<RouterFailure>, routeOptions: RouteOptions) {
|
|
541
|
+
routeRequestInFlight = false
|
|
542
|
+
sendErrorToReact("Error finding route $reasons")
|
|
543
|
+
}
|
|
688
544
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
.receiveEvent(id, "onError", event)
|
|
545
|
+
override fun onCanceled(routeOptions: RouteOptions, routerOrigin: RouterOrigin) {
|
|
546
|
+
routeRequestInFlight = false
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
)
|
|
695
550
|
}
|
|
696
551
|
|
|
697
552
|
private fun setRouteAndStartNavigation(routes: List<DirectionsRoute>) {
|
|
698
553
|
if (routes.isEmpty()) {
|
|
699
554
|
sendErrorToReact("No route found")
|
|
700
|
-
return
|
|
555
|
+
return
|
|
701
556
|
}
|
|
702
|
-
|
|
703
|
-
// will be used for active guidance
|
|
557
|
+
|
|
704
558
|
mapboxNavigation.setRoutes(routes)
|
|
705
559
|
|
|
706
|
-
// start location simulation along the primary route
|
|
707
560
|
if (shouldSimulateRoute) {
|
|
708
561
|
startSimulation(routes.first())
|
|
709
562
|
}
|
|
710
563
|
|
|
711
|
-
// show UI elements
|
|
712
564
|
binding.soundButton.visibility = View.VISIBLE
|
|
713
565
|
binding.routeOverview.visibility = View.VISIBLE
|
|
714
566
|
binding.tripProgressCard.visibility = View.VISIBLE
|
|
715
567
|
|
|
716
|
-
// move the camera to overview when new route is available
|
|
717
568
|
navigationCamera.requestNavigationCameraToFollowing()
|
|
718
569
|
}
|
|
719
570
|
|
|
720
|
-
private fun clearRouteAndStopNavigation() {
|
|
721
|
-
// clear
|
|
722
|
-
mapboxNavigation.setRoutes(listOf())
|
|
723
|
-
|
|
724
|
-
// stop simulation
|
|
725
|
-
mapboxReplayer.stop()
|
|
726
|
-
|
|
727
|
-
// hide UI elements
|
|
728
|
-
binding.soundButton.visibility = View.INVISIBLE
|
|
729
|
-
binding.maneuverView.visibility = View.INVISIBLE
|
|
730
|
-
binding.routeOverview.visibility = View.INVISIBLE
|
|
731
|
-
binding.tripProgressCard.visibility = View.INVISIBLE
|
|
732
|
-
}
|
|
733
|
-
|
|
734
571
|
private fun startSimulation(route: DirectionsRoute) {
|
|
735
572
|
mapboxReplayer.run {
|
|
736
573
|
stop()
|
|
@@ -742,8 +579,33 @@ class MapboxNavigationView(private val context: ThemedReactContext, private val
|
|
|
742
579
|
}
|
|
743
580
|
}
|
|
744
581
|
|
|
582
|
+
override fun onDetachedFromWindow() {
|
|
583
|
+
super.onDetachedFromWindow()
|
|
584
|
+
try {
|
|
585
|
+
mapboxNavigation.unregisterRoutesObserver(routesObserver)
|
|
586
|
+
mapboxNavigation.unregisterArrivalObserver(arrivalObserver)
|
|
587
|
+
mapboxNavigation.unregisterRouteProgressObserver(routeProgressObserver)
|
|
588
|
+
mapboxNavigation.unregisterLocationObserver(locationObserver)
|
|
589
|
+
mapboxNavigation.unregisterVoiceInstructionsObserver(voiceInstructionsObserver)
|
|
590
|
+
mapboxNavigation.unregisterRouteProgressObserver(replayProgressObserver)
|
|
591
|
+
} catch (_: Throwable) {
|
|
592
|
+
// ignore
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
private fun onDestroy() {
|
|
597
|
+
try {
|
|
598
|
+
MapboxNavigationProvider.destroy()
|
|
599
|
+
} catch (_: Throwable) {}
|
|
600
|
+
|
|
601
|
+
try { mapboxReplayer.finish() } catch (_: Throwable) {}
|
|
602
|
+
try { maneuverApi.cancel() } catch (_: Throwable) {}
|
|
603
|
+
try { speechApi.cancel() } catch (_: Throwable) {}
|
|
604
|
+
try { voiceInstructionsPlayer.shutdown() } catch (_: Throwable) {}
|
|
605
|
+
}
|
|
606
|
+
|
|
745
607
|
fun onDropViewInstance() {
|
|
746
|
-
|
|
608
|
+
onDestroy()
|
|
747
609
|
}
|
|
748
610
|
|
|
749
611
|
fun setOrigin(origin: Point?) {
|
|
@@ -765,4 +627,4 @@ class MapboxNavigationView(private val context: ThemedReactContext, private val
|
|
|
765
627
|
fun setMute(mute: Boolean) {
|
|
766
628
|
this.isVoiceInstructionsMuted = mute
|
|
767
629
|
}
|
|
768
|
-
}
|
|
630
|
+
}
|