diff --git a/CHANGELOG.md b/CHANGELOG.md index 0657fee4a0c..bc1b4ef3c46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,81 @@ # Changelog for the Mapbox Navigation SDK Core Framework for Android +## Navigation SDK Core Framework 3.4.1 - 19 October, 2024 +#### Features + + +#### Bug fixes and improvements +- Optimized CA routes handling by skiping route parsing if it's already exist in direction session [#6868](https://github.com/mapbox/mapbox-navigation-android/pull/6868) +- Improved map-matching experience in tunnels. [#7042](https://github.com/mapbox/mapbox-navigation-android/pull/7042) +- Increased route stickiness in dead reckoning mode. [#7042](https://github.com/mapbox/mapbox-navigation-android/pull/7042) +- Improved odometry and road graph fusing in Manhattan urban canyons. [#7042](https://github.com/mapbox/mapbox-navigation-android/pull/7042) +- Nav SDK now removes passed alternative routes as soon as user passed fork point. [#6813](https://github.com/mapbox/mapbox-navigation-android/pull/6813) + +### Mapbox dependencies +This release depends on, and has been tested with, the following Mapbox dependencies: +- Mapbox Maps SDK `v11.7.1` ([release notes](https://github.com/mapbox/mapbox-maps-android/releases/tag/v11.7.1)) +- Mapbox Navigation Native `v320.2.0` +- Mapbox Core Common `v24.7.1` +- Mapbox Java `v7.2.0` ([release notes](https://github.com/mapbox/mapbox-java/releases/tag/v7.2.0)) + + +## Navigation SDK Core Framework 3.4.0 - 01 October, 2024 +#### Features +- Split MotionData into more accurate parts. Introduced ImuTemperatureData, OrientationData, RawAccelerometer, RawGravity, RawGyroscope and SpeedData. [#6873](https://github.com/mapbox/mapbox-navigation-android/pull/6873) +- Added unique route ID's after each subsequent restart. [#6873](https://github.com/mapbox/mapbox-navigation-android/pull/6873) +- Added filtering online routes that are too far from current location. [#6873](https://github.com/mapbox/mapbox-navigation-android/pull/6873) +- Added rerouting when server unrecognizes current route id to refresh or update alternatives. [#6873](https://github.com/mapbox/mapbox-navigation-android/pull/6873) +- Improved route completion logic. Completed state is triggered only if the puck is leaving the waypoint or after timeout reaching the waypoint threshold. [#6760](https://github.com/mapbox/mapbox-navigation-android/pull/6760) +- Signature of experimental `EtcGateApi#updateEtcGateInfo` function has been changed, now it accepts `EtcGateApi.EtcGateInfo` as a function parameter. [#6508](https://github.com/mapbox/mapbox-navigation-android/pull/6508) +- Experimental Data Inputs functionality has been removed from the `core` module to a separate `datainputs` module (`MapboxNavigation#dataInputs` and everything from the package `com.mapbox.navigation.datainputs` have been removed). [Contact us](https://www.mapbox.com/support) to get more information on how to get access to the module. [#6508](https://github.com/mapbox/mapbox-navigation-android/pull/6508) +- Experimental Adasis functionality has been removed from the `core` module (`MapboxNavigation`'s functions `setAdasisMessageObserver`, `resetAdasisMessageObserver`, `updateExternalSensorData`, and `GraphAccessor#getAdasisEdgeAttributes` have been removed). [Contact us](https://www.mapbox.com/support) in case you're interested in ADASIS functionality. [#6508](https://github.com/mapbox/mapbox-navigation-android/pull/6508) +- Added experimental `RoutingTilesOptions#fallbackNavigationTilesVersion` which lets define version of navigation tiles to fallback in case of offline routing failure with navigation tiles defined in `RoutingTilesOptions#tilesVersion`. [#6475](https://github.com/mapbox/mapbox-navigation-android/pull/6475) +- Added experimental `MapboxRouteLineViewOptions#fadeOnHighZoomsConfig` and `MapboxRouteArrowOptions#fadeOnHighZoomsConfig` to configure smooth fading out of route line or/and arrows on high zoom levels. [#6367](https://github.com/mapbox/mapbox-navigation-android/pull/6367) +- The `PredictiveCacheController(PredictiveCacheOptions)` constructor is now deprecated. Use `PredictiveCacheController(MapboxNavigation, PredictiveCacheOptions)` instead. [#6376](https://github.com/mapbox/mapbox-navigation-android/pull/6376) +- Added `NavigationScaleGestureHandlerOptions#followingRotationAngleThreshold` that define threshold angle for rotation for `FOLLOWING` Navigation Camera state. [#6234](https://github.com/mapbox/mapbox-navigation-android/pull/6234) +- Added the ability to filter road names based on the system language [#6163](https://github.com/mapbox/mapbox-navigation-android/pull/6163) +- `com.mapbox.navigation.base.road.model.RoadComponent` objects that contain only slashes in their text are filtered out [#6163](https://github.com/mapbox/mapbox-navigation-android/pull/6163) +- Now `EHorizonResultType.Type` has a new element called `EHorizonResultType.NOT_AVAILABLE`. [#6290](https://github.com/mapbox/mapbox-navigation-android/pull/6290) +- Old `MapboxNavigation.postUserFeedback()` functions have been deprecated, use an overloading that accepts `UserFeedback` as a parameter. [#5781](https://github.com/mapbox/mapbox-navigation-android/pull/5781) +- Introduce MapboxRouteCalloutApi and MapboxRouteCalloutView to attach callouts to route lines with info about duration [#2743](https://github.com/mapbox/mapbox-navigation-android/pull/2743) +- Optimized memory usage in Directions API model classes by interning frequently occurring strings in JSON. [#5854](https://github.com/mapbox/mapbox-navigation-android/pull/5854) +- Added experimental `MapboxNavigation#replanRoute` to handle cases when user changes route options during active guidance, [#5286](https://github.com/mapbox/mapbox-navigation-android/pull/5286) +for example enabling avoid ferries. +- Added `DataInputsManager` to allow the provision of data from external sensors to the navigator, see `MapboxNavigation.dataInputsManager`. Experimental `EtcGateInfo` has been moved to `com.mapbox.navigation.core.datainputs` package. `EtcGateApi` has been deprecated. [#5957](https://github.com/mapbox/mapbox-navigation-android/pull/5957) +- Removing the ExperimentalMapboxNavigationAPI flag for Search predictive cache. [#5615](https://github.com/mapbox/mapbox-navigation-android/pull/5615) +- [BREAKING CHANGE] `PredictiveCacheOptions.unrecognizedTilesetDescriptorOptions` has been renamed to `PredictiveCacheOptions.predictiveCacheSearchOptionsList`. Additionally, `PredictiveCacheUnrecognizedTilesetDescriptorOptions` has been renamed to `PredictiveCacheSearchOptions`. Now, only search-related options can be passed to `PredictiveCacheSearchOptions`. [#5244](https://github.com/mapbox/mapbox-navigation-android/pull/5244) +- Introduced experimental traffic adjustment mechanism during a drive and added `TrafficOverrideOptions` to control this feature [#2811](https://github.com/mapbox/mapbox-navigation-android/pull/2811) +- Changed `Alternatives` that deviate close to a destination point are removed before a fork is reached. [#5848](https://github.com/mapbox/mapbox-navigation-android/pull/5848) +- Added `RerouteStrategyForMapMatchedRoutes` to `RerouteOptions`. Reroute option `enableLegacyBehaviorForMapMatchedRoute` was removed, use `NavigateToFinalDestination` strategy instead. [#5256](https://github.com/mapbox/mapbox-navigation-android/pull/5256) + +#### Bug fixes and improvements +- Fixed unnecessary reroutes when using onboard router. [#6873](https://github.com/mapbox/mapbox-navigation-android/pull/6873) +- Fixed remaining alternative routes when passing fork points. [#6873](https://github.com/mapbox/mapbox-navigation-android/pull/6873) +- Improved updating alternative routes [#6760](https://github.com/mapbox/mapbox-navigation-android/pull/6760) +- Fixed `CarSearchLocationProvider` produces _NullPointerException_ when using Mapbox Search SDK. [#6702](https://github.com/mapbox/mapbox-navigation-android/pull/6702) +- Fixed a bug causing some history files recorded during the previous app sessions not to be uploaded by the Copilot. [#6359](https://github.com/mapbox/mapbox-navigation-android/pull/6359) +- Fixed an issue where native memory was not being properly released after the `MapboxNavigation` object was destroyed. [#6376](https://github.com/mapbox/mapbox-navigation-android/pull/6376) +- Fixed the issue of unwanted rerouting occurring immediately after setting a new route [#6163](https://github.com/mapbox/mapbox-navigation-android/pull/6163) +- Fixed a crash caused by an overflow in the JNI global reference table. [#6290](https://github.com/mapbox/mapbox-navigation-android/pull/6290) +- Fixed an issue with vignettes in Romania and Bulgaria for offline routing when tolls are excluded. [#6290](https://github.com/mapbox/mapbox-navigation-android/pull/6290) +- Addressed several issues that occurred when switching to an alternative route. [#6290](https://github.com/mapbox/mapbox-navigation-android/pull/6290) +- Implementation of `RerouteController#registerRerouteStateObserver` now invokes observer immediately with current state instead of posting invocation to the main looper. [#5286](https://github.com/mapbox/mapbox-navigation-android/pull/5286) +- Fixed UI jank caused by on-device TextToSpeech player. [#5638](https://github.com/mapbox/mapbox-navigation-android/pull/5638) +- Removed `PredictiveCacheController#removeSearchControllers` and `PredictiveCacheController#createSearchControllers`. Now search predictive cache controller is created and destroyed together with `PredictiveCacheController` instance if `PredictiveCacheOptions.predictiveCacheSearchOptionsList` is provided. [#5714](https://github.com/mapbox/mapbox-navigation-android/pull/5714) +- Improve performance when handling large road objects on the eHorizon's MPP. [#6014](https://github.com/mapbox/mapbox-navigation-android/pull/6014) +- Fixed `Routes` that origin is out of primary route cannot be added as alternatives. [#5848](https://github.com/mapbox/mapbox-navigation-android/pull/5848) +- Fixed a crash due to incorrect OpenLR input data [#5400](https://github.com/mapbox/mapbox-navigation-android/pull/5400) +- Fixed a bug with spinning smoothed coordinate [#5400](https://github.com/mapbox/mapbox-navigation-android/pull/5400) +- Fixed issue for calculating the trim-offset value for the vanishing route line feature when the current geometry index emitted by route progress is greater than the value expected. + +### Mapbox dependencies +This release depends on, and has been tested with, the following Mapbox dependencies: +- Mapbox Maps SDK `v11.7.0` ([release notes](https://github.com/mapbox/mapbox-maps-android/releases/tag/v11.7.0)) +- Mapbox Navigation Native `v320.0.0` +- Mapbox Core Common `v24.7.0` +- Mapbox Java `v7.2.0` ([release notes](https://github.com/mapbox/mapbox-java/releases/tag/v7.2.0)) + + ## Navigation SDK Core Framework 3.4.0-rc.1 - 17 September, 2024 #### Features diff --git a/androidauto/gradle.properties b/androidauto/gradle.properties index aee6c6a38b5..210eac3ca0e 100644 --- a/androidauto/gradle.properties +++ b/androidauto/gradle.properties @@ -1,3 +1,2 @@ -POM_ARTIFACT_ID=android-auto-components POM_ARTIFACT_TITLE=Mapbox Navigation SDK POM_DESCRIPTION=Artifact that provides Android Auto compoents \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 469116820c2..3ace1e24505 100644 --- a/gradle.properties +++ b/gradle.properties @@ -33,5 +33,3 @@ systemProp.kotlin.daemon.jvm.options=-Xss8m # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true - -RELEASE_TAG_PREFIX=v \ No newline at end of file diff --git a/gradle/artifact-properties-patcher.gradle b/gradle/artifact-properties-patcher.gradle new file mode 100644 index 00000000000..02bd9335ae9 --- /dev/null +++ b/gradle/artifact-properties-patcher.gradle @@ -0,0 +1,65 @@ +import groovy.json.JsonSlurper +import groovy.json.JsonOutput + +ext.patchPomDependencies = { Node mainNode -> + def dependenciesNode = mainNode['dependencies'] + + def patched = false + + dependenciesNode.each { depNode -> + // Find all dependencies whose artifactId equals to the name of one of project's modules. + def navSdkDependencyNodes = depNode['dependency'].findAll { dependency -> + project.ext.navSdkArtifactSettings.containsKey(dependency['artifactId'].text()) + } + + navSdkDependencyNodes.each { navSdkNode -> + patched = true + + def artifactId = navSdkNode['artifactId'].text() + navSdkNode['artifactId'][0].setValue(project.ext.navSdkArtifactSettings[artifactId].getV1()) + + def versionNode = navSdkNode['version'] + if (versionNode) { + versionNode[0].setValue(project.ext.versionName) + } else { + navSdkNode.appendNode('version', project.ext.versionName) + } + } + } + + if (patched) { + println("pom file has successfully been patched") + } +} + +ext.patchArtifactMetadataFile = { + def taskConfig = project.gradle.startParameter.taskNames + .any { it.contains("Debug") } ? "debug" : "release" + + def moduleFile = layout.buildDirectory.file("publications/$taskConfig/module.json").get().asFile + if (!moduleFile.exists()) { + println("$moduleFile file doesn't exist") + return + } + + def jsonSlurper = new JsonSlurper() + def moduleData = jsonSlurper.parse(moduleFile) + + def patched = false + if (moduleData.variants) { + moduleData.variants.each { variant -> + variant.dependencies.each { dependency -> + if (project.ext.navSdkArtifactSettings.containsKey(dependency.module)) { + patched = true + dependency.module = project.ext.navSdkArtifactSettings[dependency.module].getV1() + dependency.version = [requires: project.ext.versionName] + } + } + } + } + + if (patched) { + moduleFile.text = JsonOutput.prettyPrint(JsonOutput.toJson(moduleData)) + println("module.json file has successfully been patched") + } +} \ No newline at end of file diff --git a/gradle/artifact-settings.gradle b/gradle/artifact-settings.gradle index 652ab2445ab..5d54005fe1e 100644 --- a/gradle/artifact-settings.gradle +++ b/gradle/artifact-settings.gradle @@ -1,6 +1,5 @@ ext { mapboxArtifactGroupId = 'com.mapbox.navigationcore' - mapboxArtifactId = project.property('POM_ARTIFACT_ID') mapboxArtifactTitle = project.property('POM_ARTIFACT_TITLE') mapboxArtifactDescription = project.property('POM_DESCRIPTION') mapboxDeveloperName = 'Mapbox' @@ -13,6 +12,36 @@ ext { snapshot = project.hasProperty("snapshot") ? project.property("snapshot").toBoolean() : false releaseTagPrefix = project.hasProperty('RELEASE_TAG_PREFIX') ? project.property('RELEASE_TAG_PREFIX') : 'mapbox-navigation-android_dash-core_' versionName = getVersionName() + + /** + * Properties used for artifact publishing. + * + * The map consists of: + * - Key: The name of the Gradle module from which the artifact is built. + * - Value: A tuple with two elements: + * 1. The artifact ID. + * 2. The SDK name used in the SDK registry. + */ + navSdkArtifactSettings = [ + 'libnavigation-android' : new Tuple2('android', 'navigation-core-android'), + 'libnavigation-base' : new Tuple2('base', 'navigation-core-base'), + 'libnavigation-core' : new Tuple2('navigation', 'navigation-core-navigation'), + 'libnavigation-copilot' : new Tuple2('copilot', 'navigation-core-copilot'), + 'libnavigation-metrics' : new Tuple2('metrics', 'navigation-core-metrics'), + 'libnavigation-tripdata' : new Tuple2('tripdata', 'navigation-core-tripdata'), + 'libnavigation-util' : new Tuple2('utils', 'navigation-core-utils'), + 'libnavigation-voice' : new Tuple2('voice', 'navigation-core-voice'), + 'libnavigator' : new Tuple2('navigator', 'navigation-core-navigator'), + 'libnavui-base' : new Tuple2('ui-base', 'navigation-core-ui-base'), + 'libnavui-maps' : new Tuple2('ui-maps', 'navigation-core-ui-maps'), + 'libnavui-util' : new Tuple2('ui-utils', 'navigation-core-ui-utils'), + 'libtrip-notification' : new Tuple2('notification', 'navigation-core-notification'), + 'ui-components' : new Tuple2('ui-components', 'navigation-core-ui-components'), + 'androidauto' : new Tuple2('android-auto-components', 'navigation-core-androidauto'), + 'libtesting-router' : new Tuple2('test-router', 'navigation-core-testing-router'), + 'libnavigation-custom-route' : new Tuple2('customroute', 'navigation-core-custom-route'), + 'libnavigation-datainputs' : new Tuple2('datainputs', 'navigation-core-datainputs') + ] } def getVersionName() { diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 2bca779776c..120c00461b0 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -13,16 +13,16 @@ ext { // version which we should use in this build def mapboxNavigatorVersion = System.getenv("FORCE_MAPBOX_NAVIGATION_NATIVE_VERSION") if (mapboxNavigatorVersion == null || mapboxNavigatorVersion == '') { - mapboxNavigatorVersion = '319.0.0' + mapboxNavigatorVersion = '320.2.0' } println("Navigation Native version: " + mapboxNavigatorVersion) version = [ - mapboxMapSdk : '11.7.0-rc.1', + mapboxMapSdk : '11.7.1', mapboxSdkServices : '7.2.0', mapboxNavigator : "${mapboxNavigatorVersion}", - mapboxCommonNative : '24.7.0-rc.2', - mapboxSearch : '2.5.0-rc.2', + mapboxCommonNative : '24.7.1', + mapboxSearch : '2.5.1', mapboxBaseAndroid : '0.11.0', androidXLifecycle : '2.4.0', androidXCoreVersion : '1.6.0', @@ -201,7 +201,7 @@ ext { jacoco : '0.8.12', googleServices : '4.3.3', mapboxSdkVersions : '1.1.3', - dokka : '1.6.21', + dokka : '1.9.20', mapboxSdkRegistry : '0.7.0', mapboxAccessToken : '0.2.1', mapboxNativeDownload : '0.2.2', diff --git a/gradle/publish.gradle b/gradle/publish.gradle index 943722b35c9..c56697e15a9 100644 --- a/gradle/publish.gradle +++ b/gradle/publish.gradle @@ -1,6 +1,7 @@ apply plugin: 'maven-publish' apply plugin: 'com.mapbox.sdkRegistry' apply from: file("$rootDir/gradle/artifact-settings.gradle") +apply from: file("$rootDir/gradle/artifact-properties-patcher.gradle") apply from: "$rootDir/gradle/kdoc-settings.gradle" afterEvaluate { @@ -9,7 +10,7 @@ afterEvaluate { release(MavenPublication) { from components.findByName('release') groupId project.ext.mapboxArtifactGroupId - artifactId project.ext.mapboxArtifactId + artifactId project.ext.navSdkArtifactSettings[project.name].getV1() version project.ext.versionName artifact(androidSourcesJar) @@ -34,12 +35,14 @@ afterEvaluate { scmNode.appendNode("connection", project.ext.mapboxArtifactScmUrl) scmNode.appendNode("developerConnection", project.ext.mapboxArtifactScmUrl) scmNode.appendNode("url", project.ext.mapboxArtifactUrl) + + project.ext.patchPomDependencies(mainNode) } } debug(MavenPublication) { from components.findByName('debug') groupId project.ext.mapboxArtifactGroupId - artifactId project.ext.mapboxArtifactId + artifactId project.ext.navSdkArtifactSettings[project.name].getV1() version project.ext.versionName artifact(androidSourcesJar) @@ -49,29 +52,8 @@ afterEvaluate { } } -def sdkNameMap = [:] -sdkNameMap["libnavigation-android"] = "navigation-core-android" -sdkNameMap["libnavigation-base"] = "navigation-core-base" -sdkNameMap["libnavigation-core"] = "navigation-core-navigation" -sdkNameMap["libnavigation-copilot"] = "navigation-core-copilot" -sdkNameMap["libnavigation-metrics"] = "navigation-core-metrics" -sdkNameMap["libnavigation-tripdata"] = "navigation-core-tripdata" -sdkNameMap["libnavigation-voice"] = "navigation-core-voice" -sdkNameMap["libnavigator"] = "navigation-core-navigator" -sdkNameMap["libtrip-notification"] = "navigation-core-notification" -sdkNameMap["libnavigation-util"] = "navigation-core-utils" -sdkNameMap["libnavui-base"] = "navigation-core-ui-base" -sdkNameMap["libnavui-maps"] = "navigation-core-ui-maps" -sdkNameMap["ui-components"] = "navigation-core-ui-components" -sdkNameMap["androidauto"] = "navigation-core-androidauto" -sdkNameMap["libnavui-util"] = "navigation-core-ui-utils" -sdkNameMap["libnavui-androidauto"] = "navigation-core-ui-androidauto" -sdkNameMap["libtesting-router"] = "navigation-core-testing-router" -sdkNameMap["libnavigation-custom-route"] = "navigation-core-custom-route" -sdkNameMap["libnavigation-datainputs"] = "navigation-core-datainputs" - registry { - sdkName = sdkNameMap[project.name] + sdkName = project.ext.navSdkArtifactSettings[project.name].getV2() production = true snapshot = project.ext.snapshot override = snapshot @@ -79,7 +61,6 @@ registry { publish = true publishMessage = "cc @mapbox/navigation-android" publications = ["release"] - featureFlag = project.hasProperty('ARTIFACT_FEATURE_FLAG') ? project.property('ARTIFACT_FEATURE_FLAG') : '' } task androidSourcesJar(type: Jar) { @@ -91,3 +72,19 @@ task androidKdocJar(type: Jar) { archiveClassifier.set('javadoc') from kdocPath } + +tasks.register("patchArtifactMetadataFileTask") { + doLast { + project.ext.patchArtifactMetadataFile() + } +} + +afterEvaluate { + tasks.matching { it.name.startsWith('generateMetadataFileFor') }.configureEach { task -> + task.finalizedBy 'patchArtifactMetadataFileTask' + } + + tasks.matching { it.name ==~ /publish\w+PublicationToMavenLocal/ }.configureEach { task -> + task.dependsOn 'patchArtifactMetadataFileTask' + } +} diff --git a/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/core/CoreRerouteTest.kt b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/core/CoreRerouteTest.kt index c92b874fc06..bc3761df3af 100644 --- a/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/core/CoreRerouteTest.kt +++ b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/core/CoreRerouteTest.kt @@ -22,6 +22,8 @@ import com.mapbox.navigation.base.trip.model.RouteProgressState import com.mapbox.navigation.core.MapboxNavigation import com.mapbox.navigation.core.MapboxNavigationProvider import com.mapbox.navigation.core.directions.session.RoutesExtra +import com.mapbox.navigation.core.directions.session.RoutesExtra.ROUTES_UPDATE_REASON_ALTERNATIVE +import com.mapbox.navigation.core.directions.session.RoutesExtra.ROUTES_UPDATE_REASON_REROUTE import com.mapbox.navigation.core.internal.extensions.flowLocationMatcherResult import com.mapbox.navigation.core.reroute.RerouteOptionsAdapter import com.mapbox.navigation.core.reroute.RerouteState @@ -926,6 +928,58 @@ class CoreRerouteTest : BaseCoreNoCleanUpTest() { } } + @Test + fun reroute_from_primary_route_to_ignored_alternative() = sdkTest { + withMapboxNavigation( + historyRecorderRule = mapboxHistoryTestRule, + ) { mapboxNavigation -> + val mockRoute = RoutesProvider.dc_short_alternative_with_fork_point(context) + mockWebServerRule.requestHandlers.addAll(mockRoute.mockRequestHandlers) + + val routes = mapboxNavigation.requestRoutes( + RouteOptions.builder() + .applyDefaultNavigationOptions() + .applyLanguageAndVoiceUnitOptions(context) + .baseUrl(mockWebServerRule.baseUrl) + .waypointsPerRoute(true) + .coordinatesList(mockRoute.routeWaypoints).build(), + ).getSuccessfulResultOrThrowException().routes + + val mainRoute = routes[0] + val alternativeRoute = routes[1] + + mockLocationReplayerRule.playRoute( + directionsRoute = mainRoute.directionsRoute, + ) + + mapboxNavigation.startTripSession() + mapboxNavigation.setNavigationRoutes(routes) + + // alternative fork point passed reason should occur on this route + mapboxNavigation.routesUpdates().filter { + it.reason == ROUTES_UPDATE_REASON_ALTERNATIVE && it.ignoredRoutes.any { + it.navigationRoute == alternativeRoute && + it.reason == "Alternative fork point passed" + } + }.first() + + mockLocationReplayerRule.playRoute( + directionsRoute = alternativeRoute.directionsRoute, + eventsToDrop = 30, // approx value to start alternative rotue after "fork point" + ) + + // reroute to hidden alternative should occur + val rerouteToHiddenAlternativeUpdate = mapboxNavigation.routesUpdates().filter { + it.reason == ROUTES_UPDATE_REASON_REROUTE + }.first() + + assertEquals( + alternativeRoute, + rerouteToHiddenAlternativeUpdate.navigationRoutes[0], + ) + } + } + private fun createMapboxNavigation(customRefreshInterval: Long? = null): MapboxNavigation { var mapboxNavigation: MapboxNavigation? = null diff --git a/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/core/WaypointsTest.kt b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/core/WaypointsTest.kt index 599f8e98fb5..699d3c73f70 100644 --- a/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/core/WaypointsTest.kt +++ b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/core/WaypointsTest.kt @@ -22,7 +22,6 @@ import com.mapbox.navigation.instrumentation_tests.activity.EmptyTestActivity import com.mapbox.navigation.testing.ui.BaseTest import com.mapbox.navigation.testing.ui.utils.MapboxNavigationRule import com.mapbox.navigation.testing.ui.utils.coroutines.getSuccessfulResultOrThrowException -import com.mapbox.navigation.testing.ui.utils.coroutines.navigateNextRouteLeg import com.mapbox.navigation.testing.ui.utils.coroutines.requestRoutes import com.mapbox.navigation.testing.ui.utils.coroutines.sdkTest import com.mapbox.navigation.testing.ui.utils.coroutines.setNavigationRoutesAndWaitForUpdate @@ -162,7 +161,7 @@ class WaypointsTest : BaseTest(EmptyTestActivity::class.java) Point.fromLngLat(140.025878, 35.660315), Point.fromLngLat(140.02985194436837, 35.6621859075361), Point.fromLngLat(140.0277017481984, 35.65792632910045), - Point.fromLngLat(140.03887765416835, 35.66023142441715), + Point.fromLngLat(140.038772, 35.660329), Point.fromLngLat(140.0231453915486, 35.667495318461164), Point.fromLngLat(140.03969561587877, 35.67009382118668), ) @@ -205,7 +204,6 @@ class WaypointsTest : BaseTest(EmptyTestActivity::class.java) checkLocation(coordinates[1], legWaypoint.location) assertEquals(LegWaypoint.REGULAR, legWaypoint.type) - mapboxNavigation.navigateNextRouteLeg() stayOnPosition(coordinates[2], 270f) delay(1000) assertEquals(1, nextWaypoints.size) @@ -216,7 +214,6 @@ class WaypointsTest : BaseTest(EmptyTestActivity::class.java) checkLocation(coordinates[3], legWaypoint.location) assertEquals(LegWaypoint.REGULAR, legWaypoint.type) - mapboxNavigation.navigateNextRouteLeg() stayOnPosition(coordinates[4], 45f) delay(1000) assertEquals(2, nextWaypoints.size) @@ -263,7 +260,6 @@ class WaypointsTest : BaseTest(EmptyTestActivity::class.java) ) assertEquals(LegWaypoint.EV_CHARGING_ADDED, legWaypoint.type) - mapboxNavigation.navigateNextRouteLeg() stayOnPosition(routes[0].waypoints!![2].location(), 0f) nextWaypoints.waitUntilHasSize(2) legWaypoint = nextWaypoints[1]!! diff --git a/libnavigation-android/build.gradle b/libnavigation-android/build.gradle index 9a18fc8d51e..9598a05aad3 100644 --- a/libnavigation-android/build.gradle +++ b/libnavigation-android/build.gradle @@ -36,3 +36,4 @@ dependencies { apply from: "../gradle/track-public-apis.gradle" apply from: "../gradle/jacoco.gradle" apply from: "../gradle/publish.gradle" +apply from: "../../../../scripts_shared/find-all-common-sdk-versions.gradle" diff --git a/libnavigation-android/gradle.properties b/libnavigation-android/gradle.properties index 07d03eff677..1afced59197 100644 --- a/libnavigation-android/gradle.properties +++ b/libnavigation-android/gradle.properties @@ -1,3 +1,2 @@ -POM_ARTIFACT_ID=android POM_ARTIFACT_TITLE=Mapbox Navigation SDK POM_DESCRIPTION=Artifact that groups all of the Navigation SDK features \ No newline at end of file diff --git a/libnavigation-base/gradle.properties b/libnavigation-base/gradle.properties index 59954d56298..f991c4404b6 100644 --- a/libnavigation-base/gradle.properties +++ b/libnavigation-base/gradle.properties @@ -1,3 +1,2 @@ -POM_ARTIFACT_ID=base POM_ARTIFACT_TITLE=Mapbox Navigation Base POM_DESCRIPTION=Artifact that provides the set of interface definitions and DTOs used in the modular Navigation SDK \ No newline at end of file diff --git a/libnavigation-base/src/main/java/com/mapbox/navigation/base/internal/factory/RouteIndicesFactory.kt b/libnavigation-base/src/main/java/com/mapbox/navigation/base/internal/factory/RouteIndicesFactory.kt index df52dfcfa5e..2c92bc58665 100644 --- a/libnavigation-base/src/main/java/com/mapbox/navigation/base/internal/factory/RouteIndicesFactory.kt +++ b/libnavigation-base/src/main/java/com/mapbox/navigation/base/internal/factory/RouteIndicesFactory.kt @@ -12,11 +12,13 @@ object RouteIndicesFactory { routeGeometryIndex: Int, legGeometryIndex: Int, intersectionIndex: Int, + isForkPointPassed: Boolean, ): RouteIndices = RouteIndices( legIndex, stepIndex, routeGeometryIndex, legGeometryIndex, intersectionIndex, + isForkPointPassed, ) } diff --git a/libnavigation-base/src/main/java/com/mapbox/navigation/base/internal/trip/model/RouteIndices.kt b/libnavigation-base/src/main/java/com/mapbox/navigation/base/internal/trip/model/RouteIndices.kt index 1a26018b22e..211a81f13d5 100644 --- a/libnavigation-base/src/main/java/com/mapbox/navigation/base/internal/trip/model/RouteIndices.kt +++ b/libnavigation-base/src/main/java/com/mapbox/navigation/base/internal/trip/model/RouteIndices.kt @@ -8,6 +8,7 @@ package com.mapbox.navigation.base.internal.trip.model * @param routeGeometryIndex current index in the route geometry. Analogous to [RouteProgress.currentRouteGeometryIndex] for primary route. * @param legGeometryIndex current index in the leg geometry. Analogous to [RouteLegProgress.geometryIndex] for primary route. * @param intersectionIndex current step-wise intersection index. Analogous to [RouteStepProgress.intersectionIndex] for primary route. + * @param isForkPointPassed indicates whether the fork point was passed. */ class RouteIndices internal constructor( val legIndex: Int, @@ -15,6 +16,7 @@ class RouteIndices internal constructor( val routeGeometryIndex: Int, val legGeometryIndex: Int, val intersectionIndex: Int, + val isForkPointPassed: Boolean, ) { /** @@ -31,6 +33,7 @@ class RouteIndices internal constructor( if (routeGeometryIndex != other.routeGeometryIndex) return false if (legGeometryIndex != other.legGeometryIndex) return false if (intersectionIndex != other.intersectionIndex) return false + if (isForkPointPassed != other.isForkPointPassed) return false return true } @@ -44,6 +47,7 @@ class RouteIndices internal constructor( result = 31 * result + routeGeometryIndex result = 31 * result + legGeometryIndex result = 31 * result + intersectionIndex + result = 31 * result + isForkPointPassed.hashCode() return result } @@ -56,7 +60,8 @@ class RouteIndices internal constructor( "stepIndex=$stepIndex, " + "routeGeometryIndex=$routeGeometryIndex, " + "legGeometryIndex=$legGeometryIndex, " + - "intersectionIndex=$intersectionIndex" + + "intersectionIndex=$intersectionIndex, " + + "isForkPointPassed=$isForkPointPassed" + ")" } } diff --git a/libnavigation-base/src/main/java/com/mapbox/navigation/base/internal/utils/DirectionsResponseUtils.kt b/libnavigation-base/src/main/java/com/mapbox/navigation/base/internal/utils/DirectionsResponseUtils.kt index 53a8e9b4c9e..dc0533e7d9a 100644 --- a/libnavigation-base/src/main/java/com/mapbox/navigation/base/internal/utils/DirectionsResponseUtils.kt +++ b/libnavigation-base/src/main/java/com/mapbox/navigation/base/internal/utils/DirectionsResponseUtils.kt @@ -1,5 +1,6 @@ package com.mapbox.navigation.base.internal.utils +import com.mapbox.api.directions.v5.models.DirectionsResponse import com.mapbox.bindgen.DataRef import com.mapbox.bindgen.Expected import com.mapbox.bindgen.ExpectedFactory @@ -7,7 +8,6 @@ import com.mapbox.navigation.base.internal.route.RoutesResponse import com.mapbox.navigation.base.internal.route.toNavigationRoute import com.mapbox.navigation.base.route.NavigationRoute import com.mapbox.navigation.base.route.RouterOrigin -import com.mapbox.navigation.base.route.toDirectionsResponse import com.mapbox.navigation.utils.internal.logE import com.mapbox.navigator.RouteInterface import kotlinx.coroutines.CoroutineDispatcher @@ -44,13 +44,18 @@ suspend fun parseDirectionsResponse( fun parseRouteInterfaces( routes: List, responseTimeElapsedSeconds: Long, + routeLookup: (routeId: String) -> NavigationRoute?, + routeToDirections: (route: RouteInterface) -> DirectionsResponse, ): Expected> { return try { routes.groupBy { it.responseUuid } .map { (_, routes) -> - val directionsResponse = routes.first().responseJsonRef.toDirectionsResponse() + val directionsResponse by lazy { + routeToDirections(routes.first()) + } + routes.map { - it.toNavigationRoute( + routeLookup(it.routeId) ?: it.toNavigationRoute( responseTimeElapsedSeconds, directionsResponse, ) diff --git a/libnavigation-base/src/main/java/com/mapbox/navigation/base/internal/utils/RoutesParsingManager.kt b/libnavigation-base/src/main/java/com/mapbox/navigation/base/internal/utils/RoutesParsingManager.kt index 3c01f64d9ff..6487dd1fdf4 100644 --- a/libnavigation-base/src/main/java/com/mapbox/navigation/base/internal/utils/RoutesParsingManager.kt +++ b/libnavigation-base/src/main/java/com/mapbox/navigation/base/internal/utils/RoutesParsingManager.kt @@ -2,8 +2,11 @@ package com.mapbox.navigation.base.internal.utils +import com.mapbox.api.directions.v5.models.DirectionsResponse import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI +import com.mapbox.navigation.base.route.toDirectionsResponse import com.mapbox.navigation.utils.internal.logD +import com.mapbox.navigator.RouteInterface import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import java.nio.ByteBuffer @@ -30,6 +33,11 @@ interface RouteParsingManager { arguments: AlternativesInfo, parsing: suspend () -> T, ): AlternativesParsingResult + + fun parseRouteToDirections(route: RouteInterface): DirectionsResponse { + logD(LOG_TAG) { "Parsing directions response for routeId = ${route.routeId}" } + return route.responseJsonRef.toDirectionsResponse() + } } fun createRouteParsingManager(): RouteParsingManager { diff --git a/libnavigation-copilot/gradle.properties b/libnavigation-copilot/gradle.properties index 95516cddbdf..c934ca80f7f 100644 --- a/libnavigation-copilot/gradle.properties +++ b/libnavigation-copilot/gradle.properties @@ -1,3 +1,2 @@ -POM_ARTIFACT_ID=copilot POM_ARTIFACT_TITLE=Mapbox Navigation Copilot POM_DESCRIPTION=Artifact that provides Copilot capabilities \ No newline at end of file diff --git a/libnavigation-core/gradle.properties b/libnavigation-core/gradle.properties index c4f69febbdb..7421add2c6d 100644 --- a/libnavigation-core/gradle.properties +++ b/libnavigation-core/gradle.properties @@ -1,3 +1,2 @@ -POM_ARTIFACT_ID=navigation POM_ARTIFACT_TITLE=Mapbox Navigation SDK POM_DESCRIPTION=Main artifact that provides all default modules and entry points of the Mapbox Navigation SDK \ No newline at end of file diff --git a/libnavigation-core/src/main/java/com/mapbox/navigation/core/MapboxNavigation.kt b/libnavigation-core/src/main/java/com/mapbox/navigation/core/MapboxNavigation.kt index cc0d8df6fe7..fc6ddafcc71 100644 --- a/libnavigation-core/src/main/java/com/mapbox/navigation/core/MapboxNavigation.kt +++ b/libnavigation-core/src/main/java/com/mapbox/navigation/core/MapboxNavigation.kt @@ -49,6 +49,7 @@ import com.mapbox.navigation.core.directions.session.RoutesSetStartedParams import com.mapbox.navigation.core.directions.session.RoutesUpdatedResult import com.mapbox.navigation.core.directions.session.SetNavigationRoutesStartedObserver import com.mapbox.navigation.core.directions.session.Utils +import com.mapbox.navigation.core.directions.session.routesPlusIgnored import com.mapbox.navigation.core.history.MapboxHistoryReader import com.mapbox.navigation.core.history.MapboxHistoryRecorder import com.mapbox.navigation.core.internal.MapboxNavigationSDKInitializerImpl @@ -589,6 +590,7 @@ class MapboxNavigation @VisibleForTesting internal constructor( tripSession, threadController, routeParsingManager, + directionsSession, ) routeAlternativesController.setRouteUpdateSuggestionListener(::updateRoutes) @OptIn(ExperimentalPreviewMapboxNavigationAPI::class) @@ -641,6 +643,13 @@ class MapboxNavigation @VisibleForTesting internal constructor( navigationOptions.applicationContext, navigator, ) + + registerRouteProgressObserver( + NavigationComponentProvider.createForkPointPassedObserver( + directionsSession, + ::currentLegIndex, + ), + ) } @OptIn(ExperimentalMapboxNavigationAPI::class) @@ -2117,7 +2126,7 @@ class MapboxNavigation @VisibleForTesting internal constructor( private suspend fun prepareNavigationForRoutesParsing() { withContext(Dispatchers.Main.immediate) { - if (directionsSession.routes.size > 1) { + if (directionsSession.routesPlusIgnored.size > 1) { suspendCoroutine { continuation -> setNavigationRoutes(directionsSession.routes.take(1), currentLegIndex()) { continuation.resume(Unit) diff --git a/libnavigation-core/src/main/java/com/mapbox/navigation/core/NavigationComponentProvider.kt b/libnavigation-core/src/main/java/com/mapbox/navigation/core/NavigationComponentProvider.kt index 89eaf41af52..e2c5b7c0113 100644 --- a/libnavigation-core/src/main/java/com/mapbox/navigation/core/NavigationComponentProvider.kt +++ b/libnavigation-core/src/main/java/com/mapbox/navigation/core/NavigationComponentProvider.kt @@ -9,6 +9,7 @@ import com.mapbox.navigation.base.options.RerouteOptions import com.mapbox.navigation.base.trip.notification.TripNotification import com.mapbox.navigation.core.accounts.BillingController import com.mapbox.navigation.core.arrival.ArrivalProgressObserver +import com.mapbox.navigation.core.directions.ForkPointPassedObserver import com.mapbox.navigation.core.directions.session.DirectionsSession import com.mapbox.navigation.core.directions.session.MapboxDirectionsSession import com.mapbox.navigation.core.ev.EVDynamicDataHolder @@ -24,6 +25,7 @@ import com.mapbox.navigation.core.trip.service.MapboxTripService import com.mapbox.navigation.core.trip.service.TripService import com.mapbox.navigation.core.trip.session.MapboxTripSession import com.mapbox.navigation.core.trip.session.NavigationSession +import com.mapbox.navigation.core.trip.session.RouteProgressObserver import com.mapbox.navigation.core.trip.session.TripSession import com.mapbox.navigation.core.trip.session.TripSessionLocationEngine import com.mapbox.navigation.core.trip.session.eh.EHorizonSubscriptionManagerImpl @@ -153,4 +155,12 @@ internal object NavigationComponentProvider { threadController, evDynamicDataHolder, ) + + fun createForkPointPassedObserver( + directionsSession: DirectionsSession, + currentLegIndex: () -> Int, + ): RouteProgressObserver = ForkPointPassedObserver( + directionsSession, + currentLegIndex, + ) } diff --git a/libnavigation-core/src/main/java/com/mapbox/navigation/core/arrival/AutoArrivalController.kt b/libnavigation-core/src/main/java/com/mapbox/navigation/core/arrival/AutoArrivalController.kt index 6acb092c05f..4b45623ff5f 100644 --- a/libnavigation-core/src/main/java/com/mapbox/navigation/core/arrival/AutoArrivalController.kt +++ b/libnavigation-core/src/main/java/com/mapbox/navigation/core/arrival/AutoArrivalController.kt @@ -1,9 +1,6 @@ package com.mapbox.navigation.core.arrival -import android.os.SystemClock -import com.mapbox.api.directions.v5.models.RouteLeg import com.mapbox.navigation.base.trip.model.RouteLegProgress -import java.util.concurrent.TimeUnit /** * The default controller for arrival. This will move onto the next leg automatically @@ -11,28 +8,10 @@ import java.util.concurrent.TimeUnit */ open class AutoArrivalController : ArrivalController { - private var routeLegCompletedTime: Long? = null - private var currentRouteLeg: RouteLeg? = null - /** - * Moves onto the next leg after 5 seconds have passed. + * Moves onto the next immediately. */ override fun navigateNextRouteLeg(routeLegProgress: RouteLegProgress): Boolean { - if (currentRouteLeg != routeLegProgress.routeLeg) { - currentRouteLeg = routeLegProgress.routeLeg - routeLegCompletedTime = SystemClock.elapsedRealtimeNanos() - } - - val elapsedTimeNanos = SystemClock.elapsedRealtimeNanos() - (routeLegCompletedTime ?: 0L) - val shouldNavigateNextRouteLeg = elapsedTimeNanos >= AUTO_ARRIVAL_NANOS - if (shouldNavigateNextRouteLeg) { - currentRouteLeg = null - routeLegCompletedTime = null - } - return shouldNavigateNextRouteLeg - } - - internal companion object { - val AUTO_ARRIVAL_NANOS = TimeUnit.SECONDS.toNanos(5L) + return true } } diff --git a/libnavigation-core/src/main/java/com/mapbox/navigation/core/directions/ForkPointPassedObserver.kt b/libnavigation-core/src/main/java/com/mapbox/navigation/core/directions/ForkPointPassedObserver.kt new file mode 100644 index 00000000000..8df21d2ccf6 --- /dev/null +++ b/libnavigation-core/src/main/java/com/mapbox/navigation/core/directions/ForkPointPassedObserver.kt @@ -0,0 +1,62 @@ +package com.mapbox.navigation.core.directions + +import com.mapbox.navigation.base.internal.extensions.internalAlternativeRouteIndices +import com.mapbox.navigation.base.trip.model.RouteProgress +import com.mapbox.navigation.core.SetRoutes +import com.mapbox.navigation.core.directions.session.DirectionsSession +import com.mapbox.navigation.core.directions.session.DirectionsSessionRoutes +import com.mapbox.navigation.core.directions.session.IgnoredRoute +import com.mapbox.navigation.core.directions.session.routesPlusIgnored +import com.mapbox.navigation.core.trip.session.RouteProgressObserver +import com.mapbox.navigation.utils.internal.logD + +internal class ForkPointPassedObserver( + private val directionsSession: DirectionsSession, + private val currentLegIndex: () -> Int, +) : RouteProgressObserver { + override fun onRouteProgressChanged(routeProgress: RouteProgress) { + val allCurrentRoutes = directionsSession.routesPlusIgnored + + if (allCurrentRoutes.isEmpty()) return + + val needToHideAlternatives = routeProgress + .internalAlternativeRouteIndices() + .filter { it.value.isForkPointPassed } + + val newRoutes = DirectionsSessionRoutes( + acceptedRoutes = allCurrentRoutes.filter { it.id !in needToHideAlternatives }, + ignoredRoutes = allCurrentRoutes.filter { it.id in needToHideAlternatives } + .map { IgnoredRoute(it, REASON_ALTERNATIVE_FORK_POINT_PASSED) }, + setRoutesInfo = SetRoutes.Alternatives(currentLegIndex()), + ) + + when { + newRoutes.ignoredRoutes == directionsSession.ignoredRoutes && + newRoutes.acceptedRoutes == directionsSession.routes -> return + else -> { + if (newRoutes.ignoredRoutes.isNotEmpty()) { + logD( + "Hiding alternatives due to fork point has passed: " + + "${newRoutes.ignoredRoutes}", + TAG, + ) + } + + if (newRoutes.acceptedRoutes != directionsSession.routes) { + logD( + "Settigns new routes due to fork point changes: " + + "${newRoutes.acceptedRoutes}", + TAG, + ) + } + + directionsSession.setNavigationRoutesFinished(newRoutes) + } + } + } + + companion object { + private const val TAG = "ForkPointPassedObserver" + internal const val REASON_ALTERNATIVE_FORK_POINT_PASSED = "Alternative fork point passed" + } +} diff --git a/libnavigation-core/src/main/java/com/mapbox/navigation/core/directions/session/DirectionsSession.kt b/libnavigation-core/src/main/java/com/mapbox/navigation/core/directions/session/DirectionsSession.kt index bdbd91b451c..e5e0e393353 100644 --- a/libnavigation-core/src/main/java/com/mapbox/navigation/core/directions/session/DirectionsSession.kt +++ b/libnavigation-core/src/main/java/com/mapbox/navigation/core/directions/session/DirectionsSession.kt @@ -1,5 +1,6 @@ package com.mapbox.navigation.core.directions.session +import androidx.annotation.VisibleForTesting import com.mapbox.api.directions.v5.models.RouteOptions import com.mapbox.navigation.base.route.NavigationRoute import com.mapbox.navigation.base.route.NavigationRouterCallback @@ -9,9 +10,19 @@ import com.mapbox.navigation.core.internal.utils.mapToReason internal interface DirectionsSession : RouteRefresh { + @VisibleForTesting val routesUpdatedResult: RoutesUpdatedResult? + val routes: List + /** + * A list of routes that have been ignored. + * + * Ignored routes are those that are not currently being used for navigation, + * but are still kept in memory for potential future use. + */ + val ignoredRoutes: List + val initialLegIndex: Int fun setNavigationRoutesStarted(params: RoutesSetStartedParams) @@ -72,6 +83,12 @@ internal interface DirectionsSession : RouteRefresh { fun shutdown() } +internal val DirectionsSession.routesPlusIgnored: List + get() = routes + ignoredRoutes.map { it.navigationRoute } + +internal fun DirectionsSession.findRoute(routeId: String): NavigationRoute? = + routesPlusIgnored.find { it.id == routeId } + internal data class DirectionsSessionRoutes( val acceptedRoutes: List, val ignoredRoutes: List, diff --git a/libnavigation-core/src/main/java/com/mapbox/navigation/core/directions/session/MapboxDirectionsSession.kt b/libnavigation-core/src/main/java/com/mapbox/navigation/core/directions/session/MapboxDirectionsSession.kt index bcd1801b0ac..ee74584d9e6 100644 --- a/libnavigation-core/src/main/java/com/mapbox/navigation/core/directions/session/MapboxDirectionsSession.kt +++ b/libnavigation-core/src/main/java/com/mapbox/navigation/core/directions/session/MapboxDirectionsSession.kt @@ -1,5 +1,6 @@ package com.mapbox.navigation.core.directions.session +import androidx.annotation.VisibleForTesting import com.mapbox.api.directions.v5.models.DirectionsRoute import com.mapbox.api.directions.v5.models.RouteOptions import com.mapbox.navigation.base.internal.RouteRefreshRequestData @@ -26,10 +27,15 @@ internal class MapboxDirectionsSession( private val onSetNavigationRoutesStartedObservers = CopyOnWriteArraySet() + @VisibleForTesting override var routesUpdatedResult: RoutesUpdatedResult? = null + override val routes: List get() = routesUpdatedResult?.navigationRoutes ?: emptyList() + override val ignoredRoutes: List + get() = routesUpdatedResult?.ignoredRoutes ?: emptyList() + override var initialLegIndex = DEFAULT_INITIAL_LEG_INDEX private set @@ -39,6 +45,7 @@ internal class MapboxDirectionsSession( override fun setNavigationRoutesFinished(routes: DirectionsSessionRoutes) { this.initialLegIndex = routes.setRoutesInfo.initialLegIndex() + if ( routesUpdatedResult?.navigationRoutes?.isEmpty() == true && routes.acceptedRoutes.isEmpty() @@ -46,7 +53,10 @@ internal class MapboxDirectionsSession( return } - val result = routes.toRoutesUpdatedResult().also { routesUpdatedResult = it } + val result = routes.toRoutesUpdatedResult().also { + routesUpdatedResult = it + } + onSetNavigationRoutesFinishedObservers.forEach { it.onRoutesChanged(result) } diff --git a/libnavigation-core/src/main/java/com/mapbox/navigation/core/internal/utils/Bitmap.kt b/libnavigation-core/src/main/java/com/mapbox/navigation/core/internal/utils/Bitmap.kt index 46cc9dce70c..96dba812881 100644 --- a/libnavigation-core/src/main/java/com/mapbox/navigation/core/internal/utils/Bitmap.kt +++ b/libnavigation-core/src/main/java/com/mapbox/navigation/core/internal/utils/Bitmap.kt @@ -1,30 +1,11 @@ package com.mapbox.navigation.core.internal.utils import android.graphics.Bitmap -import android.util.Base64 -import com.mapbox.bindgen.DataRef import com.mapbox.navigation.core.telemetry.events.BitmapEncodeOptions -import com.mapbox.navigator.ScreenshotFormat import java.io.ByteArrayOutputStream -import java.nio.ByteBuffer -import java.nio.charset.Charset import kotlin.math.min import kotlin.math.roundToInt -@JvmSynthetic -internal fun String.encodedBitmapToNativeScreenshotFormat(): ScreenshotFormat { - val encoded = toByteArray(Charset.defaultCharset()) - - val byteBuffer = ByteBuffer.allocateDirect(encoded.size) - byteBuffer.put(encoded) - val dataRef = DataRef(byteBuffer) - - return ScreenshotFormat( - dataRef, - Base64.encodeToString(encoded, Base64.DEFAULT), - ) -} - @JvmSynthetic internal fun Bitmap.encode( options: BitmapEncodeOptions = BitmapEncodeOptions.Builder().build(), diff --git a/libnavigation-core/src/main/java/com/mapbox/navigation/core/navigator/NavigatorMapper.kt b/libnavigation-core/src/main/java/com/mapbox/navigation/core/navigator/NavigatorMapper.kt index 23f1564c910..cf541015c34 100644 --- a/libnavigation-core/src/main/java/com/mapbox/navigation/core/navigator/NavigatorMapper.kt +++ b/libnavigation-core/src/main/java/com/mapbox/navigation/core/navigator/NavigatorMapper.kt @@ -192,6 +192,7 @@ private fun NavigationStatus.getRouteProgress( it.geometryIndex, it.shapeIndex, it.intersectionIndex, + it.isForkPointPassed, ) } diff --git a/libnavigation-core/src/main/java/com/mapbox/navigation/core/reroute/MapboxRerouteController.kt b/libnavigation-core/src/main/java/com/mapbox/navigation/core/reroute/MapboxRerouteController.kt index 08e051d3330..85649274a64 100644 --- a/libnavigation-core/src/main/java/com/mapbox/navigation/core/reroute/MapboxRerouteController.kt +++ b/libnavigation-core/src/main/java/com/mapbox/navigation/core/reroute/MapboxRerouteController.kt @@ -19,6 +19,7 @@ import com.mapbox.navigation.base.route.ResponseOriginAPI import com.mapbox.navigation.base.route.RouterFailure import com.mapbox.navigation.base.route.RouterOrigin import com.mapbox.navigation.core.directions.session.DirectionsSession +import com.mapbox.navigation.core.directions.session.routesPlusIgnored import com.mapbox.navigation.core.ev.EVDynamicDataHolder import com.mapbox.navigation.core.internal.router.GetRouteSignature import com.mapbox.navigation.core.routeoptions.RouteOptionsUpdater @@ -142,7 +143,7 @@ internal class MapboxRerouteController @VisibleForTesting constructor( val routeProgress = tripSession.getRouteProgress() val routeAlternativeId = routeProgress?.routeAlternativeId - val routes = directionsSession.routes + val routes = directionsSession.routesPlusIgnored if (!ignoreDeviationToAlternatives && routeAlternativeId != null) { val relevantAlternative = routes.find { it.id == routeAlternativeId } if (relevantAlternative != null) { diff --git a/libnavigation-core/src/main/java/com/mapbox/navigation/core/routealternatives/RouteAlternativesController.kt b/libnavigation-core/src/main/java/com/mapbox/navigation/core/routealternatives/RouteAlternativesController.kt index b5db829155b..4887486e6f1 100644 --- a/libnavigation-core/src/main/java/com/mapbox/navigation/core/routealternatives/RouteAlternativesController.kt +++ b/libnavigation-core/src/main/java/com/mapbox/navigation/core/routealternatives/RouteAlternativesController.kt @@ -1,6 +1,5 @@ package com.mapbox.navigation.core.routealternatives -import com.mapbox.bindgen.Expected import com.mapbox.bindgen.ExpectedFactory import com.mapbox.navigation.base.internal.utils.AlternativesInfo import com.mapbox.navigation.base.internal.utils.AlternativesParsingResult @@ -12,6 +11,8 @@ import com.mapbox.navigation.base.route.NavigationRoute import com.mapbox.navigation.base.route.RouteAlternativesOptions import com.mapbox.navigation.base.route.RouterOrigin import com.mapbox.navigation.base.trip.model.RouteProgress +import com.mapbox.navigation.core.directions.session.DirectionsSession +import com.mapbox.navigation.core.directions.session.findRoute import com.mapbox.navigation.core.internal.routealternatives.NavigationRouteAlternativesObserver import com.mapbox.navigation.core.trip.session.TripSession import com.mapbox.navigation.navigator.internal.MapboxNativeNavigator @@ -37,6 +38,7 @@ internal class RouteAlternativesController( private val tripSession: TripSession, private val threadController: ThreadController, private val routeParsingManager: RouteParsingManager, + private val directionSession: DirectionsSession, ) : AlternativeMetadataProvider { @RouterOrigin @@ -63,13 +65,10 @@ internal class RouteAlternativesController( fun setRouteUpdateSuggestionListener(listener: UpdateRoutesSuggestionObserver?) { updateNativeObserver { - if (listener == null) { - defaultAlternativesHandler = null + defaultAlternativesHandler = if (listener == null) { + null } else { - defaultAlternativesHandler = - RouteAlternativesToRouteUpdateSuggestionsAdapter( - listener, - ) + RouteAlternativesToRouteUpdateSuggestionsAdapter(listener) } } } @@ -135,21 +134,19 @@ internal class RouteAlternativesController( } observerProcessingJob?.cancel() - observerProcessingJob = - processRouteAlternatives( - onlinePrimaryRoute, - routeAlternatives, - ) { alternatives, origin -> - logD("${alternatives.size} alternatives available", LOG_CATEGORY) - - val routeProgress = tripSession.getRouteProgress() - ?: run { - logD("skipping alternatives update - no progress", LOG_CATEGORY) - return@processRouteAlternatives - } - - observerToTrigger?.onRouteAlternatives(routeProgress, alternatives, origin) + observerProcessingJob = processRouteAlternatives( + onlinePrimaryRoute, + routeAlternatives, + ) { alternatives, origin -> + logD("${alternatives.size} alternatives available", LOG_CATEGORY) + + val routeProgress = tripSession.getRouteProgress() ?: run { + logD("skipping alternatives update - no progress", LOG_CATEGORY) + return@processRouteAlternatives } + + observerToTrigger?.onRouteAlternatives(routeProgress, alternatives, origin) + } } override fun onError(message: String) { @@ -175,20 +172,22 @@ internal class RouteAlternativesController( val primaryRoutes = onlinePrimaryRoute?.let { listOf(it) } ?: emptyList() val allAlternatives = primaryRoutes + nativeAlternatives.map { it.route } - val alternatives: List = if (allAlternatives.isNotEmpty()) { + val alternatives = if (allAlternatives.isNotEmpty()) { val args = AlternativesInfo( RouteResponseInfo.fromResponses(allAlternatives.map { it.responseJsonRef.buffer }), ) - val alternativesParsingResult: - AlternativesParsingResult>> = - routeParsingManager.parseAlternatives(args) { - withContext(ThreadController.DefaultDispatcher) { - parseRouteInterfaces( - allAlternatives, - responseTimeElapsedSeconds, - ) - } + + val alternativesParsingResult = routeParsingManager.parseAlternatives(args) { + withContext(ThreadController.DefaultDispatcher) { + parseRouteInterfaces( + routes = allAlternatives, + responseTimeElapsedSeconds = responseTimeElapsedSeconds, + routeLookup = directionSession::findRoute, + routeToDirections = routeParsingManager::parseRouteToDirections, + ) } + } + val expected = when (alternativesParsingResult) { AlternativesParsingResult.NotActual -> { ExpectedFactory.createError( @@ -248,13 +247,11 @@ internal class RouteAlternativesController( private class RouteAlternativesToRouteUpdateSuggestionsAdapter( private val suggestRouteUpdate: (UpdateRouteSuggestion) -> Unit, - ) : - NavigationRouteAlternativesObserver { + ) : NavigationRouteAlternativesObserver { override fun onRouteAlternatives( routeProgress: RouteProgress, alternatives: List, - @RouterOrigin - routerOrigin: String, + @RouterOrigin routerOrigin: String, ) { when (routeProgress.navigationRoute.origin) { RouterOrigin.ONLINE -> { diff --git a/libnavigation-core/src/main/java/com/mapbox/navigation/core/routealternatives/RouteAlternativesControllerProvider.kt b/libnavigation-core/src/main/java/com/mapbox/navigation/core/routealternatives/RouteAlternativesControllerProvider.kt index 3ca1047bc27..3436a12cb68 100644 --- a/libnavigation-core/src/main/java/com/mapbox/navigation/core/routealternatives/RouteAlternativesControllerProvider.kt +++ b/libnavigation-core/src/main/java/com/mapbox/navigation/core/routealternatives/RouteAlternativesControllerProvider.kt @@ -2,6 +2,7 @@ package com.mapbox.navigation.core.routealternatives import com.mapbox.navigation.base.internal.utils.RouteParsingManager import com.mapbox.navigation.base.route.RouteAlternativesOptions +import com.mapbox.navigation.core.directions.session.DirectionsSession import com.mapbox.navigation.core.trip.session.TripSession import com.mapbox.navigation.navigator.internal.MapboxNativeNavigator import com.mapbox.navigation.utils.internal.ThreadController @@ -14,11 +15,13 @@ internal object RouteAlternativesControllerProvider { tripSession: TripSession, threadController: ThreadController, routeParsingManager: RouteParsingManager, + directionsSession: DirectionsSession, ) = RouteAlternativesController( options, navigator, tripSession, threadController, routeParsingManager, + directionsSession, ) } diff --git a/libnavigation-core/src/main/java/com/mapbox/navigation/core/routerefresh/RouteRefreshController.kt b/libnavigation-core/src/main/java/com/mapbox/navigation/core/routerefresh/RouteRefreshController.kt index 265cf1e6cd7..acd583eea06 100644 --- a/libnavigation-core/src/main/java/com/mapbox/navigation/core/routerefresh/RouteRefreshController.kt +++ b/libnavigation-core/src/main/java/com/mapbox/navigation/core/routerefresh/RouteRefreshController.kt @@ -3,6 +3,7 @@ package com.mapbox.navigation.core.routerefresh import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI import com.mapbox.navigation.base.route.NavigationRoute import com.mapbox.navigation.core.RoutesInvalidatedObserver +import com.mapbox.navigation.core.directions.ForkPointPassedObserver import com.mapbox.navigation.core.directions.session.RoutesExtra import com.mapbox.navigation.core.directions.session.RoutesObserver import com.mapbox.navigation.core.directions.session.RoutesUpdatedResult @@ -121,7 +122,16 @@ class RouteRefreshController internal constructor( if (result.reason != RoutesExtra.ROUTES_UPDATE_REASON_REFRESH) { routeRefresherResultProcessor.reset() immediateRouteRefreshController.cancel() - plannedRouteRefreshController.startRoutesRefreshing(result.navigationRoutes) + + // "fork point passed" routes still have a chance to be useful for navigation, + // even if they are on the ignore list. We need to refresh them as well. + val validAlternatives = result.ignoredRoutes.filter { + it.reason == ForkPointPassedObserver.REASON_ALTERNATIVE_FORK_POINT_PASSED + }.map { it.navigationRoute } + + plannedRouteRefreshController.startRoutesRefreshing( + result.navigationRoutes + validAlternatives, + ) } } } diff --git a/libnavigation-core/src/main/java/com/mapbox/navigation/core/telemetry/UserFeedback.kt b/libnavigation-core/src/main/java/com/mapbox/navigation/core/telemetry/UserFeedback.kt index 04cebfe9698..51cbf4d5bfa 100644 --- a/libnavigation-core/src/main/java/com/mapbox/navigation/core/telemetry/UserFeedback.kt +++ b/libnavigation-core/src/main/java/com/mapbox/navigation/core/telemetry/UserFeedback.kt @@ -5,10 +5,10 @@ import com.mapbox.geojson.Point import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI import com.mapbox.navigation.core.MapboxNavigation import com.mapbox.navigation.core.internal.telemetry.ExtendedUserFeedback -import com.mapbox.navigation.core.internal.utils.encodedBitmapToNativeScreenshotFormat import com.mapbox.navigation.core.telemetry.events.FeedbackEvent import com.mapbox.navigation.core.telemetry.events.FeedbackHelper import com.mapbox.navigation.core.telemetry.events.FeedbackMetadata +import com.mapbox.navigator.ScreenshotFormat /** * Class for user feedbacks, contains properties that were passed to @@ -17,7 +17,7 @@ import com.mapbox.navigation.core.telemetry.events.FeedbackMetadata * @property feedbackType feedback type, one of [FeedbackEvent.Type] or a custom one * @property feedbackSubTypes list of [FeedbackEvent.SubType] and/or custom feedback subtypes * @property description description message - * @property screenshot screenshot that will be attached to feedback + * @property screenshot base64-encoded screenshot that will be attached to feedback * @property feedbackMetadata use it to attach feedback to a specific passed location. */ @ExperimentalPreviewMapboxNavigationAPI @@ -110,7 +110,7 @@ class UserFeedback private constructor( } /** - * Screenshot that will be attached to feedback + * Base64-encoded screenshot that will be attached to feedback */ fun screenshot(screenshot: String?): Builder = apply { this.screenshot = screenshot @@ -143,7 +143,7 @@ class UserFeedback private constructor( feedbackType, feedbackSubTypes, description, - screenshot?.encodedBitmapToNativeScreenshotFormat(), + ScreenshotFormat(null, screenshot), ) } diff --git a/libnavigation-core/src/test/java/com/mapbox/navigation/core/MapboxNavigationBaseTest.kt b/libnavigation-core/src/test/java/com/mapbox/navigation/core/MapboxNavigationBaseTest.kt index aeff304de2c..bb181c80c62 100644 --- a/libnavigation-core/src/test/java/com/mapbox/navigation/core/MapboxNavigationBaseTest.kt +++ b/libnavigation-core/src/test/java/com/mapbox/navigation/core/MapboxNavigationBaseTest.kt @@ -209,7 +209,7 @@ internal open class MapboxNavigationBaseTest { } returns evDynamicDataHolder mockkObject(RouteAlternativesControllerProvider) every { - RouteAlternativesControllerProvider.create(any(), any(), any(), any(), any()) + RouteAlternativesControllerProvider.create(any(), any(), any(), any(), any(), any()) } returns routeAlternativesController mockkObject(MapMatchingAPIProvider) every { diff --git a/libnavigation-core/src/test/java/com/mapbox/navigation/core/MapboxNavigationTest.kt b/libnavigation-core/src/test/java/com/mapbox/navigation/core/MapboxNavigationTest.kt index 7913a8b47c8..92a491d28a8 100644 --- a/libnavigation-core/src/test/java/com/mapbox/navigation/core/MapboxNavigationTest.kt +++ b/libnavigation-core/src/test/java/com/mapbox/navigation/core/MapboxNavigationTest.kt @@ -12,6 +12,7 @@ import com.mapbox.navigation.base.trip.model.RouteLegProgress import com.mapbox.navigation.base.trip.model.RouteProgress import com.mapbox.navigation.core.arrival.ArrivalController import com.mapbox.navigation.core.arrival.ArrivalProgressObserver +import com.mapbox.navigation.core.directions.ForkPointPassedObserver import com.mapbox.navigation.core.directions.session.DirectionsSessionRoutes import com.mapbox.navigation.core.directions.session.IgnoredRoute import com.mapbox.navigation.core.directions.session.RoutesExtra @@ -2301,6 +2302,19 @@ internal class MapboxNavigationTest : MapboxNavigationBaseTest() { } } + @Test + fun `ForkPointPassedObserver is registered during instantiation`() { + val forkPassedObserverMock = mockk(relaxed = true) + + every { + NavigationComponentProvider.createForkPointPassedObserver(any(), any()) + } returns forkPassedObserverMock + + createMapboxNavigation() + + verify { tripSession.registerRouteProgressObserver(refEq(forkPassedObserverMock)) } + } + private fun interceptRefreshObserver(): RouteRefreshObserver { val observers = mutableListOf() verify { routeRefreshController.registerRouteRefreshObserver(capture(observers)) } diff --git a/libnavigation-core/src/test/java/com/mapbox/navigation/core/RoutesProgressDataProviderTest.kt b/libnavigation-core/src/test/java/com/mapbox/navigation/core/RoutesProgressDataProviderTest.kt index dcc5c165179..d0070094ecf 100644 --- a/libnavigation-core/src/test/java/com/mapbox/navigation/core/RoutesProgressDataProviderTest.kt +++ b/libnavigation-core/src/test/java/com/mapbox/navigation/core/RoutesProgressDataProviderTest.kt @@ -49,6 +49,7 @@ class RoutesProgressDataProviderTest { altRouteGeometryIndex1, altLegGeometryIndex1, 1, + false, ), altId2 to RouteIndicesFactory.buildRouteIndices( altLegIndex2, @@ -56,6 +57,7 @@ class RoutesProgressDataProviderTest { altRouteGeometryIndex2, altLegGeometryIndex2, 2, + false, ), ) } diff --git a/libnavigation-core/src/test/java/com/mapbox/navigation/core/arrival/AutoArrivalControllerTest.kt b/libnavigation-core/src/test/java/com/mapbox/navigation/core/arrival/AutoArrivalControllerTest.kt index c9e68a987fd..70448122f01 100644 --- a/libnavigation-core/src/test/java/com/mapbox/navigation/core/arrival/AutoArrivalControllerTest.kt +++ b/libnavigation-core/src/test/java/com/mapbox/navigation/core/arrival/AutoArrivalControllerTest.kt @@ -1,66 +1,17 @@ package com.mapbox.navigation.core.arrival -import android.os.SystemClock -import com.mapbox.api.directions.v5.models.RouteLeg -import com.mapbox.navigation.base.trip.model.RouteLegProgress -import com.mapbox.navigation.core.arrival.AutoArrivalController.Companion.AUTO_ARRIVAL_NANOS -import io.mockk.every import io.mockk.mockk -import io.mockk.mockkStatic -import io.mockk.unmockkObject -import org.junit.After -import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue -import org.junit.Before import org.junit.Test class AutoArrivalControllerTest { private val arrivalController = AutoArrivalController() - @Before - fun setup() { - mockkStatic(SystemClock::class) - } - - @After - fun teardown() { - unmockkObject(SystemClock.elapsedRealtimeNanos()) - } - - @Test - fun `should navigate next at predicted arrival`() { - val routeLeg = mockk() - - mockNanos(100) - assertFalse(arrivalController.navigateNextRouteLeg(mockProgress(routeLeg))) - mockNanos(100 + AUTO_ARRIVAL_NANOS - 1) - assertFalse(arrivalController.navigateNextRouteLeg(mockProgress(routeLeg))) - mockNanos(100 + AUTO_ARRIVAL_NANOS) - assertTrue(arrivalController.navigateNextRouteLeg(mockProgress(routeLeg))) - } - @Test - fun `should restart timer if rerouted`() { - val routeLeg = mockk() - - mockNanos(100) - assertFalse(arrivalController.navigateNextRouteLeg(mockProgress(routeLeg))) - mockNanos(100L + AUTO_ARRIVAL_NANOS) - val reroutedRouteLeg = mockk() - assertFalse(arrivalController.navigateNextRouteLeg(mockProgress(reroutedRouteLeg))) - mockNanos(100 + AUTO_ARRIVAL_NANOS + AUTO_ARRIVAL_NANOS - 1) - assertFalse(arrivalController.navigateNextRouteLeg(mockProgress(reroutedRouteLeg))) - - mockNanos(100 + AUTO_ARRIVAL_NANOS + AUTO_ARRIVAL_NANOS) - assertTrue(arrivalController.navigateNextRouteLeg(mockProgress(reroutedRouteLeg))) - } - - private fun mockNanos(nanos: Long) = every { - SystemClock.elapsedRealtimeNanos() - } returns nanos + fun `navigateNextRouteLeg fire true`() { + val result = arrivalController.navigateNextRouteLeg(mockk()) - private fun mockProgress(mockedRouteLeg: RouteLeg) = mockk { - every { routeLeg } returns mockedRouteLeg + assertTrue(result) } } diff --git a/libnavigation-core/src/test/java/com/mapbox/navigation/core/directions/ForkPointPassedObserverTest.kt b/libnavigation-core/src/test/java/com/mapbox/navigation/core/directions/ForkPointPassedObserverTest.kt new file mode 100644 index 00000000000..f90a9bbf2ca --- /dev/null +++ b/libnavigation-core/src/test/java/com/mapbox/navigation/core/directions/ForkPointPassedObserverTest.kt @@ -0,0 +1,209 @@ +package com.mapbox.navigation.core.directions + +import com.mapbox.navigation.base.internal.extensions.internalAlternativeRouteIndices +import com.mapbox.navigation.base.route.NavigationRoute +import com.mapbox.navigation.base.trip.model.RouteProgress +import com.mapbox.navigation.core.SetRoutes +import com.mapbox.navigation.core.directions.session.DirectionsSession +import com.mapbox.navigation.core.directions.session.DirectionsSessionRoutes +import com.mapbox.navigation.core.directions.session.IgnoredRoute +import com.mapbox.navigation.testing.LoggingFrontendTestRule +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class ForkPointPassedObserverTest { + @get:Rule + val logRule = LoggingFrontendTestRule() + + private lateinit var directionsSession: DirectionsSession + private lateinit var currentLegIndex: () -> Int + private lateinit var observer: ForkPointPassedObserver + + @Before + fun setUp() { + directionsSession = mockk(relaxed = true) + currentLegIndex = mockk() + observer = ForkPointPassedObserver(directionsSession, currentLegIndex) + } + + @Test + fun `onRouteProgressChanged hides alternatives when fork point passed`() { + val routeProgress: RouteProgress = mockk { + every { internalAlternativeRouteIndices() } returns mapOf( + "route1" to mockk { every { isForkPointPassed } returns true }, + "route2" to mockk { every { isForkPointPassed } returns false }, + ) + } + + val route1 = mockk { every { id } returns "route1" } + val route2 = mockk { every { id } returns "route2" } + val routes = listOf(route1, route2) + + every { directionsSession.routes } returns routes + every { currentLegIndex.invoke() } returns 0 + + observer.onRouteProgressChanged(routeProgress) + + verify { + directionsSession.setNavigationRoutesFinished( + DirectionsSessionRoutes( + listOf(route2), + listOf( + IgnoredRoute( + route1, + ForkPointPassedObserver.REASON_ALTERNATIVE_FORK_POINT_PASSED, + ), + ), + SetRoutes.Alternatives(0), + ), + ) + } + } + + @Test + fun `onRouteProgressChanged does nothing when no fork point passed`() { + val mainRoute = mockk { every { id } returns "route1" } + val alternativeRoute = mockk { every { id } returns "route2" } + + val routeProgress: RouteProgress = mockk { + every { navigationRoute } returns mainRoute + every { internalAlternativeRouteIndices() } returns mapOf( + "route2" to mockk { every { isForkPointPassed } returns false }, + ) + } + + val routes = listOf( + mainRoute, + alternativeRoute, + ) + + every { directionsSession.routes } returns routes + every { currentLegIndex.invoke() } returns 0 + + observer.onRouteProgressChanged(routeProgress) + + verify(exactly = 0) { directionsSession.setNavigationRoutesFinished(any()) } + } + + @Test + fun `onRouteProgressChanged does nothing when no routes`() { + val routeProgress: RouteProgress = mockk { + every { internalAlternativeRouteIndices() } returns mapOf( + "route1" to mockk { every { isForkPointPassed } returns true }, + ) + } + + every { currentLegIndex.invoke() } returns 0 + every { directionsSession.routes } returns emptyList() + + observer.onRouteProgressChanged(routeProgress) + + verify(exactly = 0) { directionsSession.setNavigationRoutesFinished(any()) } + } + + @Test + fun `ignored alternative becomes available and is returned to routes list`() { + val route1 = mockk { every { id } returns "route1" } + val route2 = mockk { every { id } returns "route2" } + val currentRotues = listOf(route1, route2) + + val ignoredRoute = IgnoredRoute( + navigationRoute = mockk { every { id } returns "ignored_route" }, + reason = ForkPointPassedObserver.REASON_ALTERNATIVE_FORK_POINT_PASSED, + ) + + every { directionsSession.routes } returns currentRotues + every { directionsSession.ignoredRoutes } returns listOf(ignoredRoute) + every { currentLegIndex.invoke() } returns 0 + + val routeProgress: RouteProgress = mockk { + every { internalAlternativeRouteIndices() } returns mapOf( + "route1" to mockk(relaxed = true), + "route2" to mockk(relaxed = true), + "ignored_route" to mockk(relaxed = true), + ) + } + + observer.onRouteProgressChanged(routeProgress) + + verify { + directionsSession.setNavigationRoutesFinished( + DirectionsSessionRoutes( + currentRotues + ignoredRoute.navigationRoute, + emptyList(), + SetRoutes.Alternatives(0), + ), + ) + } + } + + @Test + fun `the same available and ignored routes should not trigger direction session update`() { + val route1 = mockk { every { id } returns "route1" } + val route2 = mockk { every { id } returns "route2" } + val currentRotues = listOf(route1, route2) + + val ignoredRoute = IgnoredRoute( + navigationRoute = mockk { every { id } returns "ignored_route" }, + reason = ForkPointPassedObserver.REASON_ALTERNATIVE_FORK_POINT_PASSED, + ) + + every { directionsSession.routes } returns currentRotues + every { directionsSession.ignoredRoutes } returns listOf(ignoredRoute) + every { currentLegIndex.invoke() } returns 0 + + val routeProgress: RouteProgress = mockk { + every { internalAlternativeRouteIndices() } returns mapOf( + "route1" to mockk(relaxed = true), + "route2" to mockk(relaxed = true), + "ignored_route" to mockk(relaxed = true) { + every { isForkPointPassed } returns true + }, + ) + } + + observer.onRouteProgressChanged(routeProgress) + + verify(exactly = 0) { + directionsSession.setNavigationRoutesFinished(any()) + } + } + + @Test + fun `alternative route with fork point passed from true to false returns to main routes`() { + val route1 = mockk { every { id } returns "route1" } + val route2 = mockk { every { id } returns "route2" } + val ignoredRoute = IgnoredRoute( + navigationRoute = mockk { every { id } returns "ignored_route" }, + reason = ForkPointPassedObserver.REASON_ALTERNATIVE_FORK_POINT_PASSED, + ) + + every { directionsSession.routes } returns listOf(route1, route2) + every { directionsSession.ignoredRoutes } returns listOf(ignoredRoute) + every { currentLegIndex.invoke() } returns 0 + + val routeProgress: RouteProgress = mockk { + every { internalAlternativeRouteIndices() } returns mapOf( + "route1" to mockk { every { isForkPointPassed } returns false }, + "route2" to mockk { every { isForkPointPassed } returns false }, + "ignored_route" to mockk { every { isForkPointPassed } returns false }, + ) + } + + observer.onRouteProgressChanged(routeProgress) + + verify { + directionsSession.setNavigationRoutesFinished( + DirectionsSessionRoutes( + listOf(route1, route2, ignoredRoute.navigationRoute), + emptyList(), + SetRoutes.Alternatives(0), + ), + ) + } + } +} diff --git a/libnavigation-core/src/test/java/com/mapbox/navigation/core/navigator/NavigatorMapperTest.kt b/libnavigation-core/src/test/java/com/mapbox/navigation/core/navigator/NavigatorMapperTest.kt index 1a198250e94..3b6ddbe9b8d 100644 --- a/libnavigation-core/src/test/java/com/mapbox/navigation/core/navigator/NavigatorMapperTest.kt +++ b/libnavigation-core/src/test/java/com/mapbox/navigation/core/navigator/NavigatorMapperTest.kt @@ -106,8 +106,8 @@ class NavigatorMapperTest { currentRouteGeometryIndex = routeGeometryIndex, inParkingAisle = true, alternativeRoutesIndices = mapOf( - "id#2" to RouteIndicesFactory.buildRouteIndices(2, 4, 6, 8, 10), - "id#3" to RouteIndicesFactory.buildRouteIndices(3, 7, 5, 11, 9), + "id#2" to RouteIndicesFactory.buildRouteIndices(2, 4, 6, 8, 10, false), + "id#3" to RouteIndicesFactory.buildRouteIndices(3, 7, 5, 11, 9, false), ), ) diff --git a/libnavigation-core/src/test/java/com/mapbox/navigation/core/routealternatives/RouteAlternativesControllerTest.kt b/libnavigation-core/src/test/java/com/mapbox/navigation/core/routealternatives/RouteAlternativesControllerTest.kt index 2820fd6c617..7b0c907befe 100644 --- a/libnavigation-core/src/test/java/com/mapbox/navigation/core/routealternatives/RouteAlternativesControllerTest.kt +++ b/libnavigation-core/src/test/java/com/mapbox/navigation/core/routealternatives/RouteAlternativesControllerTest.kt @@ -9,6 +9,8 @@ import com.mapbox.navigation.base.route.NavigationRoute import com.mapbox.navigation.base.route.RouteAlternativesOptions import com.mapbox.navigation.base.route.RouterOrigin import com.mapbox.navigation.base.trip.model.RouteProgress +import com.mapbox.navigation.core.directions.session.DirectionsSession +import com.mapbox.navigation.core.directions.session.findRoute import com.mapbox.navigation.core.internal.routealternatives.NavigationRouteAlternativesObserver import com.mapbox.navigation.core.trip.session.TripSession import com.mapbox.navigation.navigator.internal.MapboxNativeNavigator @@ -25,14 +27,18 @@ import com.mapbox.navigation.testing.factories.createRouteOptions import com.mapbox.navigation.utils.internal.ThreadController import com.mapbox.navigator.RouteAlternative import com.mapbox.navigator.RouteAlternativesControllerInterface +import com.mapbox.navigator.RouteAlternativesObserver import com.mapbox.navigator.RouteIntersection import io.mockk.every import io.mockk.just import io.mockk.mockk import io.mockk.mockkObject +import io.mockk.mockkStatic import io.mockk.runs import io.mockk.slot +import io.mockk.spyk import io.mockk.unmockkObject +import io.mockk.unmockkStatic import io.mockk.verify import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.pauseDispatcher @@ -71,6 +77,8 @@ class RouteAlternativesControllerTest { } private val tripSession: TripSession = mockk(relaxed = true) + private val directionSession: DirectionsSession = mockk(relaxed = true) + private fun createRouteAlternativesController( options: RouteAlternativesOptions = RouteAlternativesOptions.Builder().build(), routeParsingManager: RouteParsingManager = createParsingManagerForTest(), @@ -80,11 +88,16 @@ class RouteAlternativesControllerTest { tripSession, ThreadController(), routeParsingManager, + directionSession, ) @Before fun setup() { mockkObject(ThreadController) + + mockkStatic(DirectionsSession::findRoute) + every { directionSession.findRoute(any()) } returns null + every { ThreadController.IODispatcher } returns coroutineRule.testDispatcher every { ThreadController.DefaultDispatcher } returns coroutineRule.testDispatcher } @@ -92,6 +105,7 @@ class RouteAlternativesControllerTest { @After fun tearDown() { unmockkObject(ThreadController) + unmockkStatic(DirectionsSession::findRoute) } @Test @@ -1488,6 +1502,126 @@ class RouteAlternativesControllerTest { verify(exactly = 1) { controllerInterface.removeObserver(any()) } } + @Test + fun `during alternatives parsing should look up for existing route`() = coroutineRule.runBlockingTest { + fun createRouteWithId( + newRouteId: String, + ) = createNativeAlternativeMock().apply { + val spyk = spyk(route) { + every { routeId } returns newRouteId + } + every { route } returns spyk + } + + val nativeRoute = createRouteWithId("route") + val firstAlternative = createRouteWithId("alternative_route_1") + val secondAlternative = createRouteWithId("alternative_route_2") + + every { directionSession.findRoute(any()) } returns null + + val routeAlternativesController = createRouteAlternativesController() + + val nativeObserver = slot() + every { controllerInterface.addObserver(capture(nativeObserver)) } just runs + + var ignore: UpdateRouteSuggestion? = null + routeAlternativesController.setRouteUpdateSuggestionListener { + ignore = it + } + + nativeObserver.captured.onRouteAlternativesUpdated( + nativeRoute.route, + listOf(firstAlternative, secondAlternative), + emptyList(), + ) + + listOf( + nativeRoute.route.routeId, + firstAlternative.route.routeId, + secondAlternative.route.routeId, + ).forEach { routeId -> + verify(exactly = 1) { + directionSession.findRoute(routeId) + } + } + } + + @Test + fun `during alternatives parsing if route already exist should not parse again`() = coroutineRule.runBlockingTest { + fun createRouteWithId( + newRouteId: String, + newResponseUUID: String, + ) = createNativeAlternativeMock().apply { + val spyk = spyk(route) { + every { routeId } returns newRouteId + every { responseUuid } returns newResponseUUID + } + every { route } returns spyk + } + + val nativeRoute = createRouteWithId("route", "uuid1").route + val firstAlternative = createRouteWithId("alternative_route_1", "uuid2") + val secondAlternative = createRouteWithId("alternative_route_2", "uuid2") + + val firstAlternativeRoute = firstAlternative.route + val secondAlternativeRoute = secondAlternative.route + + every { directionSession.findRoute(eq("route")) } returns null + every { directionSession.findRoute(eq("alternative_route_1")) } returns null + every { + directionSession.findRoute(eq("alternative_route_2")) + } returns mockk( + relaxed = true, + ) + + val routeParsingMock = spyk(createParsingManagerForTest()) { + val fromJson = DirectionsResponse.fromJson( + FileUtils.loadJsonFixture("route_alternative_from_native.json"), + ) + every { parseRouteToDirections(any()) } returns fromJson + } + + val routeAlternativesController = createRouteAlternativesController( + routeParsingManager = routeParsingMock, + ) + + val nativeObserver = slot() + every { controllerInterface.addObserver(capture(nativeObserver)) } just runs + + var ignore: UpdateRouteSuggestion? = null + routeAlternativesController.setRouteUpdateSuggestionListener { + ignore = it + } + + nativeObserver.captured.onRouteAlternativesUpdated( + nativeRoute, + listOf(firstAlternative, secondAlternative), + emptyList(), + ) + + listOf( + nativeRoute.routeId, + firstAlternativeRoute.routeId, + secondAlternativeRoute.routeId, + ).forEach { routeId -> + verify(exactly = 1) { + directionSession.findRoute(routeId) + } + } + + verify(exactly = 1) { + routeParsingMock.parseRouteToDirections(refEq(nativeRoute)) + } + + verify(exactly = 1) { + routeParsingMock.parseRouteToDirections(refEq(firstAlternativeRoute)) + } + + verify(exactly = 0) { + routeParsingMock.parseRouteToDirections(refEq(secondAlternativeRoute)) + } + } + private val nativeInfoFork = com.mapbox.navigator.AlternativeRouteInfo( 100.0, // distance 200.0, // duration @@ -1541,12 +1675,13 @@ class RouteAlternativesControllerTest { val responseJson = FileUtils.loadJsonFixture(fileName) val response = DirectionsResponse.fromJson(responseJson) val nativeRoute = createRouteInterface( - responseJson = FileUtils.loadJsonFixture(fileName), + responseJson = responseJson, requestURI = routeRequestUrl.toString(), routerOrigin = routerOrigin, responseUUID = response.uuid()!!, routeIndex = routeIndex, ) + return mockk { every { route } returns nativeRoute every { isNew } returns true diff --git a/libnavigation-core/src/test/java/com/mapbox/navigation/core/telemetry/UserFeedbackTest.kt b/libnavigation-core/src/test/java/com/mapbox/navigation/core/telemetry/UserFeedbackTest.kt new file mode 100644 index 00000000000..7106827e6bc --- /dev/null +++ b/libnavigation-core/src/test/java/com/mapbox/navigation/core/telemetry/UserFeedbackTest.kt @@ -0,0 +1,32 @@ +package com.mapbox.navigation.core.telemetry + +import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI +import com.mapbox.navigation.core.telemetry.UserFeedback.Companion.mapToNative +import com.mapbox.navigation.core.telemetry.events.FeedbackEvent +import com.mapbox.navigator.ScreenshotFormat +import org.junit.Assert.assertEquals +import org.junit.Test + +@OptIn(ExperimentalPreviewMapboxNavigationAPI::class) +class UserFeedbackTest { + + @Test + fun `test mapToNative()`() { + val userFeedback = UserFeedback.Builder( + feedbackType = FeedbackEvent.ARRIVAL_FEEDBACK_GOOD, + description = "test-description", + ) + .feedbackSubTypes(listOf(FeedbackEvent.MANEUVER_INCORRECT)) + .screenshot("test-base-64") + .build() + + val nativeUserFeedback = com.mapbox.navigator.UserFeedback( + FeedbackEvent.ARRIVAL_FEEDBACK_GOOD, + listOf(FeedbackEvent.MANEUVER_INCORRECT), + "test-description", + ScreenshotFormat(null, "test-base-64"), + ) + + assertEquals(nativeUserFeedback, userFeedback.mapToNative()) + } +} diff --git a/libnavigation-metrics/gradle.properties b/libnavigation-metrics/gradle.properties index c2468ece0c3..abc7e13424f 100644 --- a/libnavigation-metrics/gradle.properties +++ b/libnavigation-metrics/gradle.properties @@ -1,3 +1,2 @@ -POM_ARTIFACT_ID=metrics POM_ARTIFACT_TITLE=Mapbox Navigation Metrics POM_DESCRIPTION=Artifact that provides the default implementation of the metrics integration \ No newline at end of file diff --git a/libnavigation-tripdata/gradle.properties b/libnavigation-tripdata/gradle.properties index dfe7a1662a6..b8492a60b7b 100644 --- a/libnavigation-tripdata/gradle.properties +++ b/libnavigation-tripdata/gradle.properties @@ -1,3 +1,2 @@ -POM_ARTIFACT_ID=tripdata POM_ARTIFACT_TITLE=Mapbox Navigation SDK POM_DESCRIPTION=Artifact that provides trip related data diff --git a/libnavigation-util/gradle.properties b/libnavigation-util/gradle.properties index 7e8671f346a..d38ea4fb26e 100644 --- a/libnavigation-util/gradle.properties +++ b/libnavigation-util/gradle.properties @@ -1,3 +1,2 @@ -POM_ARTIFACT_ID=utils POM_ARTIFACT_TITLE=Mapbox Navigation Utils POM_DESCRIPTION=Artifact that provides basic navigation utilities diff --git a/libnavigation-voice/gradle.properties b/libnavigation-voice/gradle.properties index a92178d0f04..121e0561b19 100644 --- a/libnavigation-voice/gradle.properties +++ b/libnavigation-voice/gradle.properties @@ -1,3 +1,2 @@ -POM_ARTIFACT_ID=voice POM_ARTIFACT_TITLE=Mapbox Navigation SDK POM_DESCRIPTION=Artifact that allows users to call into Voice API \ No newline at end of file diff --git a/libnavigator/gradle.properties b/libnavigator/gradle.properties index 5b4cf9d53fa..43500037981 100644 --- a/libnavigator/gradle.properties +++ b/libnavigator/gradle.properties @@ -1,3 +1,2 @@ -POM_ARTIFACT_ID=navigator POM_ARTIFACT_TITLE=Mapbox Navigation Native wrapper POM_DESCRIPTION=Artifact that provides the native capabilities of the SDK diff --git a/libnavigator/gradlew.bat b/libnavigator/gradlew.bat index e95643d6a2c..f9553162f12 100644 --- a/libnavigator/gradlew.bat +++ b/libnavigator/gradlew.bat @@ -1,84 +1,84 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/libnavui-base/gradle.properties b/libnavui-base/gradle.properties index 28de95a23f5..6a9b46d19b4 100644 --- a/libnavui-base/gradle.properties +++ b/libnavui-base/gradle.properties @@ -1,3 +1,2 @@ -POM_ARTIFACT_ID=ui-base POM_ARTIFACT_TITLE=Mapbox Navigation SDK POM_DESCRIPTION=Artifact that provides the set of interface definitions and DTOs used in the modular UI components \ No newline at end of file diff --git a/libnavui-maps/build.gradle b/libnavui-maps/build.gradle index 78a0369771b..7ced9e341c1 100644 --- a/libnavui-maps/build.gradle +++ b/libnavui-maps/build.gradle @@ -46,7 +46,7 @@ android { dependencies { api project(":libnavui-base") - api (dependenciesList.mapboxMapSdk) + api(dependenciesList.mapboxMapSdk) api dependenciesList.mapboxSdkTurf implementation dependenciesList.androidXAppCompat diff --git a/libnavui-maps/gradle.properties b/libnavui-maps/gradle.properties index 8037f4a7ade..42561356720 100644 --- a/libnavui-maps/gradle.properties +++ b/libnavui-maps/gradle.properties @@ -1,3 +1,2 @@ -POM_ARTIFACT_ID=ui-maps POM_ARTIFACT_TITLE=Mapbox Navigation SDK POM_DESCRIPTION=Artifact that allows users to call into Maps API \ No newline at end of file diff --git a/libnavui-util/gradle.properties b/libnavui-util/gradle.properties index 9a7639a26c1..29298699210 100644 --- a/libnavui-util/gradle.properties +++ b/libnavui-util/gradle.properties @@ -1,3 +1,2 @@ -POM_ARTIFACT_ID=ui-utils POM_ARTIFACT_TITLE=Mapbox Navigation SDK POM_DESCRIPTION=Artifact that provides basic navigation utilities \ No newline at end of file diff --git a/libtesting-navigation-core-utils/src/main/java/com/mapbox/navigation/testing/utils/routes/RoutesProvider.kt b/libtesting-navigation-core-utils/src/main/java/com/mapbox/navigation/testing/utils/routes/RoutesProvider.kt index 3d7c108b256..4ade6acaec6 100644 --- a/libtesting-navigation-core-utils/src/main/java/com/mapbox/navigation/testing/utils/routes/RoutesProvider.kt +++ b/libtesting-navigation-core-utils/src/main/java/com/mapbox/navigation/testing/utils/routes/RoutesProvider.kt @@ -196,6 +196,29 @@ object RoutesProvider { ) } + fun dc_short_alternative_with_fork_point(context: Context): MockRoute { + val jsonResponse = + readRawFileText(context, R.raw.route_response_alternative_and_fork_point) + + val coordinates = listOf( + Point.fromLngLat(-77.02949848947188, 38.90802940158288), + Point.fromLngLat(-77.028611, 38.910507), + ) + + return MockRoute( + jsonResponse, + DirectionsResponse.fromJson(jsonResponse), + listOf( + MockDirectionsRequestHandler( + profile = DirectionsCriteria.PROFILE_DRIVING_TRAFFIC, + jsonResponse = jsonResponse, + expectedCoordinates = coordinates, + ), + ), + coordinates, + ) + } + fun dc_short_with_alternative_reroute(context: Context): MockRoute { val jsonResponse = readRawFileText(context, R.raw.route_response_dc_short_with_alternative_reroute) diff --git a/libtesting-resources/src/main/res/raw/route_response_alternative_and_fork_point.json b/libtesting-resources/src/main/res/raw/route_response_alternative_and_fork_point.json new file mode 100644 index 00000000000..e1095870f06 --- /dev/null +++ b/libtesting-resources/src/main/res/raw/route_response_alternative_and_fork_point.json @@ -0,0 +1,941 @@ +{ + "routes": [ + { + "weight_name": "auto", + "weight": 276.351, + "duration": 137.477, + "distance": 389.138, + "legs": [ + { + "via_waypoints": [], + "admins": [{"iso_3166_1_alpha3": "USA", "iso_3166_1": "US"}], + "weight": 276.351, + "duration": 137.477, + "steps": [ + { + "intersections": [ + { + "entry": [true], + "bearings": [360], + "duration": 16.533, + "mapbox_streets_v8": {"class": "secondary"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "weight": 20.253, + "geometry_index": 0, + "location": [-77.029628, 38.908013] + }, + { + "entry": [true, false, false, false], + "in": 2, + "bearings": [0, 90, 180, 270], + "duration": 2.244, + "turn_weight": 1, + "turn_duration": 0.007, + "mapbox_streets_v8": {"class": "secondary"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "weight": 3.573, + "geometry_index": 1, + "location": [-77.029631, 38.908508] + }, + { + "mapbox_streets_v8": {"class": "secondary"}, + "location": [-77.029631, 38.908575], + "geometry_index": 2, + "admin_index": 0, + "weight": 5.15, + "is_urban": true, + "traffic_signal": true, + "turn_duration": 2.022, + "turn_weight": 2, + "duration": 4.652, + "bearings": [17, 89, 180, 270, 344], + "out": 0, + "in": 2, + "entry": [true, true, false, false, false] + }, + { + "entry": [true, false, false, false], + "in": 2, + "bearings": [4, 90, 197, 270], + "duration": 5.857, + "turn_weight": 1, + "turn_duration": 0.048, + "mapbox_streets_v8": {"class": "secondary"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "weight": 8.245, + "geometry_index": 3, + "location": [-77.029597, 38.908663] + }, + { + "entry": [true, true, false], + "in": 2, + "bearings": [24, 89, 184], + "duration": 8.016, + "turn_weight": 0.75, + "turn_duration": 0.026, + "mapbox_streets_v8": {"class": "secondary"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "weight": 10.673, + "geometry_index": 6, + "location": [-77.029576, 38.908864] + }, + { + "entry": [true, false, false, false], + "in": 2, + "bearings": [35, 111, 204, 293], + "duration": 2.841, + "turn_weight": 1, + "turn_duration": 0.014, + "mapbox_streets_v8": {"class": "secondary"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "weight": 4.307, + "geometry_index": 10, + "location": [-77.029492, 38.909009] + }, + { + "bearings": [52, 215], + "entry": [true, false], + "in": 1, + "turn_weight": 0.75, + "mapbox_streets_v8": {"class": "secondary"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "geometry_index": 11, + "location": [-77.02945, 38.909055] + } + ], + "maneuver": { + "type": "depart", + "instruction": "Drive north on 13th Street Northwest.", + "bearing_after": 360, + "bearing_before": 0, + "location": [-77.029628, 38.908013] + }, + "name": "13th Street Northwest", + "duration": 49.353, + "distance": 139.887, + "driving_side": "right", + "weight": 63.975, + "mode": "driving", + "geometry": "yeweiAvno|qC}]DeC?oDcAaAYoF?_BOqAc@mAi@gAm@y@k@{AsAmAmBiA{BkAgC" + }, + { + "intersections": [ + { + "entry": [true, false, false], + "in": 1, + "bearings": [53, 232, 259], + "duration": 0.901, + "turn_weight": 5.75, + "turn_duration": 0.007, + "mapbox_streets_v8": {"class": "roundabout"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "weight": 6.893, + "geometry_index": 14, + "location": [-77.029265, 38.909169] + }, + { + "lanes": [ + { + "indications": ["straight"], + "valid": false, + "active": false + }, + { + "indications": ["straight"], + "valid_indication": "straight", + "valid": true, + "active": true + }, + { + "indications": ["straight"], + "valid": false, + "active": false + }, + {"indications": ["right"], "valid": false, "active": false} + ], + "entry": [true, false], + "in": 1, + "bearings": [44, 233], + "duration": 4.256, + "mapbox_streets_v8": {"class": "roundabout"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "weight": 5.227, + "geometry_index": 15, + "location": [-77.029203, 38.909205] + }, + { + "lanes": [ + { + "indications": ["straight"], + "valid": false, + "active": false + }, + { + "indications": ["straight"], + "valid_indication": "straight", + "valid": true, + "active": true + }, + { + "indications": ["straight"], + "valid": false, + "active": false + }, + {"indications": ["right"], "valid": false, "active": false} + ], + "location": [-77.028972, 38.909425], + "geometry_index": 20, + "admin_index": 0, + "weight": 3.969, + "is_urban": true, + "mapbox_streets_v8": {"class": "roundabout"}, + "traffic_signal": true, + "turn_duration": 2.142, + "duration": 5.33, + "bearings": [9, 37, 215], + "out": 0, + "in": 2, + "entry": [true, true, false] + }, + { + "entry": [false, false, false, true], + "in": 1, + "bearings": [96, 189, 276, 360], + "duration": 2.672, + "turn_duration": 0.036, + "mapbox_streets_v8": {"class": "roundabout"}, + "is_urban": true, + "admin_index": 0, + "out": 3, + "weight": 3.308, + "geometry_index": 22, + "location": [-77.028939, 38.909582] + }, + { + "entry": [false, false, true], + "in": 1, + "bearings": [101, 180, 352], + "duration": 0.195, + "turn_duration": 0.033, + "mapbox_streets_v8": {"class": "roundabout"}, + "is_urban": true, + "admin_index": 0, + "out": 2, + "weight": 0.22, + "geometry_index": 27, + "location": [-77.02894, 38.909713] + }, + { + "lanes": [ + { + "indications": ["straight"], + "valid": false, + "active": false + }, + { + "indications": ["straight", "right"], + "valid_indication": "straight", + "valid": true, + "active": true + }, + {"indications": ["right"], "valid": false, "active": false} + ], + "location": [-77.028943, 38.909729], + "geometry_index": 28, + "admin_index": 0, + "weight": 8.451, + "is_urban": true, + "mapbox_streets_v8": {"class": "primary"}, + "turn_duration": 0.159, + "turn_weight": 5, + "duration": 3.022, + "bearings": [39, 172, 341], + "out": 0, + "in": 1, + "entry": [true, false, true] + }, + { + "entry": [true, false, false], + "in": 2, + "bearings": [59, 175, 219], + "duration": 2.03, + "turn_weight": 0.75, + "turn_duration": 0.025, + "mapbox_streets_v8": {"class": "primary"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "weight": 3.323, + "geometry_index": 32, + "location": [-77.028811, 38.909857] + }, + { + "entry": [true, false, false, false], + "in": 2, + "bearings": [66, 152, 239, 335], + "duration": 15.612, + "turn_duration": 0.011, + "mapbox_streets_v8": {"class": "primary"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "weight": 19.11, + "geometry_index": 34, + "location": [-77.028745, 38.909888] + }, + { + "bearings": [1, 67, 180, 246], + "entry": [false, true, false, false], + "in": 3, + "turn_duration": 0.008, + "mapbox_streets_v8": {"class": "primary"}, + "is_urban": true, + "admin_index": 0, + "out": 1, + "geometry_index": 35, + "location": [-77.028196, 38.910077] + } + ], + "maneuver": { + "type": "rotary", + "exit": 2, + "instruction": "Enter Logan Circle Northwest and take the 2nd exit onto Rhode Island Avenue Northwest.", + "modifier": "straight", + "bearing_after": 53, + "bearing_before": 52, + "location": [-77.029265, 38.909169] + }, + "rotary_name": "Logan Circle Northwest", + "name": "Rhode Island Avenue Northwest", + "duration": 36.981, + "distance": 159.611, + "driving_side": "right", + "weight": 54.176, + "mode": "driving", + "geometry": "anyeiA`xn|qCgA{BmAsBoAiBwAyAcBwA}A}@aFs@wAMs@Is@AiA@y@FYB_@DwBsBo@m@o@s@g@q@[u@a@mAyJia@cAqE" + }, + { + "intersections": [ + { + "mapbox_streets_v8": {"class": "tertiary"}, + "location": [-77.028091, 38.910111], + "geometry_index": 36, + "admin_index": 0, + "weight": 14.151, + "is_urban": true, + "traffic_signal": true, + "turn_duration": 4.254, + "turn_weight": 10, + "duration": 7.673, + "bearings": [65, 179, 247, 359], + "out": 3, + "in": 2, + "entry": [true, false, false, true] + }, + { + "mapbox_streets_v8": {"class": "primary"}, + "location": [-77.028094, 38.910256], + "geometry_index": 37, + "admin_index": 0, + "weight": 25.094, + "is_urban": true, + "traffic_signal": true, + "turn_duration": 9.102, + "turn_weight": 22.5, + "duration": 11.117, + "bearings": [66, 179, 244, 359], + "out": 2, + "in": 1, + "entry": [false, false, true, true] + }, + { + "bearings": [1, 64, 182, 246], + "entry": [false, false, false, true], + "in": 1, + "turn_duration": 0.008, + "mapbox_streets_v8": {"class": "primary"}, + "is_urban": true, + "admin_index": 0, + "out": 3, + "geometry_index": 38, + "location": [-77.028193, 38.910219] + } + ], + "maneuver": { + "type": "continue", + "instruction": "Make a left U-turn at 12th Street Northwest onto Rhode Island Avenue Northwest.", + "modifier": "uturn", + "bearing_after": 244, + "bearing_before": 67, + "location": [-77.028091, 38.910111] + }, + "name": "Rhode Island Avenue Northwest", + "duration": 23.247, + "distance": 46.666, + "driving_side": "right", + "weight": 44.692, + "mode": "driving", + "geometry": "}h{eiAtnl|qCaHDhAdEvCzL" + }, + { + "intersections": [ + { + "entry": [false, true, true], + "in": 0, + "bearings": [66, 245, 336], + "duration": 6.138, + "turn_weight": 80, + "turn_duration": 2.105, + "mapbox_streets_v8": {"class": "service"}, + "is_urban": true, + "admin_index": 0, + "out": 2, + "weight": 85.145, + "geometry_index": 39, + "location": [-77.028415, 38.910143] + }, + { + "bearings": [66, 156, 246, 336], + "entry": [false, false, false, true], + "in": 1, + "turn_weight": 2, + "turn_duration": 0.007, + "mapbox_streets_v8": {"class": "service"}, + "is_urban": true, + "admin_index": 0, + "out": 3, + "geometry_index": 40, + "location": [-77.028447, 38.910198] + } + ], + "maneuver": { + "type": "turn", + "instruction": "Turn right.", + "modifier": "right", + "bearing_after": 336, + "bearing_before": 246, + "location": [-77.028415, 38.910143] + }, + "name": "", + "duration": 27.897, + "distance": 42.974, + "driving_side": "right", + "weight": 113.508, + "mode": "driving", + "geometry": "}j{eiA|bm|qCmB~@uHnD}@Vw@N}@B_C?" + }, + { + "intersections": [ + { + "bearings": [175], + "entry": [true], + "in": 0, + "admin_index": 0, + "geometry_index": 45, + "location": [-77.028557, 38.910507] + } + ], + "maneuver": { + "type": "arrive", + "instruction": "Your destination is on the left.", + "modifier": "left", + "bearing_after": 0, + "bearing_before": 355, + "location": [-77.028557, 38.910507] + }, + "name": "", + "duration": 0, + "distance": 0, + "driving_side": "right", + "weight": 0, + "mode": "driving", + "geometry": "ua|eiAxkm|qC??" + } + ], + "distance": 389.138, + "summary": "13th Street Northwest, Logan Circle Northwest" + } + ], + "geometry": "yeweiAvno|qC}]DeC?oDcAaAYoF?_BOqAc@mAi@gAm@y@k@{AsAmAmBiA{BkAgCgA{BmAsBoAiBwAyAcBwA}A}@aFs@wAMs@Is@AiA@y@FYB_@DwBsBo@m@o@s@g@q@[u@a@mAyJia@cAqEaHDhAdEvCzLmB~@uHnD}@Vw@N}@B_C?" + }, + { + "weight_name": "auto", + "weight": 285.1, + "duration": 154.506, + "distance": 405.4, + "legs": [ + { + "via_waypoints": [], + "admins": [{"iso_3166_1_alpha3": "USA", "iso_3166_1": "US"}], + "weight": 285.1, + "duration": 154.506, + "steps": [ + { + "intersections": [ + { + "entry": [true], + "bearings": [360], + "duration": 16.533, + "mapbox_streets_v8": {"class": "secondary"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "weight": 20.253, + "geometry_index": 0, + "location": [-77.029628, 38.908013] + }, + { + "entry": [true, false, false, false], + "in": 2, + "bearings": [0, 90, 180, 270], + "duration": 2.244, + "turn_weight": 1, + "turn_duration": 0.007, + "mapbox_streets_v8": {"class": "secondary"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "weight": 3.573, + "geometry_index": 1, + "location": [-77.029631, 38.908508] + }, + { + "mapbox_streets_v8": {"class": "secondary"}, + "location": [-77.029631, 38.908575], + "geometry_index": 2, + "admin_index": 0, + "weight": 5.15, + "is_urban": true, + "traffic_signal": true, + "turn_duration": 2.022, + "turn_weight": 2, + "duration": 4.652, + "bearings": [17, 89, 180, 270, 344], + "out": 0, + "in": 2, + "entry": [true, true, false, false, false] + }, + { + "entry": [true, false, false, false], + "in": 2, + "bearings": [4, 90, 197, 270], + "duration": 5.857, + "turn_weight": 1, + "turn_duration": 0.048, + "mapbox_streets_v8": {"class": "secondary"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "weight": 8.245, + "geometry_index": 3, + "location": [-77.029597, 38.908663] + }, + { + "entry": [true, true, false], + "in": 2, + "bearings": [24, 89, 184], + "duration": 8.016, + "turn_weight": 0.75, + "turn_duration": 0.026, + "mapbox_streets_v8": {"class": "secondary"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "weight": 10.673, + "geometry_index": 6, + "location": [-77.029576, 38.908864] + }, + { + "entry": [true, false, false, false], + "in": 2, + "bearings": [35, 111, 204, 293], + "duration": 2.841, + "turn_weight": 1, + "turn_duration": 0.014, + "mapbox_streets_v8": {"class": "secondary"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "weight": 4.307, + "geometry_index": 10, + "location": [-77.029492, 38.909009] + }, + { + "bearings": [52, 215], + "entry": [true, false], + "in": 1, + "turn_weight": 0.75, + "mapbox_streets_v8": {"class": "secondary"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "geometry_index": 11, + "location": [-77.02945, 38.909055] + } + ], + "maneuver": { + "type": "depart", + "instruction": "Drive north on 13th Street Northwest.", + "bearing_after": 360, + "bearing_before": 0, + "location": [-77.029628, 38.908013] + }, + "name": "13th Street Northwest", + "duration": 49.353, + "distance": 139.887, + "driving_side": "right", + "weight": 63.975, + "mode": "driving", + "geometry": "yeweiAvno|qC}]DeC?oDcAaAYoF?_BOqAc@mAi@gAm@y@k@{AsAmAmBiA{BkAgC" + }, + { + "intersections": [ + { + "entry": [true, false, false], + "in": 1, + "bearings": [53, 232, 259], + "duration": 0.901, + "turn_weight": 5.75, + "turn_duration": 0.007, + "mapbox_streets_v8": {"class": "roundabout"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "weight": 6.893, + "geometry_index": 14, + "location": [-77.029265, 38.909169] + }, + { + "lanes": [ + { + "indications": ["straight"], + "valid": false, + "active": false + }, + { + "indications": ["straight"], + "valid": false, + "active": false + }, + { + "indications": ["straight"], + "valid_indication": "straight", + "valid": true, + "active": true + }, + {"indications": ["right"], "valid": false, "active": false} + ], + "entry": [true, false], + "in": 1, + "bearings": [44, 233], + "duration": 4.256, + "mapbox_streets_v8": {"class": "roundabout"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "weight": 5.227, + "geometry_index": 15, + "location": [-77.029203, 38.909205] + }, + { + "lanes": [ + { + "indications": ["straight"], + "valid": false, + "active": false + }, + { + "indications": ["straight"], + "valid": false, + "active": false + }, + { + "indications": ["straight"], + "valid_indication": "straight", + "valid": true, + "active": true + }, + {"indications": ["right"], "valid": false, "active": false} + ], + "location": [-77.028972, 38.909425], + "geometry_index": 20, + "admin_index": 0, + "weight": 3.969, + "is_urban": true, + "mapbox_streets_v8": {"class": "roundabout"}, + "traffic_signal": true, + "turn_duration": 2.142, + "duration": 5.33, + "bearings": [9, 37, 215], + "out": 0, + "in": 2, + "entry": [true, true, false] + }, + { + "entry": [false, false, false, true], + "in": 1, + "bearings": [96, 189, 276, 360], + "duration": 2.672, + "turn_duration": 0.036, + "mapbox_streets_v8": {"class": "roundabout"}, + "is_urban": true, + "admin_index": 0, + "out": 3, + "weight": 3.308, + "geometry_index": 22, + "location": [-77.028939, 38.909582] + }, + { + "entry": [false, false, true], + "in": 1, + "bearings": [101, 180, 352], + "duration": 0.195, + "turn_duration": 0.033, + "mapbox_streets_v8": {"class": "roundabout"}, + "is_urban": true, + "admin_index": 0, + "out": 2, + "weight": 0.22, + "geometry_index": 27, + "location": [-77.02894, 38.909713] + }, + { + "entry": [true, false, true], + "in": 1, + "bearings": [39, 172, 341], + "duration": 2.777, + "lanes": [ + { + "indications": ["straight"], + "valid": false, + "active": false + }, + { + "indications": ["straight", "right"], + "valid_indication": "straight", + "valid": true, + "active": true + }, + {"indications": ["right"], "valid": false, "active": false} + ], + "turn_duration": 0.041, + "mapbox_streets_v8": {"class": "roundabout"}, + "is_urban": true, + "admin_index": 0, + "out": 2, + "weight": 3.408, + "geometry_index": 28, + "location": [-77.028943, 38.909729] + }, + { + "entry": [false, false, false, true], + "in": 1, + "bearings": [68, 161, 246, 326], + "duration": 3.357, + "turn_duration": 0.048, + "mapbox_streets_v8": {"class": "roundabout"}, + "is_urban": true, + "admin_index": 0, + "out": 3, + "weight": 4.043, + "geometry_index": 33, + "location": [-77.029007, 38.90987] + }, + { + "entry": [false, false, true], + "in": 1, + "bearings": [106, 145, 311], + "duration": 0.175, + "turn_duration": 0.052, + "mapbox_streets_v8": {"class": "roundabout"}, + "is_urban": true, + "admin_index": 0, + "out": 2, + "weight": 0.11, + "geometry_index": 38, + "location": [-77.029151, 38.910033] + }, + { + "lanes": [ + { + "indications": ["straight"], + "valid": false, + "active": false + }, + { + "indications": ["straight", "right"], + "valid_indication": "right", + "valid": true, + "active": true + } + ], + "location": [-77.029163, 38.910041], + "geometry_index": 39, + "admin_index": 0, + "weight": 18.86, + "is_urban": true, + "mapbox_streets_v8": {"class": "street"}, + "turn_duration": 0.1, + "turn_weight": 5, + "duration": 11.471, + "bearings": [131, 307, 350], + "out": 2, + "in": 0, + "entry": [false, true, true] + }, + { + "entry": [true, false, false, false], + "in": 2, + "bearings": [6, 115, 171, 296], + "duration": 24.736, + "turn_weight": 2, + "turn_duration": 0.02, + "mapbox_streets_v8": {"class": "street"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "weight": 32.24, + "geometry_index": 42, + "location": [-77.029203, 38.910237] + }, + { + "bearings": [25, 205, 296], + "entry": [true, false, true], + "in": 1, + "turn_weight": 1, + "turn_duration": 0.019, + "mapbox_streets_v8": {"class": "street"}, + "is_urban": true, + "admin_index": 0, + "out": 0, + "geometry_index": 49, + "location": [-77.029041, 38.910645] + } + ], + "maneuver": { + "type": "rotary", + "exit": 3, + "instruction": "Enter Logan Circle Northwest and take the 3rd exit onto Vermont Avenue Northwest.", + "modifier": "straight", + "bearing_after": 53, + "bearing_before": 52, + "location": [-77.029265, 38.909169] + }, + "rotary_name": "Logan Circle Northwest", + "name": "Vermont Avenue Northwest", + "duration": 68.49, + "distance": 207.6, + "driving_side": "right", + "weight": 94.397, + "mode": "driving", + "geometry": "anyeiA`xn|qCgA{BmAsBoAiBwAyAcBwA}A}@aFs@wAMs@Is@AiA@y@FYB_@DG@{@P_AVeA^oAr@gAr@cAv@{@r@}@|@_A`AOViGz@y@LcADqACsAKgAMkAQaAW_A_@sJ{EoKkF" + }, + { + "intersections": [ + { + "entry": [true, true, false], + "in": 2, + "bearings": [24, 114, 205], + "duration": 7.776, + "turn_weight": 82, + "turn_duration": 1.908, + "mapbox_streets_v8": {"class": "service"}, + "is_urban": true, + "admin_index": 0, + "out": 1, + "weight": 89.35, + "geometry_index": 50, + "location": [-77.028923, 38.910845] + }, + { + "bearings": [25, 115, 206, 294], + "entry": [false, true, false, false], + "in": 3, + "turn_weight": 2, + "turn_duration": 0.007, + "mapbox_streets_v8": {"class": "service"}, + "is_urban": true, + "admin_index": 0, + "out": 1, + "geometry_index": 51, + "location": [-77.02882, 38.910809] + } + ], + "maneuver": { + "type": "turn", + "instruction": "Turn right.", + "modifier": "right", + "bearing_after": 114, + "bearing_before": 25, + "location": [-77.028923, 38.910845] + }, + "name": "", + "duration": 36.663, + "distance": 57.913, + "driving_side": "right", + "weight": 126.727, + "mode": "driving", + "geometry": "yv|eiAtbn|qCfAmEzDkO~KA" + }, + { + "intersections": [ + { + "bearings": [0], + "entry": [true], + "in": 0, + "admin_index": 0, + "geometry_index": 53, + "location": [-77.028557, 38.910507] + } + ], + "maneuver": { + "type": "arrive", + "instruction": "Your destination is on the right.", + "modifier": "right", + "bearing_after": 0, + "bearing_before": 180, + "location": [-77.028557, 38.910507] + }, + "name": "", + "duration": 0, + "distance": 0, + "driving_side": "right", + "weight": 0, + "mode": "driving", + "geometry": "ua|eiAxkm|qC??" + } + ], + "distance": 405.4, + "summary": "13th Street Northwest, Logan Circle Northwest" + } + ], + "geometry": "yeweiAvno|qC}]DeC?oDcAaAYoF?_BOqAc@mAi@gAm@y@k@{AsAmAmBiA{BkAgCgA{BmAsBoAiBwAyAcBwA}A}@aFs@wAMs@Is@AiA@y@FYB_@DG@{@P_AVeA^oAr@gAr@cAv@{@r@}@|@_A`AOViGz@y@LcADqACsAKgAMkAQaAW_A_@sJ{EoKkFfAmEzDkO~KA" + } + ], + "waypoints": [ + { + "distance": 0.926, + "name": "13th Street Northwest", + "location": [-77.029628, 38.908013] + }, + {"distance": 4.657, "name": "", "location": [-77.028557, 38.910507]} + ], + "code": "Ok", + "uuid": "mK-PkEwk7pnJBTMJLrqe5BVATqWp2dpaLUwtDXn3UZ42CTXviDma1A==" +} \ No newline at end of file diff --git a/libtesting-router/gradle.properties b/libtesting-router/gradle.properties index 9fd49e17d9d..c732fe7192a 100644 --- a/libtesting-router/gradle.properties +++ b/libtesting-router/gradle.properties @@ -1,3 +1,2 @@ -POM_ARTIFACT_ID=test-router POM_ARTIFACT_TITLE=Mapbox Navigation Test Router POM_DESCRIPTION=Artifact that provides testing infrastructure to setup routes in test scenarios \ No newline at end of file diff --git a/libtrip-notification/gradle.properties b/libtrip-notification/gradle.properties index 0ad502757d5..46e5bf99058 100644 --- a/libtrip-notification/gradle.properties +++ b/libtrip-notification/gradle.properties @@ -1,3 +1,2 @@ -POM_ARTIFACT_ID=notification POM_ARTIFACT_TITLE=Mapbox Navigation Notification POM_DESCRIPTION=Artifact that provides the default implementation of the navigation notification diff --git a/ui-components/gradle.properties b/ui-components/gradle.properties index dcd15b9dd18..c2f5b287403 100644 --- a/ui-components/gradle.properties +++ b/ui-components/gradle.properties @@ -1,3 +1,2 @@ -POM_ARTIFACT_ID=ui-components POM_ARTIFACT_TITLE=Mapbox Navigation SDK POM_DESCRIPTION=Artifact that provides UI compoents \ No newline at end of file