@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.
- package/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +313 -0
- package/android/build.gradle +98 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/expo/modules/mapboxnavigation/ExpoMapboxNavigationModule.kt +120 -0
- package/android/src/main/java/expo/modules/mapboxnavigation/ExpoMapboxNavigationView.kt +1100 -0
- package/android/src/main/res/drawable-hdpi/icon_compass.png +0 -0
- package/android/src/main/res/drawable-hdpi/icon_mute.png +0 -0
- package/android/src/main/res/drawable-hdpi/icon_overview.png +0 -0
- package/android/src/main/res/drawable-hdpi/icon_sound.png +0 -0
- package/android/src/main/res/drawable-hdpi/icon_x.png +0 -0
- package/android/src/main/res/drawable-ldpi/icon_compass.png +0 -0
- package/android/src/main/res/drawable-ldpi/icon_mute.png +0 -0
- package/android/src/main/res/drawable-ldpi/icon_overview.png +0 -0
- package/android/src/main/res/drawable-ldpi/icon_sound.png +0 -0
- package/android/src/main/res/drawable-ldpi/icon_x.png +0 -0
- package/android/src/main/res/drawable-mdpi/icon_compass.png +0 -0
- package/android/src/main/res/drawable-mdpi/icon_mute.png +0 -0
- package/android/src/main/res/drawable-mdpi/icon_overview.png +0 -0
- package/android/src/main/res/drawable-mdpi/icon_sound.png +0 -0
- package/android/src/main/res/drawable-mdpi/icon_x.png +0 -0
- package/android/src/main/res/drawable-xhdpi/icon_compass.png +0 -0
- package/android/src/main/res/drawable-xhdpi/icon_mute.png +0 -0
- package/android/src/main/res/drawable-xhdpi/icon_overview.png +0 -0
- package/android/src/main/res/drawable-xhdpi/icon_sound.png +0 -0
- package/android/src/main/res/drawable-xhdpi/icon_x.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/icon_compass.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/icon_mute.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/icon_overview.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/icon_sound.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/icon_x.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/icon_compass.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/icon_mute.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/icon_overview.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/icon_sound.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/icon_x.png +0 -0
- package/android/src/main/res/values/styles.xml +9 -0
- package/app.plugin.js +1 -0
- package/build/ExpoMapboxNavigation.types.d.ts +90 -0
- package/build/ExpoMapboxNavigation.types.d.ts.map +1 -0
- package/build/ExpoMapboxNavigation.types.js +2 -0
- package/build/ExpoMapboxNavigation.types.js.map +1 -0
- package/build/ExpoMapboxNavigationModule.d.ts +3 -0
- package/build/ExpoMapboxNavigationModule.d.ts.map +1 -0
- package/build/ExpoMapboxNavigationModule.js +3 -0
- package/build/ExpoMapboxNavigationModule.js.map +1 -0
- package/build/ExpoMapboxNavigationView.d.ts +5 -0
- package/build/ExpoMapboxNavigationView.d.ts.map +1 -0
- package/build/ExpoMapboxNavigationView.js +7 -0
- package/build/ExpoMapboxNavigationView.js.map +1 -0
- package/build/ExpoMapboxNavigationView.web.d.ts +4 -0
- package/build/ExpoMapboxNavigationView.web.d.ts.map +1 -0
- package/build/ExpoMapboxNavigationView.web.js +7 -0
- package/build/ExpoMapboxNavigationView.web.js.map +1 -0
- package/build/index.d.ts +4 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +3 -0
- package/build/index.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/ExpoMapboxNavigation.podspec +46 -0
- package/ios/ExpoMapboxNavigationModule.swift +92 -0
- package/ios/ExpoMapboxNavigationView.swift +509 -0
- package/package.json +47 -0
- package/plugin/config.js +127 -0
- package/src/ExpoMapboxNavigation.types.ts +70 -0
- package/src/ExpoMapboxNavigationModule.ts +2 -0
- package/src/ExpoMapboxNavigationView.tsx +17 -0
- package/src/ExpoMapboxNavigationView.web.tsx +13 -0
- package/src/index.ts +11 -0
|
@@ -0,0 +1,1100 @@
|
|
|
1
|
+
package expo.modules.mapboxnavigation
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.res.Configuration
|
|
5
|
+
import android.content.res.Resources
|
|
6
|
+
import android.graphics.Color
|
|
7
|
+
import android.view.Gravity
|
|
8
|
+
import android.view.View
|
|
9
|
+
import android.view.ViewGroup
|
|
10
|
+
import android.widget.ImageView
|
|
11
|
+
import android.widget.LinearLayout
|
|
12
|
+
import android.widget.TextView
|
|
13
|
+
import androidx.constraintlayout.widget.ConstraintLayout
|
|
14
|
+
import androidx.constraintlayout.widget.ConstraintSet
|
|
15
|
+
import androidx.lifecycle.LifecycleOwner
|
|
16
|
+
import androidx.lifecycle.setViewTreeLifecycleOwner
|
|
17
|
+
import com.mapbox.api.directions.v5.models.RouteOptions
|
|
18
|
+
import com.mapbox.bindgen.Expected
|
|
19
|
+
import com.mapbox.common.location.Location
|
|
20
|
+
import com.mapbox.geojson.Point
|
|
21
|
+
import com.mapbox.geojson.utils.PolylineUtils
|
|
22
|
+
import com.mapbox.maps.CameraOptions
|
|
23
|
+
import com.mapbox.maps.EdgeInsets
|
|
24
|
+
import com.mapbox.maps.ImageHolder
|
|
25
|
+
import com.mapbox.maps.MapView
|
|
26
|
+
import com.mapbox.maps.MapboxMap
|
|
27
|
+
import com.mapbox.maps.Style
|
|
28
|
+
import com.mapbox.maps.extension.localization.localizeLabels
|
|
29
|
+
import com.mapbox.maps.extension.style.layers.*
|
|
30
|
+
import com.mapbox.maps.extension.style.layers.generated.RasterLayer
|
|
31
|
+
import com.mapbox.maps.extension.style.sources.TileSet
|
|
32
|
+
import com.mapbox.maps.extension.style.sources.addSource
|
|
33
|
+
import com.mapbox.maps.extension.style.sources.generated.RasterSource
|
|
34
|
+
import com.mapbox.maps.plugin.LocationPuck2D
|
|
35
|
+
import com.mapbox.maps.plugin.animation.camera
|
|
36
|
+
import com.mapbox.maps.plugin.locationcomponent.OnIndicatorPositionChangedListener
|
|
37
|
+
import com.mapbox.maps.plugin.locationcomponent.location
|
|
38
|
+
import com.mapbox.navigation.base.extensions.applyDefaultNavigationOptions
|
|
39
|
+
import com.mapbox.navigation.base.formatter.DistanceFormatterOptions
|
|
40
|
+
import com.mapbox.navigation.base.route.NavigationRoute
|
|
41
|
+
import com.mapbox.navigation.base.route.NavigationRouterCallback
|
|
42
|
+
import com.mapbox.navigation.base.route.RouterFailure
|
|
43
|
+
import com.mapbox.navigation.base.route.RouterOrigin
|
|
44
|
+
import com.mapbox.navigation.base.trip.model.RouteLegProgress
|
|
45
|
+
import com.mapbox.navigation.base.trip.model.RouteProgress
|
|
46
|
+
import com.mapbox.navigation.core.arrival.ArrivalObserver
|
|
47
|
+
import com.mapbox.navigation.core.directions.session.RoutesObserver
|
|
48
|
+
import com.mapbox.navigation.core.directions.session.RoutesUpdatedResult
|
|
49
|
+
import com.mapbox.navigation.core.formatter.MapboxDistanceFormatter
|
|
50
|
+
import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp
|
|
51
|
+
import com.mapbox.navigation.core.mapmatching.MapMatchingAPICallback
|
|
52
|
+
import com.mapbox.navigation.core.mapmatching.MapMatchingFailure
|
|
53
|
+
import com.mapbox.navigation.core.mapmatching.MapMatchingOptions
|
|
54
|
+
import com.mapbox.navigation.core.mapmatching.MapMatchingSuccessfulResult
|
|
55
|
+
import com.mapbox.navigation.core.trip.session.LocationMatcherResult
|
|
56
|
+
import com.mapbox.navigation.core.trip.session.LocationObserver
|
|
57
|
+
import com.mapbox.navigation.core.trip.session.OffRouteObserver
|
|
58
|
+
import com.mapbox.navigation.core.trip.session.RouteProgressObserver
|
|
59
|
+
import com.mapbox.navigation.core.trip.session.VoiceInstructionsObserver
|
|
60
|
+
import com.mapbox.navigation.tripdata.maneuver.api.MapboxManeuverApi
|
|
61
|
+
import com.mapbox.navigation.tripdata.progress.api.MapboxTripProgressApi
|
|
62
|
+
import com.mapbox.navigation.tripdata.progress.model.DistanceRemainingFormatter
|
|
63
|
+
import com.mapbox.navigation.tripdata.progress.model.EstimatedTimeToArrivalFormatter
|
|
64
|
+
import com.mapbox.navigation.tripdata.progress.model.TimeRemainingFormatter
|
|
65
|
+
import com.mapbox.navigation.tripdata.progress.model.TripProgressUpdateFormatter
|
|
66
|
+
import com.mapbox.navigation.tripdata.progress.model.TripProgressUpdateValue
|
|
67
|
+
import com.mapbox.navigation.ui.base.util.MapboxNavigationConsumer
|
|
68
|
+
import com.mapbox.navigation.ui.components.maneuver.model.ManeuverPrimaryOptions
|
|
69
|
+
import com.mapbox.navigation.ui.components.maneuver.model.ManeuverViewOptions
|
|
70
|
+
import com.mapbox.navigation.ui.components.maneuver.view.MapboxManeuverView
|
|
71
|
+
import com.mapbox.navigation.ui.components.maps.camera.view.MapboxRecenterButton
|
|
72
|
+
import com.mapbox.navigation.ui.components.maps.camera.view.MapboxRouteOverviewButton
|
|
73
|
+
import com.mapbox.navigation.ui.components.voice.view.MapboxSoundButton
|
|
74
|
+
import com.mapbox.navigation.ui.maps.camera.NavigationCamera
|
|
75
|
+
import com.mapbox.navigation.ui.maps.camera.data.FollowingFrameOptions.FocalPoint
|
|
76
|
+
import com.mapbox.navigation.ui.maps.camera.data.MapboxNavigationViewportDataSource
|
|
77
|
+
import com.mapbox.navigation.ui.maps.camera.lifecycle.NavigationBasicGesturesHandler
|
|
78
|
+
import com.mapbox.navigation.ui.maps.camera.state.NavigationCameraState
|
|
79
|
+
import com.mapbox.navigation.ui.maps.camera.transition.NavigationCameraTransitionOptions
|
|
80
|
+
import com.mapbox.navigation.ui.maps.location.NavigationLocationProvider
|
|
81
|
+
import com.mapbox.navigation.ui.maps.route.RouteLayerConstants.TOP_LEVEL_ROUTE_LINE_LAYER_ID
|
|
82
|
+
import com.mapbox.navigation.ui.maps.route.arrow.api.MapboxRouteArrowApi
|
|
83
|
+
import com.mapbox.navigation.ui.maps.route.arrow.api.MapboxRouteArrowView
|
|
84
|
+
import com.mapbox.navigation.ui.maps.route.arrow.model.RouteArrowOptions
|
|
85
|
+
import com.mapbox.navigation.ui.maps.route.line.api.MapboxRouteLineApi
|
|
86
|
+
import com.mapbox.navigation.ui.maps.route.line.api.MapboxRouteLineView
|
|
87
|
+
import com.mapbox.navigation.ui.maps.route.line.model.*
|
|
88
|
+
import com.mapbox.navigation.voice.api.*
|
|
89
|
+
import com.mapbox.navigation.voice.model.SpeechAnnouncement
|
|
90
|
+
import com.mapbox.navigation.voice.model.SpeechError
|
|
91
|
+
import com.mapbox.navigation.voice.model.SpeechValue
|
|
92
|
+
import com.mapbox.navigation.voice.model.SpeechVolume
|
|
93
|
+
import expo.modules.kotlin.AppContext
|
|
94
|
+
import expo.modules.kotlin.viewevent.EventDispatcher
|
|
95
|
+
import expo.modules.kotlin.views.ExpoView
|
|
96
|
+
import java.util.Locale
|
|
97
|
+
|
|
98
|
+
val PIXEL_DENSITY = Resources.getSystem().displayMetrics.density
|
|
99
|
+
|
|
100
|
+
class ExpoMapboxNavigationView(context: Context, appContext: AppContext) :
|
|
101
|
+
ExpoView(context, appContext) {
|
|
102
|
+
|
|
103
|
+
// Set view tree lifecycle owner to the activity
|
|
104
|
+
// Otherwise leads to crashes when mapbox package tries to query the view tree lifecycle
|
|
105
|
+
// owner
|
|
106
|
+
// and gets null. Not set automatically for some reason.
|
|
107
|
+
// https://github.com/mapbox/mapbox-navigation-android/blob/188d4781b31bb328733eeca593edc8087e38d915/ui-utils/src/main/java/com/mapbox/navigation/ui/utils/internal/lifecycle/ViewLifecycleRegistry.kt#L67
|
|
108
|
+
init {
|
|
109
|
+
this.setViewTreeLifecycleOwner(
|
|
110
|
+
appContext.activityProvider?.currentActivity as LifecycleOwner
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private var isMuted = false
|
|
115
|
+
private var currentCoordinates: List<Point>? = null
|
|
116
|
+
private var currentLocale = Locale.getDefault()
|
|
117
|
+
private var currentWaypointIndices: List<Int>? = null
|
|
118
|
+
private var currentRoutesRequestId: Long? = null
|
|
119
|
+
private var currentMapMatchingRequestId: Long? = null
|
|
120
|
+
private var isUsingRouteMatchingApi = false
|
|
121
|
+
private var currentRouteProfile: String? = null
|
|
122
|
+
private var currentRouteExcludeList: List<String>? = null
|
|
123
|
+
private var currentMapStyle: String? = null
|
|
124
|
+
private var currentCustomRasterSourceUrl: String? = null
|
|
125
|
+
private var currentPlaceCustomRasterLayerAbove: String? = null
|
|
126
|
+
private var currentDisableAlternativeRoutes: Boolean? = null
|
|
127
|
+
private var currentFollowingZoom: Double? = null
|
|
128
|
+
private var vehicleMaxHeight: Double? = null
|
|
129
|
+
private var vehicleMaxWidth: Double? = null
|
|
130
|
+
|
|
131
|
+
private val onRouteProgressChanged by EventDispatcher()
|
|
132
|
+
private val onCancelNavigation by EventDispatcher()
|
|
133
|
+
private val onWaypointArrival by EventDispatcher()
|
|
134
|
+
private val onFinalDestinationArrival by EventDispatcher()
|
|
135
|
+
private val onRouteChanged by EventDispatcher()
|
|
136
|
+
private val onUserOffRoute by EventDispatcher()
|
|
137
|
+
private val onRoutesLoaded by EventDispatcher()
|
|
138
|
+
private val onRouteFailedToLoad by EventDispatcher()
|
|
139
|
+
|
|
140
|
+
private val mapboxNavigation = MapboxNavigationApp.current()
|
|
141
|
+
private var mapboxStyle: Style? = null
|
|
142
|
+
private val navigationLocationProvider = NavigationLocationProvider()
|
|
143
|
+
private var voiceInstructionsPlayer =
|
|
144
|
+
MapboxVoiceInstructionsPlayer(context, currentLocale.toLanguageTag())
|
|
145
|
+
|
|
146
|
+
private val parentConstraintLayout =
|
|
147
|
+
ConstraintLayout(context).also {
|
|
148
|
+
addView(it, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private val mapViewId = 1
|
|
152
|
+
private val mapView = createMapView(mapViewId, parentConstraintLayout)
|
|
153
|
+
private val mapboxMap = mapView.mapboxMap
|
|
154
|
+
|
|
155
|
+
private val viewportDataSource = createViewportDataSource(mapboxMap)
|
|
156
|
+
private val navigationCamera =
|
|
157
|
+
NavigationCamera(mapboxMap, mapView.camera, viewportDataSource).apply {
|
|
158
|
+
mapView.camera.addCameraAnimationsLifecycleListener(
|
|
159
|
+
NavigationBasicGesturesHandler(this)
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private val maneuverViewId = 2
|
|
164
|
+
private val maneuverView = createManueverView(maneuverViewId, parentConstraintLayout)
|
|
165
|
+
|
|
166
|
+
private val tripProgressViewId = 3
|
|
167
|
+
private val tripProgressTimeRemainingTextView = createCenteredTextView()
|
|
168
|
+
private val tripProgressDistanceRemainingTextView = createCenteredTextView()
|
|
169
|
+
private val tripProgressArrivalTimeTextView = createCenteredTextView()
|
|
170
|
+
private val tripProgressView =
|
|
171
|
+
createTripProgressView(
|
|
172
|
+
id = tripProgressViewId,
|
|
173
|
+
parent = parentConstraintLayout,
|
|
174
|
+
tripProgressTimeRemainingTextView = tripProgressTimeRemainingTextView,
|
|
175
|
+
tripProgressDistanceRemainingTextView = tripProgressDistanceRemainingTextView,
|
|
176
|
+
tripProgressArrivalTimeTextView = tripProgressArrivalTimeTextView
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
private val soundButtonId = 4
|
|
180
|
+
private val soundButton =
|
|
181
|
+
createSoundButton(soundButtonId, parentConstraintLayout) {
|
|
182
|
+
voiceInstructionsPlayer.volume(SpeechVolume(if (isMuted) 1.0f else 0.0f))
|
|
183
|
+
it.findViewById<ImageView>(com.mapbox.navigation.ui.components.R.id.buttonIcon)
|
|
184
|
+
.setImageResource(
|
|
185
|
+
if (isMuted) R.drawable.icon_sound else R.drawable.icon_mute
|
|
186
|
+
)
|
|
187
|
+
isMuted = !isMuted
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private val overviewButtonId = 5
|
|
191
|
+
private val overviewButton =
|
|
192
|
+
createOverviewButton(overviewButtonId, parentConstraintLayout) {
|
|
193
|
+
navigationCamera.requestNavigationCameraToOverview()
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private val recenterButtonId = 6
|
|
197
|
+
private val recenterButton =
|
|
198
|
+
createRecenterButton(recenterButtonId, parentConstraintLayout) {
|
|
199
|
+
navigationCamera.requestNavigationCameraToFollowing()
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private val cancelButtonId = 7
|
|
203
|
+
private val cancelButton =
|
|
204
|
+
createCancelButton(cancelButtonId, parentConstraintLayout) {
|
|
205
|
+
onCancelNavigation(mapOf())
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
private val parentConstraintSet =
|
|
209
|
+
createAndApplyConstraintSet(
|
|
210
|
+
mapViewId = mapViewId,
|
|
211
|
+
maneuverViewId = maneuverViewId,
|
|
212
|
+
tripProgressViewId = tripProgressViewId,
|
|
213
|
+
soundButtonId = soundButtonId,
|
|
214
|
+
overviewButtonId = overviewButtonId,
|
|
215
|
+
recenterButtonId = recenterButtonId,
|
|
216
|
+
cancelButtonId = cancelButtonId,
|
|
217
|
+
constraintLayout = parentConstraintLayout
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
private val routeLineApiOptions = MapboxRouteLineApiOptions.Builder().build()
|
|
221
|
+
private val routeLineApi = MapboxRouteLineApi(routeLineApiOptions)
|
|
222
|
+
|
|
223
|
+
private val routeLineViewOptions =
|
|
224
|
+
MapboxRouteLineViewOptions.Builder(context).routeLineBelowLayerId("road-label").build()
|
|
225
|
+
private val routeLineView = MapboxRouteLineView(routeLineViewOptions)
|
|
226
|
+
|
|
227
|
+
private val routeArrow = MapboxRouteArrowApi()
|
|
228
|
+
private val routeArrowOptions =
|
|
229
|
+
RouteArrowOptions.Builder(context)
|
|
230
|
+
.withAboveLayerId(TOP_LEVEL_ROUTE_LINE_LAYER_ID)
|
|
231
|
+
.build()
|
|
232
|
+
private val routeArrowView = MapboxRouteArrowView(routeArrowOptions)
|
|
233
|
+
|
|
234
|
+
private val distanceFormatter = DistanceFormatterOptions.Builder(context).build()
|
|
235
|
+
private var maneuverApi = MapboxManeuverApi(MapboxDistanceFormatter(distanceFormatter))
|
|
236
|
+
|
|
237
|
+
private var tripProgressFormatter =
|
|
238
|
+
TripProgressUpdateFormatter.Builder(context)
|
|
239
|
+
.distanceRemainingFormatter(DistanceRemainingFormatter(distanceFormatter))
|
|
240
|
+
.timeRemainingFormatter(TimeRemainingFormatter(context))
|
|
241
|
+
.estimatedTimeToArrivalFormatter(EstimatedTimeToArrivalFormatter(context))
|
|
242
|
+
.build()
|
|
243
|
+
private var tripProgressApi = MapboxTripProgressApi(tripProgressFormatter)
|
|
244
|
+
|
|
245
|
+
private var speechApi = MapboxSpeechApi(context, currentLocale.toLanguageTag())
|
|
246
|
+
private val voiceInstructionsPlayerCallback =
|
|
247
|
+
MapboxNavigationConsumer<SpeechAnnouncement> { value -> speechApi.clean(value) }
|
|
248
|
+
|
|
249
|
+
private val speechCallback =
|
|
250
|
+
MapboxNavigationConsumer<Expected<SpeechError, SpeechValue>> { expected ->
|
|
251
|
+
expected.fold(
|
|
252
|
+
{ error ->
|
|
253
|
+
voiceInstructionsPlayer.play(
|
|
254
|
+
error.fallback,
|
|
255
|
+
voiceInstructionsPlayerCallback
|
|
256
|
+
)
|
|
257
|
+
},
|
|
258
|
+
{ value ->
|
|
259
|
+
voiceInstructionsPlayer.play(
|
|
260
|
+
value.announcement,
|
|
261
|
+
voiceInstructionsPlayerCallback
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
private val voiceInstructionsObserver = VoiceInstructionsObserver { voiceInstructions ->
|
|
267
|
+
speechApi.generate(voiceInstructions, speechCallback)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
private val routesRequestCallback =
|
|
271
|
+
object : NavigationRouterCallback {
|
|
272
|
+
override fun onRoutesReady(
|
|
273
|
+
routes: List<NavigationRoute>,
|
|
274
|
+
@RouterOrigin routerOrigin: String
|
|
275
|
+
) {
|
|
276
|
+
onRoutesReady(routes)
|
|
277
|
+
}
|
|
278
|
+
override fun onCanceled(
|
|
279
|
+
routeOptions: RouteOptions,
|
|
280
|
+
@RouterOrigin routerOrigin: String
|
|
281
|
+
) {}
|
|
282
|
+
override fun onFailure(reasons: List<RouterFailure>, routeOptions: RouteOptions) {
|
|
283
|
+
onRouteFailedToLoad(mapOf("errorMessage" to reasons.first().message))
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
|
|
288
|
+
private val mapMatchingRequestCallback =
|
|
289
|
+
object : MapMatchingAPICallback {
|
|
290
|
+
override fun success(result: MapMatchingSuccessfulResult) {
|
|
291
|
+
onRoutesReady(result.navigationRoutes)
|
|
292
|
+
}
|
|
293
|
+
override fun onCancel() {}
|
|
294
|
+
override fun failure(failure: MapMatchingFailure) {}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private val routesObserver =
|
|
298
|
+
object : RoutesObserver {
|
|
299
|
+
override fun onRoutesChanged(result: RoutesUpdatedResult) {
|
|
300
|
+
onRoutesLoaded(
|
|
301
|
+
mapOf(
|
|
302
|
+
"routes" to
|
|
303
|
+
mapOf(
|
|
304
|
+
"mainRoute" to
|
|
305
|
+
convertRoute(
|
|
306
|
+
result.navigationRoutes.first()
|
|
307
|
+
),
|
|
308
|
+
"alternativeRoutes" to
|
|
309
|
+
result.navigationRoutes.drop(1).map {
|
|
310
|
+
convertRoute(it)
|
|
311
|
+
}
|
|
312
|
+
)
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
// Handle viewport data source
|
|
317
|
+
if (result.navigationRoutes.isNotEmpty()) {
|
|
318
|
+
viewportDataSource.onRouteChanged(result.navigationRoutes.first())
|
|
319
|
+
viewportDataSource.evaluate()
|
|
320
|
+
} else {
|
|
321
|
+
viewportDataSource.clearRouteData()
|
|
322
|
+
viewportDataSource.evaluate()
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Handle route lines
|
|
326
|
+
val alternativesMetadata =
|
|
327
|
+
mapboxNavigation?.getAlternativeMetadataFor(result.navigationRoutes)
|
|
328
|
+
if (alternativesMetadata != null) {
|
|
329
|
+
routeLineApi.setNavigationRoutes(
|
|
330
|
+
result.navigationRoutes,
|
|
331
|
+
alternativesMetadata
|
|
332
|
+
) { value ->
|
|
333
|
+
mapboxStyle?.let { routeLineView.renderRouteDrawData(it, value) }
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Clear speech
|
|
338
|
+
speechApi.cancel()
|
|
339
|
+
voiceInstructionsPlayer.clear()
|
|
340
|
+
|
|
341
|
+
// Add observer to navigation camera
|
|
342
|
+
navigationCamera.registerNavigationCameraStateChangeObserver {
|
|
343
|
+
navigationCameraState ->
|
|
344
|
+
// shows/hide the recenter button depending on the camera
|
|
345
|
+
// state
|
|
346
|
+
when (navigationCameraState) {
|
|
347
|
+
NavigationCameraState.TRANSITION_TO_FOLLOWING,
|
|
348
|
+
NavigationCameraState.FOLLOWING -> recenterButton.visibility = View.GONE
|
|
349
|
+
NavigationCameraState.TRANSITION_TO_OVERVIEW,
|
|
350
|
+
NavigationCameraState.OVERVIEW,
|
|
351
|
+
NavigationCameraState.IDLE -> recenterButton.visibility = View.VISIBLE
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
this@ExpoMapboxNavigationView.onRouteChanged(mapOf())
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
private val routeProgressObserver =
|
|
360
|
+
object : RouteProgressObserver {
|
|
361
|
+
override fun onRouteProgressChanged(routeProgress: RouteProgress) {
|
|
362
|
+
// Handle viewport data source
|
|
363
|
+
viewportDataSource.onRouteProgressChanged(routeProgress)
|
|
364
|
+
viewportDataSource.evaluate()
|
|
365
|
+
|
|
366
|
+
// Handle route lines
|
|
367
|
+
routeLineApi.updateWithRouteProgress(routeProgress) { result ->
|
|
368
|
+
mapboxStyle?.let { routeLineView.renderRouteLineUpdate(it, result) }
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Handle route arrows
|
|
372
|
+
val updatedManeuverArrow = routeArrow.addUpcomingManeuverArrow(routeProgress)
|
|
373
|
+
mapboxStyle?.let {
|
|
374
|
+
routeArrowView.renderManeuverUpdate(it, updatedManeuverArrow)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Handle manuevers
|
|
378
|
+
val maneuvers = maneuverApi.getManeuvers(routeProgress)
|
|
379
|
+
maneuverView.renderManeuvers(maneuvers)
|
|
380
|
+
|
|
381
|
+
// Handle trip progress view
|
|
382
|
+
tripProgressApi.getTripProgress(routeProgress).let {
|
|
383
|
+
update: TripProgressUpdateValue ->
|
|
384
|
+
val formatter = update.formatter
|
|
385
|
+
tripProgressTimeRemainingTextView.text =
|
|
386
|
+
formatter.getTimeRemaining(update.totalTimeRemaining)
|
|
387
|
+
tripProgressDistanceRemainingTextView.text =
|
|
388
|
+
formatter.getDistanceRemaining(update.distanceRemaining)
|
|
389
|
+
tripProgressArrivalTimeTextView.text =
|
|
390
|
+
formatter.getEstimatedTimeToArrival(update.estimatedTimeToArrival)
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Send progress event
|
|
394
|
+
this@ExpoMapboxNavigationView.onRouteProgressChanged(
|
|
395
|
+
mapOf(
|
|
396
|
+
"distanceRemaining" to routeProgress.distanceRemaining,
|
|
397
|
+
"distanceTraveled" to routeProgress.distanceTraveled,
|
|
398
|
+
"durationRemaining" to routeProgress.durationRemaining,
|
|
399
|
+
"fractionTraveled" to routeProgress.fractionTraveled
|
|
400
|
+
)
|
|
401
|
+
)
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
private val locationObserver =
|
|
406
|
+
object : LocationObserver {
|
|
407
|
+
override fun onNewLocationMatcherResult(
|
|
408
|
+
locationMatcherResult: LocationMatcherResult
|
|
409
|
+
) {}
|
|
410
|
+
override fun onNewRawLocation(rawLocation: com.mapbox.common.location.Location) {
|
|
411
|
+
// Update puck location
|
|
412
|
+
navigationLocationProvider.changePosition(
|
|
413
|
+
location = rawLocation,
|
|
414
|
+
)
|
|
415
|
+
// Update viewport data source
|
|
416
|
+
viewportDataSource.onLocationChanged(rawLocation)
|
|
417
|
+
viewportDataSource.evaluate()
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private val arrivalObserver =
|
|
422
|
+
object : ArrivalObserver {
|
|
423
|
+
override fun onWaypointArrival(routeProgress: RouteProgress) {
|
|
424
|
+
onWaypointArrival(
|
|
425
|
+
mapOf(
|
|
426
|
+
"distanceRemaining" to routeProgress.distanceRemaining,
|
|
427
|
+
"distanceTraveled" to routeProgress.distanceTraveled,
|
|
428
|
+
"durationRemaining" to routeProgress.durationRemaining,
|
|
429
|
+
"fractionTraveled" to routeProgress.fractionTraveled
|
|
430
|
+
)
|
|
431
|
+
)
|
|
432
|
+
}
|
|
433
|
+
override fun onNextRouteLegStart(routeLegProgress: RouteLegProgress) {}
|
|
434
|
+
override fun onFinalDestinationArrival(routeProgress: RouteProgress) {
|
|
435
|
+
onFinalDestinationArrival(mapOf())
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
private val offRouteObserver =
|
|
440
|
+
object : OffRouteObserver {
|
|
441
|
+
override fun onOffRouteStateChanged(offRoute: Boolean) {
|
|
442
|
+
if (offRoute) {
|
|
443
|
+
onUserOffRoute(mapOf())
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
private val onIndicatorPositionChangedListener = OnIndicatorPositionChangedListener { point ->
|
|
449
|
+
val result = routeLineApi.updateTraveledRouteLine(point)
|
|
450
|
+
mapboxStyle?.let { routeLineView.renderRouteLineUpdate(it, result) }
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
private fun createMapView(id: Int, parent: ViewGroup): MapView {
|
|
454
|
+
return MapView(context).apply {
|
|
455
|
+
setId(id)
|
|
456
|
+
parent.addView(this)
|
|
457
|
+
|
|
458
|
+
mapboxMap.loadStyle(Style.MAPBOX_STREETS) { style: Style -> mapboxStyle = style }
|
|
459
|
+
|
|
460
|
+
location.apply {
|
|
461
|
+
locationPuck =
|
|
462
|
+
LocationPuck2D(
|
|
463
|
+
bearingImage =
|
|
464
|
+
ImageHolder.from(
|
|
465
|
+
com.mapbox
|
|
466
|
+
.navigation
|
|
467
|
+
.ui
|
|
468
|
+
.components
|
|
469
|
+
.R
|
|
470
|
+
.drawable
|
|
471
|
+
.mapbox_navigation_puck_icon
|
|
472
|
+
),
|
|
473
|
+
)
|
|
474
|
+
setLocationProvider(navigationLocationProvider)
|
|
475
|
+
puckBearingEnabled = true
|
|
476
|
+
enabled = true
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
private fun createManueverView(id: Int, parent: ViewGroup): MapboxManeuverView {
|
|
482
|
+
return MapboxManeuverView(context).apply {
|
|
483
|
+
setId(id)
|
|
484
|
+
parent.addView(this)
|
|
485
|
+
|
|
486
|
+
val maneuverViewOptions =
|
|
487
|
+
ManeuverViewOptions.Builder()
|
|
488
|
+
.primaryManeuverOptions(
|
|
489
|
+
ManeuverPrimaryOptions.Builder()
|
|
490
|
+
.textAppearance(R.style.ManeuverTextAppearance)
|
|
491
|
+
.build()
|
|
492
|
+
)
|
|
493
|
+
.build()
|
|
494
|
+
|
|
495
|
+
updateManeuverViewOptions(maneuverViewOptions)
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
private fun createCenteredTextView(): TextView {
|
|
500
|
+
return TextView(context).apply { setGravity(Gravity.CENTER) }
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
private fun createTripProgressView(
|
|
504
|
+
id: Int,
|
|
505
|
+
parent: ViewGroup,
|
|
506
|
+
tripProgressTimeRemainingTextView: TextView,
|
|
507
|
+
tripProgressDistanceRemainingTextView: TextView,
|
|
508
|
+
tripProgressArrivalTimeTextView: TextView
|
|
509
|
+
): LinearLayout {
|
|
510
|
+
return LinearLayout(context).apply {
|
|
511
|
+
setId(id)
|
|
512
|
+
parent.addView(this)
|
|
513
|
+
setOrientation(LinearLayout.VERTICAL)
|
|
514
|
+
setBackgroundColor(Color.WHITE)
|
|
515
|
+
setPadding(
|
|
516
|
+
(5 * PIXEL_DENSITY).toInt(),
|
|
517
|
+
(5 * PIXEL_DENSITY).toInt(),
|
|
518
|
+
(5 * PIXEL_DENSITY).toInt(),
|
|
519
|
+
(5 * PIXEL_DENSITY).toInt()
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
addView(
|
|
523
|
+
tripProgressTimeRemainingTextView,
|
|
524
|
+
LayoutParams.MATCH_PARENT,
|
|
525
|
+
(40 * PIXEL_DENSITY).toInt()
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
val bottomContainer =
|
|
529
|
+
LinearLayout(context).apply {
|
|
530
|
+
setOrientation(LinearLayout.HORIZONTAL)
|
|
531
|
+
setGravity(Gravity.CENTER)
|
|
532
|
+
addView(
|
|
533
|
+
tripProgressDistanceRemainingTextView,
|
|
534
|
+
(60 * PIXEL_DENSITY).toInt(),
|
|
535
|
+
(20 * PIXEL_DENSITY).toInt()
|
|
536
|
+
)
|
|
537
|
+
addView(
|
|
538
|
+
tripProgressArrivalTimeTextView,
|
|
539
|
+
(60 * PIXEL_DENSITY).toInt(),
|
|
540
|
+
(20 * PIXEL_DENSITY).toInt()
|
|
541
|
+
)
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
addView(bottomContainer, LayoutParams.MATCH_PARENT, (20 * PIXEL_DENSITY).toInt())
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
private fun createSoundButton(
|
|
549
|
+
id: Int,
|
|
550
|
+
parent: ViewGroup,
|
|
551
|
+
onClick: (MapboxSoundButton) -> Unit
|
|
552
|
+
): MapboxSoundButton {
|
|
553
|
+
return MapboxSoundButton(context).apply {
|
|
554
|
+
setId(id)
|
|
555
|
+
parent.addView(this)
|
|
556
|
+
findViewById<ImageView>(com.mapbox.navigation.ui.components.R.id.buttonIcon)
|
|
557
|
+
.setImageResource(R.drawable.icon_sound)
|
|
558
|
+
setOnClickListener { onClick(this) }
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
private fun createOverviewButton(
|
|
563
|
+
id: Int,
|
|
564
|
+
parent: ViewGroup,
|
|
565
|
+
onClick: () -> Unit
|
|
566
|
+
): MapboxRouteOverviewButton {
|
|
567
|
+
return MapboxRouteOverviewButton(context).apply {
|
|
568
|
+
setId(id)
|
|
569
|
+
parent.addView(this)
|
|
570
|
+
findViewById<ImageView>(com.mapbox.navigation.ui.components.R.id.buttonIcon)
|
|
571
|
+
.setImageResource(R.drawable.icon_overview)
|
|
572
|
+
setOnClickListener { onClick() }
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
private fun createRecenterButton(
|
|
577
|
+
id: Int,
|
|
578
|
+
parent: ViewGroup,
|
|
579
|
+
onClick: () -> Unit
|
|
580
|
+
): MapboxRecenterButton {
|
|
581
|
+
return MapboxRecenterButton(context).apply {
|
|
582
|
+
setId(id)
|
|
583
|
+
findViewById<ImageView>(com.mapbox.navigation.ui.components.R.id.buttonIcon)
|
|
584
|
+
.setImageResource(R.drawable.icon_compass)
|
|
585
|
+
parent.addView(this)
|
|
586
|
+
setVisibility(View.GONE)
|
|
587
|
+
setOnClickListener { onClick() }
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
private fun createCancelButton(
|
|
592
|
+
id: Int,
|
|
593
|
+
parent: ViewGroup,
|
|
594
|
+
onClick: (MapboxSoundButton) -> Unit
|
|
595
|
+
): MapboxSoundButton {
|
|
596
|
+
return MapboxSoundButton(context).apply {
|
|
597
|
+
setId(id)
|
|
598
|
+
parent.addView(this)
|
|
599
|
+
findViewById<ImageView>(com.mapbox.navigation.ui.components.R.id.buttonIcon)
|
|
600
|
+
.setImageResource(R.drawable.icon_x)
|
|
601
|
+
setOnClickListener { onClick(this) }
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
private fun createAndApplyConstraintSet(
|
|
606
|
+
mapViewId: Int,
|
|
607
|
+
maneuverViewId: Int,
|
|
608
|
+
tripProgressViewId: Int,
|
|
609
|
+
soundButtonId: Int,
|
|
610
|
+
overviewButtonId: Int,
|
|
611
|
+
recenterButtonId: Int,
|
|
612
|
+
cancelButtonId: Int,
|
|
613
|
+
constraintLayout: ConstraintLayout
|
|
614
|
+
): ConstraintSet {
|
|
615
|
+
return ConstraintSet().apply {
|
|
616
|
+
// Add MapView constraints
|
|
617
|
+
connect(mapViewId, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP)
|
|
618
|
+
connect(mapViewId, ConstraintSet.BOTTOM, tripProgressViewId, ConstraintSet.TOP)
|
|
619
|
+
connect(mapViewId, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START)
|
|
620
|
+
connect(mapViewId, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END)
|
|
621
|
+
|
|
622
|
+
// Add ManeuverView constraints
|
|
623
|
+
connect(
|
|
624
|
+
maneuverViewId,
|
|
625
|
+
ConstraintSet.TOP,
|
|
626
|
+
mapViewId,
|
|
627
|
+
ConstraintSet.TOP,
|
|
628
|
+
(4 * PIXEL_DENSITY).toInt()
|
|
629
|
+
)
|
|
630
|
+
connect(
|
|
631
|
+
maneuverViewId,
|
|
632
|
+
ConstraintSet.START,
|
|
633
|
+
ConstraintSet.PARENT_ID,
|
|
634
|
+
ConstraintSet.START,
|
|
635
|
+
(4 * PIXEL_DENSITY).toInt()
|
|
636
|
+
)
|
|
637
|
+
connect(
|
|
638
|
+
maneuverViewId,
|
|
639
|
+
ConstraintSet.END,
|
|
640
|
+
ConstraintSet.PARENT_ID,
|
|
641
|
+
ConstraintSet.END,
|
|
642
|
+
(4 * PIXEL_DENSITY).toInt()
|
|
643
|
+
)
|
|
644
|
+
constrainHeight(maneuverViewId, ConstraintSet.WRAP_CONTENT)
|
|
645
|
+
|
|
646
|
+
// Add TripProgressView constraints
|
|
647
|
+
connect(
|
|
648
|
+
tripProgressViewId,
|
|
649
|
+
ConstraintSet.BOTTOM,
|
|
650
|
+
ConstraintSet.PARENT_ID,
|
|
651
|
+
ConstraintSet.BOTTOM
|
|
652
|
+
)
|
|
653
|
+
connect(
|
|
654
|
+
tripProgressViewId,
|
|
655
|
+
ConstraintSet.START,
|
|
656
|
+
ConstraintSet.PARENT_ID,
|
|
657
|
+
ConstraintSet.START
|
|
658
|
+
)
|
|
659
|
+
connect(
|
|
660
|
+
tripProgressViewId,
|
|
661
|
+
ConstraintSet.END,
|
|
662
|
+
ConstraintSet.PARENT_ID,
|
|
663
|
+
ConstraintSet.END
|
|
664
|
+
)
|
|
665
|
+
constrainMinHeight(tripProgressViewId, (80 * PIXEL_DENSITY).toInt())
|
|
666
|
+
constrainWidth(tripProgressViewId, ConstraintSet.MATCH_CONSTRAINT)
|
|
667
|
+
|
|
668
|
+
// Add SoundButton constraints
|
|
669
|
+
connect(
|
|
670
|
+
soundButtonId,
|
|
671
|
+
ConstraintSet.TOP,
|
|
672
|
+
maneuverViewId,
|
|
673
|
+
ConstraintSet.BOTTOM,
|
|
674
|
+
(8 * PIXEL_DENSITY).toInt()
|
|
675
|
+
)
|
|
676
|
+
connect(
|
|
677
|
+
soundButtonId,
|
|
678
|
+
ConstraintSet.END,
|
|
679
|
+
ConstraintSet.PARENT_ID,
|
|
680
|
+
ConstraintSet.END,
|
|
681
|
+
(16 * PIXEL_DENSITY).toInt()
|
|
682
|
+
)
|
|
683
|
+
constrainWidth(soundButtonId, ConstraintSet.WRAP_CONTENT)
|
|
684
|
+
constrainHeight(soundButtonId, ConstraintSet.WRAP_CONTENT)
|
|
685
|
+
|
|
686
|
+
// Add OverviewButton constraints
|
|
687
|
+
connect(
|
|
688
|
+
overviewButtonId,
|
|
689
|
+
ConstraintSet.TOP,
|
|
690
|
+
soundButtonId,
|
|
691
|
+
ConstraintSet.BOTTOM,
|
|
692
|
+
(8 * PIXEL_DENSITY).toInt()
|
|
693
|
+
)
|
|
694
|
+
connect(
|
|
695
|
+
overviewButtonId,
|
|
696
|
+
ConstraintSet.END,
|
|
697
|
+
ConstraintSet.PARENT_ID,
|
|
698
|
+
ConstraintSet.END,
|
|
699
|
+
(16 * PIXEL_DENSITY).toInt()
|
|
700
|
+
)
|
|
701
|
+
constrainWidth(overviewButtonId, ConstraintSet.WRAP_CONTENT)
|
|
702
|
+
constrainHeight(overviewButtonId, ConstraintSet.WRAP_CONTENT)
|
|
703
|
+
|
|
704
|
+
// Add RecenterButton constraints
|
|
705
|
+
connect(
|
|
706
|
+
recenterButtonId,
|
|
707
|
+
ConstraintSet.TOP,
|
|
708
|
+
overviewButtonId,
|
|
709
|
+
ConstraintSet.BOTTOM,
|
|
710
|
+
(8 * PIXEL_DENSITY).toInt()
|
|
711
|
+
)
|
|
712
|
+
connect(
|
|
713
|
+
recenterButtonId,
|
|
714
|
+
ConstraintSet.END,
|
|
715
|
+
ConstraintSet.PARENT_ID,
|
|
716
|
+
ConstraintSet.END,
|
|
717
|
+
(16 * PIXEL_DENSITY).toInt()
|
|
718
|
+
)
|
|
719
|
+
constrainWidth(recenterButtonId, ConstraintSet.WRAP_CONTENT)
|
|
720
|
+
constrainHeight(recenterButtonId, ConstraintSet.WRAP_CONTENT)
|
|
721
|
+
|
|
722
|
+
// Add CancelButton constraints
|
|
723
|
+
connect(cancelButtonId, ConstraintSet.BOTTOM, tripProgressViewId, ConstraintSet.BOTTOM)
|
|
724
|
+
connect(cancelButtonId, ConstraintSet.TOP, tripProgressViewId, ConstraintSet.TOP)
|
|
725
|
+
connect(
|
|
726
|
+
cancelButtonId,
|
|
727
|
+
ConstraintSet.END,
|
|
728
|
+
ConstraintSet.PARENT_ID,
|
|
729
|
+
ConstraintSet.END,
|
|
730
|
+
(16 * PIXEL_DENSITY).toInt()
|
|
731
|
+
)
|
|
732
|
+
constrainWidth(cancelButtonId, ConstraintSet.WRAP_CONTENT)
|
|
733
|
+
constrainHeight(cancelButtonId, ConstraintSet.WRAP_CONTENT)
|
|
734
|
+
|
|
735
|
+
applyTo(constraintLayout)
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
private fun createViewportDataSource(mapboxMap: MapboxMap): MapboxNavigationViewportDataSource {
|
|
740
|
+
val portraitOverviewPadding =
|
|
741
|
+
EdgeInsets(
|
|
742
|
+
140.0 * PIXEL_DENSITY,
|
|
743
|
+
40.0 * PIXEL_DENSITY,
|
|
744
|
+
120.0 * PIXEL_DENSITY,
|
|
745
|
+
40.0 * PIXEL_DENSITY
|
|
746
|
+
)
|
|
747
|
+
val landscapeOverviewPadding =
|
|
748
|
+
EdgeInsets(
|
|
749
|
+
30.0 * PIXEL_DENSITY,
|
|
750
|
+
380.0 * PIXEL_DENSITY,
|
|
751
|
+
110.0 * PIXEL_DENSITY,
|
|
752
|
+
20.0 * PIXEL_DENSITY
|
|
753
|
+
)
|
|
754
|
+
val portraitFollowingPadding =
|
|
755
|
+
EdgeInsets(
|
|
756
|
+
180.0 * PIXEL_DENSITY,
|
|
757
|
+
40.0 * PIXEL_DENSITY,
|
|
758
|
+
150.0 * PIXEL_DENSITY,
|
|
759
|
+
40.0 * PIXEL_DENSITY
|
|
760
|
+
)
|
|
761
|
+
val landscapeFollowingPadding =
|
|
762
|
+
EdgeInsets(
|
|
763
|
+
30.0 * PIXEL_DENSITY,
|
|
764
|
+
380.0 * PIXEL_DENSITY,
|
|
765
|
+
110.0 * PIXEL_DENSITY,
|
|
766
|
+
40.0 * PIXEL_DENSITY
|
|
767
|
+
)
|
|
768
|
+
|
|
769
|
+
return MapboxNavigationViewportDataSource(mapboxMap).apply {
|
|
770
|
+
options.followingFrameOptions.focalPoint = FocalPoint(0.5, 0.9)
|
|
771
|
+
if (context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
|
772
|
+
) {
|
|
773
|
+
followingPadding = landscapeFollowingPadding
|
|
774
|
+
overviewPadding = landscapeOverviewPadding
|
|
775
|
+
} else {
|
|
776
|
+
followingPadding = portraitFollowingPadding
|
|
777
|
+
overviewPadding = portraitOverviewPadding
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
override fun onAttachedToWindow() {
|
|
783
|
+
super.onAttachedToWindow()
|
|
784
|
+
mapboxNavigation?.registerRoutesObserver(routesObserver)
|
|
785
|
+
mapboxNavigation?.registerRouteProgressObserver(routeProgressObserver)
|
|
786
|
+
mapboxNavigation?.registerLocationObserver(locationObserver)
|
|
787
|
+
mapboxNavigation?.registerVoiceInstructionsObserver(voiceInstructionsObserver)
|
|
788
|
+
mapboxNavigation?.registerArrivalObserver(arrivalObserver)
|
|
789
|
+
mapboxNavigation?.registerOffRouteObserver(offRouteObserver)
|
|
790
|
+
mapView.location.addOnIndicatorPositionChangedListener(onIndicatorPositionChangedListener)
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
override fun onDetachedFromWindow() {
|
|
794
|
+
super.onDetachedFromWindow()
|
|
795
|
+
mapboxNavigation?.unregisterRoutesObserver(routesObserver)
|
|
796
|
+
mapboxNavigation?.unregisterRouteProgressObserver(routeProgressObserver)
|
|
797
|
+
mapboxNavigation?.unregisterLocationObserver(locationObserver)
|
|
798
|
+
mapboxNavigation?.unregisterVoiceInstructionsObserver(voiceInstructionsObserver)
|
|
799
|
+
mapboxNavigation?.unregisterArrivalObserver(arrivalObserver)
|
|
800
|
+
mapboxNavigation?.unregisterOffRouteObserver(offRouteObserver)
|
|
801
|
+
if (mapboxNavigation?.isDestroyed != true) {
|
|
802
|
+
mapboxNavigation?.stopTripSession()
|
|
803
|
+
}
|
|
804
|
+
speechApi.cancel()
|
|
805
|
+
voiceInstructionsPlayer.shutdown()
|
|
806
|
+
mapView.location.removeOnIndicatorPositionChangedListener(
|
|
807
|
+
onIndicatorPositionChangedListener
|
|
808
|
+
)
|
|
809
|
+
routeLineApi.cancel()
|
|
810
|
+
routeLineView.cancel()
|
|
811
|
+
maneuverApi.cancel()
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
private fun convertRoute(route: NavigationRoute): Map<String, Any> {
|
|
815
|
+
return mapOf(
|
|
816
|
+
"distance" to route.directionsRoute.distance(),
|
|
817
|
+
"expectedTravelTime" to route.directionsRoute.duration(),
|
|
818
|
+
"legs" to
|
|
819
|
+
route.directionsRoute.legs()?.map { leg ->
|
|
820
|
+
mapOf(
|
|
821
|
+
"steps" to
|
|
822
|
+
leg.steps()?.map { step ->
|
|
823
|
+
val stepGeometry = step.geometry()
|
|
824
|
+
val decodedPoints =
|
|
825
|
+
stepGeometry?.let {
|
|
826
|
+
PolylineUtils.decode(it, 6)
|
|
827
|
+
}
|
|
828
|
+
?: emptyList()
|
|
829
|
+
|
|
830
|
+
mapOf(
|
|
831
|
+
"shape" to
|
|
832
|
+
mapOf(
|
|
833
|
+
"coordinates" to
|
|
834
|
+
decodedPoints.map {
|
|
835
|
+
point ->
|
|
836
|
+
mapOf(
|
|
837
|
+
"latitude" to
|
|
838
|
+
point.latitude(),
|
|
839
|
+
"longitude" to
|
|
840
|
+
point.longitude()
|
|
841
|
+
)
|
|
842
|
+
}
|
|
843
|
+
)
|
|
844
|
+
)
|
|
845
|
+
}
|
|
846
|
+
)
|
|
847
|
+
}
|
|
848
|
+
) as
|
|
849
|
+
Map<String, Any>
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
private fun onRoutesReady(routes: List<NavigationRoute>) {
|
|
853
|
+
mapboxNavigation?.setNavigationRoutes(routes)
|
|
854
|
+
mapboxNavigation?.startTripSession(withForegroundService = false)
|
|
855
|
+
navigationCamera.requestNavigationCameraToFollowing(
|
|
856
|
+
stateTransitionOptions =
|
|
857
|
+
NavigationCameraTransitionOptions.Builder()
|
|
858
|
+
.maxDuration(0) // instant transition
|
|
859
|
+
.build()
|
|
860
|
+
)
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
|
|
864
|
+
fun setCoordinates(coordinates: List<Point>) {
|
|
865
|
+
currentCoordinates = coordinates
|
|
866
|
+
update()
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
|
|
870
|
+
fun setVehicleMaxHeight(maxHeight: Double?) {
|
|
871
|
+
vehicleMaxHeight = maxHeight
|
|
872
|
+
update()
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
|
|
876
|
+
fun setVehicleMaxWidth(maxWidth: Double?) {
|
|
877
|
+
vehicleMaxWidth = maxWidth
|
|
878
|
+
update()
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
|
|
882
|
+
fun setLocale(localeStr: String?) {
|
|
883
|
+
currentLocale =
|
|
884
|
+
if (localeStr == null || localeStr == "default") Locale.getDefault()
|
|
885
|
+
else Locale.Builder().setLanguageTag(localeStr).build()
|
|
886
|
+
update()
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
|
|
890
|
+
fun setWaypointIndices(indices: List<Int>?) {
|
|
891
|
+
currentWaypointIndices = indices
|
|
892
|
+
update()
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
|
|
896
|
+
fun setIsUsingRouteMatchingApi(useRouteMatchingApi: Boolean?) {
|
|
897
|
+
isUsingRouteMatchingApi = useRouteMatchingApi ?: false
|
|
898
|
+
update()
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
|
|
902
|
+
fun setRouteProfile(profile: String?) {
|
|
903
|
+
currentRouteProfile = profile
|
|
904
|
+
update()
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
|
|
908
|
+
fun setRouteExcludeList(excludeList: List<String>?) {
|
|
909
|
+
currentRouteExcludeList = excludeList
|
|
910
|
+
update()
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
|
|
914
|
+
fun setMapStyle(style: String?) {
|
|
915
|
+
currentMapStyle = style
|
|
916
|
+
update()
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
fun setIsMuted(isMutedProp: Boolean?) {
|
|
920
|
+
if (isMutedProp != null) {
|
|
921
|
+
isMuted = isMutedProp
|
|
922
|
+
voiceInstructionsPlayer.volume(SpeechVolume(if (isMuted) 0.0f else 1.0f))
|
|
923
|
+
soundButton
|
|
924
|
+
.findViewById<ImageView>(com.mapbox.navigation.ui.components.R.id.buttonIcon)
|
|
925
|
+
.setImageResource(if (isMuted) R.drawable.icon_mute else R.drawable.icon_sound)
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
fun setInitialLocation(initialLocation: Point?, zoom: Double?) {
|
|
930
|
+
if (initialLocation != null) {
|
|
931
|
+
val cameraOptions =
|
|
932
|
+
CameraOptions.Builder().center(initialLocation).zoom(zoom ?: 14.0).build()
|
|
933
|
+
mapView.getMapboxMap().setCamera(cameraOptions)
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
|
|
938
|
+
fun setCustomRasterSourceUrl(url: String?) {
|
|
939
|
+
currentCustomRasterSourceUrl = url
|
|
940
|
+
update()
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
|
|
944
|
+
fun setPlaceCustomRasterLayerAbove(layerId: String?) {
|
|
945
|
+
currentPlaceCustomRasterLayerAbove = layerId
|
|
946
|
+
update()
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
|
|
950
|
+
fun setDisableAlternativeRoutes(disableAlternativeRoutes: Boolean?) {
|
|
951
|
+
currentDisableAlternativeRoutes = disableAlternativeRoutes
|
|
952
|
+
update()
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
|
|
956
|
+
fun setFollowingZoom(followingZoom: Double?) {
|
|
957
|
+
currentFollowingZoom = followingZoom
|
|
958
|
+
viewportDataSource.followingZoomPropertyOverride(followingZoom)
|
|
959
|
+
viewportDataSource.evaluate()
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
fun recenterMap() {
|
|
963
|
+
navigationCamera.requestNavigationCameraToFollowing()
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
fun addCustomRasterLayer() {
|
|
967
|
+
val style = mapboxMap.getStyle() ?: return
|
|
968
|
+
|
|
969
|
+
val sourceId = "raster-source"
|
|
970
|
+
val layerId = "raster-layer"
|
|
971
|
+
|
|
972
|
+
if (style.styleLayerExists(layerId)) {
|
|
973
|
+
style.removeStyleLayer(layerId)
|
|
974
|
+
}
|
|
975
|
+
if (style.styleSourceExists(sourceId)) {
|
|
976
|
+
style.removeStyleSource(sourceId)
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
if (currentCustomRasterSourceUrl.isNullOrEmpty()) {
|
|
980
|
+
return
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
val rasterSource =
|
|
984
|
+
RasterSource.Builder(sourceId)
|
|
985
|
+
.tileSet(
|
|
986
|
+
TileSet.Builder("tileset", listOf(currentCustomRasterSourceUrl!!))
|
|
987
|
+
.build()
|
|
988
|
+
)
|
|
989
|
+
.tileSize(256)
|
|
990
|
+
.build()
|
|
991
|
+
style.addSource(rasterSource)
|
|
992
|
+
|
|
993
|
+
val rasterLayer = RasterLayer(layerId, sourceId)
|
|
994
|
+
val aboveLayerId = currentPlaceCustomRasterLayerAbove ?: "water"
|
|
995
|
+
style.addLayerAbove(rasterLayer, aboveLayerId)
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
|
|
999
|
+
private fun update() {
|
|
1000
|
+
voiceInstructionsPlayer =
|
|
1001
|
+
MapboxVoiceInstructionsPlayer(context, currentLocale.toLanguageTag())
|
|
1002
|
+
voiceInstructionsPlayer.volume(
|
|
1003
|
+
SpeechVolume(if (isMuted) 0.0f else 1.0f)
|
|
1004
|
+
) // Initial volume based on current isMuted state
|
|
1005
|
+
speechApi = MapboxSpeechApi(context, currentLocale.toLanguageTag())
|
|
1006
|
+
|
|
1007
|
+
if (currentMapStyle != null) {
|
|
1008
|
+
mapboxMap.loadStyle(currentMapStyle!!) { style: Style ->
|
|
1009
|
+
mapboxStyle = style
|
|
1010
|
+
style.localizeLabels(currentLocale)
|
|
1011
|
+
addCustomRasterLayer()
|
|
1012
|
+
}
|
|
1013
|
+
} else {
|
|
1014
|
+
mapboxMap.getStyle { style: Style ->
|
|
1015
|
+
style.localizeLabels(currentLocale)
|
|
1016
|
+
addCustomRasterLayer()
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
val distanceFormatter =
|
|
1021
|
+
DistanceFormatterOptions.Builder(context).locale(currentLocale).build()
|
|
1022
|
+
maneuverApi = MapboxManeuverApi(MapboxDistanceFormatter(distanceFormatter))
|
|
1023
|
+
|
|
1024
|
+
tripProgressFormatter =
|
|
1025
|
+
TripProgressUpdateFormatter.Builder(context)
|
|
1026
|
+
.distanceRemainingFormatter(DistanceRemainingFormatter(distanceFormatter))
|
|
1027
|
+
.timeRemainingFormatter(TimeRemainingFormatter(context, currentLocale))
|
|
1028
|
+
.estimatedTimeToArrivalFormatter(EstimatedTimeToArrivalFormatter(context))
|
|
1029
|
+
.build()
|
|
1030
|
+
tripProgressApi = MapboxTripProgressApi(tripProgressFormatter)
|
|
1031
|
+
|
|
1032
|
+
if (currentMapMatchingRequestId != null) {
|
|
1033
|
+
mapboxNavigation?.cancelMapMatchingRequest(currentMapMatchingRequestId!!)
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
if (currentRoutesRequestId != null) {
|
|
1037
|
+
mapboxNavigation?.cancelRouteRequest(currentRoutesRequestId!!)
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
if (currentCoordinates != null) {
|
|
1041
|
+
if (isUsingRouteMatchingApi) {
|
|
1042
|
+
requestMapMatchingRoutes()
|
|
1043
|
+
} else {
|
|
1044
|
+
requestRoutes()
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
private fun requestRoutes() {
|
|
1050
|
+
var optionsBuilder =
|
|
1051
|
+
RouteOptions.builder()
|
|
1052
|
+
.applyDefaultNavigationOptions()
|
|
1053
|
+
.coordinatesList(currentCoordinates!!)
|
|
1054
|
+
.steps(true)
|
|
1055
|
+
.voiceInstructions(true)
|
|
1056
|
+
.language(currentLocale.toLanguageTag())
|
|
1057
|
+
.maxHeight(vehicleMaxHeight ?: null)
|
|
1058
|
+
.maxWidth(vehicleMaxWidth ?: null)
|
|
1059
|
+
.alternatives(currentDisableAlternativeRoutes != true)
|
|
1060
|
+
|
|
1061
|
+
if (currentWaypointIndices != null) {
|
|
1062
|
+
optionsBuilder = optionsBuilder.waypointIndicesList(currentWaypointIndices!!)
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
if (currentRouteProfile != null) {
|
|
1066
|
+
optionsBuilder = optionsBuilder.profile(currentRouteProfile!!)
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
if (currentRouteExcludeList != null) {
|
|
1070
|
+
optionsBuilder = optionsBuilder.excludeList(currentRouteExcludeList!!)
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
currentRoutesRequestId =
|
|
1074
|
+
mapboxNavigation?.requestRoutes(optionsBuilder.build(), routesRequestCallback)
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
|
|
1078
|
+
private fun requestMapMatchingRoutes() {
|
|
1079
|
+
var optionsBuilder =
|
|
1080
|
+
MapMatchingOptions.Builder()
|
|
1081
|
+
.coordinates(currentCoordinates!!)
|
|
1082
|
+
.bannerInstructions(true)
|
|
1083
|
+
.voiceInstructions(true)
|
|
1084
|
+
.language(currentLocale.toLanguageTag())
|
|
1085
|
+
|
|
1086
|
+
if (currentWaypointIndices != null) {
|
|
1087
|
+
optionsBuilder = optionsBuilder.waypoints(currentWaypointIndices!!)
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
if (currentRouteProfile != null) {
|
|
1091
|
+
optionsBuilder = optionsBuilder.profile(currentRouteProfile!!)
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
currentMapMatchingRequestId =
|
|
1095
|
+
mapboxNavigation?.requestMapMatching(
|
|
1096
|
+
optionsBuilder.build(),
|
|
1097
|
+
mapMatchingRequestCallback
|
|
1098
|
+
)
|
|
1099
|
+
}
|
|
1100
|
+
}
|