From 50bd79e7d74bb6c0b538afa185ed02483a9c49c1 Mon Sep 17 00:00:00 2001 From: Yury Kanetski Date: Tue, 23 May 2023 10:36:35 +0200 Subject: [PATCH] wip --- instrumentation-tests/build.gradle | 1 + .../core/TelemetryEventsTest.kt | 224 ++++++++++++++++++ .../utils/events/EventsAccumulatorRule.kt | 50 ++++ .../utils/events/domain/EventsDomain.kt | 99 ++++++++ .../utils/events/verify/EventsVerifyEx.kt | 37 +++ .../utils/http/EventsRequestHandle.kt | 14 ++ .../utils/location/MockLocationReplayer.kt | 4 + .../utils/location/TurfUtils.kt | 10 + .../navigation/core/MapboxNavigation.kt | 3 +- .../metrics/MapboxMetricsReporter.kt | 22 +- .../events/EventsServiceInterfacesManager.kt | 2 - .../metrics/events/TelemetryEventsProvider.kt | 7 +- .../navigator/internal/NavigatorLoader.kt | 40 +++- .../testing/ui/utils/coroutines/Adapters.kt | 22 ++ 14 files changed, 518 insertions(+), 17 deletions(-) create mode 100644 instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/core/TelemetryEventsTest.kt create mode 100644 instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/events/EventsAccumulatorRule.kt create mode 100644 instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/events/domain/EventsDomain.kt create mode 100644 instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/events/verify/EventsVerifyEx.kt create mode 100644 instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/http/EventsRequestHandle.kt create mode 100644 instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/location/TurfUtils.kt diff --git a/instrumentation-tests/build.gradle b/instrumentation-tests/build.gradle index 4a8022a5a90..d8159a0c699 100644 --- a/instrumentation-tests/build.gradle +++ b/instrumentation-tests/build.gradle @@ -45,6 +45,7 @@ dependencies { implementation project(':libnavigation-android') implementation project(':libnavui-dropin') implementation project(":libnavui-util") + implementation project(':libnavigator') // test androidTestImplementation project(':libtesting-ui') diff --git a/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/core/TelemetryEventsTest.kt b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/core/TelemetryEventsTest.kt new file mode 100644 index 00000000000..3898780121e --- /dev/null +++ b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/core/TelemetryEventsTest.kt @@ -0,0 +1,224 @@ +package com.mapbox.navigation.instrumentation_tests.core + +import android.content.Context +import android.location.Location +import androidx.test.platform.app.InstrumentationRegistry +import com.mapbox.bindgen.Value +import com.mapbox.common.SettingsServiceFactory +import com.mapbox.common.SettingsServiceStorageType +import com.mapbox.geojson.Point +import com.mapbox.navigation.base.options.DeviceProfile +import com.mapbox.navigation.base.options.NavigationOptions +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.telemetry.events.FeedbackEvent +import com.mapbox.navigation.instrumentation_tests.activity.EmptyTestActivity +import com.mapbox.navigation.instrumentation_tests.utils.events.EventsAccumulatorRule +import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventArrive +import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventBase +import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventDepart +import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventFeedback +import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventFreeDrive +import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventNavigationStateChanged +import com.mapbox.navigation.instrumentation_tests.utils.events.verify.verifyEvents +import com.mapbox.navigation.instrumentation_tests.utils.http.EventsRequestHandle +import com.mapbox.navigation.instrumentation_tests.utils.location.MockLocationReplayerRule +import com.mapbox.navigation.instrumentation_tests.utils.routes.MockRoute +import com.mapbox.navigation.instrumentation_tests.utils.routes.RoutesProvider +import com.mapbox.navigation.instrumentation_tests.utils.routes.RoutesProvider.toNavigationRoutes +import com.mapbox.navigation.testing.ui.BaseTest +import com.mapbox.navigation.testing.ui.utils.MapboxNavigationRule +import com.mapbox.navigation.testing.ui.utils.coroutines.passed +import com.mapbox.navigation.testing.ui.utils.coroutines.rawLocationUpdates +import com.mapbox.navigation.testing.ui.utils.coroutines.routeProgressUpdates +import com.mapbox.navigation.testing.ui.utils.coroutines.sdkTest +import com.mapbox.navigation.testing.ui.utils.getMapboxAccessTokenFromResources +import com.mapbox.navigation.testing.ui.utils.runOnMainSync +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.first +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class TelemetryEventsTest : BaseTest(EmptyTestActivity::class.java) { + + @get:Rule + val eventsAccumulatorRule = EventsAccumulatorRule( + getMapboxAccessTokenFromResources(InstrumentationRegistry.getInstrumentation().targetContext) + ) + + @get:Rule + val mockLocationReplayerRule = MockLocationReplayerRule(mockLocationUpdatesRule) + + @get:Rule + val mapboxNavigationRule = MapboxNavigationRule() + + private val coordinates = listOf( + Point.fromLngLat(-77.031991, 38.894721), + Point.fromLngLat(-77.031991, 38.895433), + Point.fromLngLat(-77.030923, 38.895433), + ) + + private lateinit var mapboxNavigation: MapboxNavigation + private val eventsRequestHandle = EventsRequestHandle() + + private companion object { + private const val LOG_CATEGORY = "TelemetryEventsTest" + + /* See mapbox_common_settings_internal.hpp in common repo */ + private const val TELEMETRY_EVENTS_BASE_URL_PROPERTY = + "com.mapbox.common.telemetry.internal.events_base_url_override" + + private fun createMapboxNavigation(context: Context): MapboxNavigation = + MapboxNavigationProvider.create( + NavigationOptions.Builder(context) + .accessToken(getMapboxAccessTokenFromResources(context)) + .deviceProfile( + DeviceProfile.Builder() + .customConfig( + """ + { + "features": { + "useTelemetryNavigationEvents": true + }, + "telemetry": { + "eventsPriority": "Immediate" + } + } + """.trimIndent() + ) + .build() + ) + .build() + ) + + /** + * Give the chance to send events: 5 attempts with 1 seconds delay + */ + private suspend fun waitForEventsBeSent(expectedEvents: Int, current: () -> Int) { + IntRange(0, 4).forEach { _ -> + if (expectedEvents == current()) { + return + } else { + delay(1_000) + } + } + } + + private fun List.prettyErrors(): String = joinToString(postfix = "\n") + } + + override fun setupMockLocation(): Location = mockLocationUpdatesRule.generateLocationUpdate { + latitude = coordinates[0].latitude() + longitude = coordinates[0].longitude() + } + + @Before + fun setup() { + runOnMainSync { + // substitute telemetry base url + SettingsServiceFactory.getInstance(SettingsServiceStorageType.NON_PERSISTENT).apply { + set(TELEMETRY_EVENTS_BASE_URL_PROPERTY, Value(mockWebServerRule.baseUrl)) + } + + mockWebServerRule.requestHandlers.add(eventsRequestHandle) + } + } + + @Test + fun freeDrivePlain() = sdkTest { + val dcShorRoute = RoutesProvider.dc_short_with_alternative(activity).apply { + setRouteAsOriginLocation() + } + mapboxNavigation = createMapboxNavigation(activity) + + mapboxNavigation.startTripSession() + mockLocationReplayerRule.playRoute(dcShorRoute.routeResponse.routes().first()) + mapboxNavigation.rawLocationUpdates().passed(50.0).first() + mapboxNavigation.stopTripSession() + + verifyResult( + EventFreeDrive(EventFreeDrive.Type.Start), + EventFreeDrive(EventFreeDrive.Type.Stop), + ) + } + + @Test + fun activeGuidancePlain() = sdkTest { + val dcShortRoute = RoutesProvider.dc_short_with_alternative(activity).apply { + setRouteAsOriginLocation() + } + mapboxNavigation = createMapboxNavigation(activity) + mockLocationReplayerRule.playbackSpeed(3.0) + + mapboxNavigation.setNavigationRoutes(dcShortRoute.toNavigationRoutes()) + mapboxNavigation.startTripSession() + mockLocationReplayerRule.playRoute(dcShortRoute.routeResponse.routes().first()) + mapboxNavigation.routeProgressUpdates().first { + it.currentState == RouteProgressState.COMPLETE + } + mapboxNavigation.stopTripSession() + + verifyResult( + EventNavigationStateChanged(EventNavigationStateChanged.State.NavStarted), + EventDepart(), + EventArrive(), + EventNavigationStateChanged(EventNavigationStateChanged.State.NavEnded), + ) + } + + @Test + fun freeDriveWithFeedback() = sdkTest(200_000) { + val dcShorRoute = RoutesProvider.dc_very_short_two_legs(activity) + mapboxNavigation = createMapboxNavigation(activity) + val feedbackType = "feedbackType" + val description = "description" + val screenshot = "screenshot" + val feedbackSubType = arrayOf("subType1", "subType2") + + mapboxNavigation.startTripSession() + mockLocationReplayerRule.playRoute(dcShorRoute.routeResponse.routes().first()) + mapboxNavigation.rawLocationUpdates().passed(20.0).first() + mapboxNavigation.postUserFeedback( + feedbackType = feedbackType, + description = description, + feedbackSource = "na", + screenshot = "screenshot", + feedbackSubType = feedbackSubType + ) + mapboxNavigation.rawLocationUpdates().passed(20.0).first() + mapboxNavigation.stopTripSession() + + verifyResult( + EventFreeDrive(EventFreeDrive.Type.Start), + EventFeedback( + driverMode = EventBase.DriverMode.FreeDrive, + feedbackType = feedbackType, + description = description, + feedbackSubType = feedbackSubType, + // TODO fixme when NN implement data_ref + screenshot = "c2NyZWVuc2hvdA==", + ), + EventFreeDrive(EventFreeDrive.Type.Stop), + ) + } + + private suspend fun verifyResult(vararg expected: EventBase) { + waitForEventsBeSent(expected.size) { eventsAccumulatorRule.events.size } + val result = expected.asList().verifyEvents(eventsAccumulatorRule.events) + + check(result.isEmpty()) { + result.prettyErrors() + } + } + + private fun MockRoute.setRouteAsOriginLocation() { + mockLocationUpdatesRule.pushLocationUpdate( + mockLocationUpdatesRule.generateLocationUpdate { + latitude = this@setRouteAsOriginLocation.routeWaypoints.first().latitude() + longitude = this@setRouteAsOriginLocation.routeWaypoints.first().longitude() + } + ) + } +} diff --git a/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/events/EventsAccumulatorRule.kt b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/events/EventsAccumulatorRule.kt new file mode 100644 index 00000000000..1e2de38968f --- /dev/null +++ b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/events/EventsAccumulatorRule.kt @@ -0,0 +1,50 @@ +package com.mapbox.navigation.instrumentation_tests.utils.events + +import com.mapbox.bindgen.Value +import com.mapbox.common.EventsServerOptions +import com.mapbox.common.EventsService +import com.mapbox.common.EventsServiceError +import com.mapbox.common.EventsServiceObserver +import com.mapbox.navigation.utils.internal.logE +import com.mapbox.navigator.Navigator +import org.junit.rules.TestWatcher +import org.junit.runner.Description + +class EventsAccumulatorRule( + mapboxToken: String, +) : TestWatcher() { + + private val eventService = EventsService.getOrCreate( + EventsServerOptions( + mapboxToken, Navigator.getUserAgentFragment(), null + ) + ) + + private val eventsServiceObserver = object : EventsServiceObserver { + override fun didEncounterError(error: EventsServiceError, events: Value) { + logE(LOG_CATEGORY) { + "Occurred error [$error] when send events: $events" + } + } + + override fun didSendEvents(events: Value) { + _events.addAll(events.contents as List) + } + } + + private val _events = mutableListOf() + val events: List get() = _events + + private companion object { + private const val LOG_CATEGORY = "EventsAccumulatorRule" + } + + override fun starting(description: Description?) { + _events.clear() + eventService.registerObserver(eventsServiceObserver) + } + + override fun finished(description: Description?) { + eventService.unregisterObserver(eventsServiceObserver) + } +} diff --git a/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/events/domain/EventsDomain.kt b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/events/domain/EventsDomain.kt new file mode 100644 index 00000000000..5a0ae6ceb53 --- /dev/null +++ b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/events/domain/EventsDomain.kt @@ -0,0 +1,99 @@ +package com.mapbox.navigation.instrumentation_tests.utils.events.domain + +internal abstract class EventBase( + driverMode: DriverMode, +) { + abstract val event: String + val eventVersion: Double = 2.4 + val driverMode: String = when (driverMode) { + DriverMode.FreeDrive -> "freeDrive" + DriverMode.ActiveGuidance -> "trip" + } + + enum class DriverMode { + FreeDrive, + ActiveGuidance, + } +} + +internal abstract class EventBaseActiveGuidance( + +) : EventBase(DriverMode.ActiveGuidance) { + +} + +internal class EventFeedback( + driverMode: DriverMode, + val feedbackType: String, + val description: String, + val feedbackSubType: Array, + val screenshot: String, +): EventBase(driverMode) { + override val event: String = "navigation.feedback" +} + +internal class EventFreeDrive( + eventType: Type, +) : EventBase(DriverMode.FreeDrive) { + override val event: String = "navigation.freeDrive" + val eventType: String = when (eventType) { + Type.Start -> "start" + Type.Stop -> "stop" + } + + enum class Type { + Start, + Stop, + } +} + +internal class EventNavigationStateChanged( + state: State, +) : EventBase(DriverMode.ActiveGuidance) { + override val event: String = "navigation.navigationStateChanged" + val state: String = when (state) { + State.NavStarted -> "navigation_started" + State.NavEnded -> "navigation_ended" + } + + enum class State { + NavStarted, + NavEnded, + } +} + +internal class EventDepart : EventBaseActiveGuidance() { + override val event: String = "navigation.depart" +} + +internal class EventArrive : EventBaseActiveGuidance() { + override val event: String = "navigation.arrive" +} + +/** + * "volumeLevel" -> {Value@20958} "33" +"driverMode" -> {Value@20960} "freeDrive" +"batteryPluggedIn" -> {Value@20962} "false" +"eventVersion" -> {Value@20964} "2.4" +"simulation" -> {Value@20966} "true" +"audioType" -> {Value@20968} "headphones" +"percentTimeInPortrait" -> {Value@20970} "100" +"operatingSystem" -> {Value@20972} "Android 11" +"driverModeId" -> {Value@20974} "6283cc3d-6262-4833-86f5-663581327e2f" +"connectivity" -> {Value@20976} "Unknown" +"driverModeStartTimestamp" -> {Value@20978} "2023-05-17T16:35:26.530Z" +"percentTimeInForeground" -> {Value@20980} "100" +"driverModeStartTimestampMonotime" -> {Value@20982} "526084002909708" +"event" -> {Value@20984} "navigation.freeDrive" +"lat" -> {Value@20986} "38.89514907122847" +"batteryLevel" -> {Value@20988} "100" +"lng" -> {Value@20990} "-77.03195362937024" +"created" -> {Value@20992} "2023-05-17T16:35:33.859Z" +"eventType" -> {Value@20994} "stop" +"version" -> {Value@20996} "2.4" +"sdkIdentifier" -> {Value@20998} "mapbox-navigation-android" +"createdMonotime" -> {Value@21000} "526091271247708" +"locationEngine" -> {Value@21002} "fused" +"screenBrightness" -> {Value@21004} "40" +"device" -> {Value@21006} "generic_x86 (x86; Android SDK built for x86)" + */ diff --git a/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/events/verify/EventsVerifyEx.kt b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/events/verify/EventsVerifyEx.kt new file mode 100644 index 00000000000..f49bbda3eb8 --- /dev/null +++ b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/events/verify/EventsVerifyEx.kt @@ -0,0 +1,37 @@ +package com.mapbox.navigation.instrumentation_tests.utils.events.verify + +import com.google.gson.Gson +import com.mapbox.bindgen.Value +import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventBase +import org.json.JSONObject + +internal fun EventBase.checkIfSubOf(value: Value): List { + val valueJson = JSONObject(value.toJson()) + val eventJson = JSONObject(Gson().toJson(this)) + + val nonValidValues = mutableListOf() + val subKeys = eventJson.keys() + subKeys.forEach { eventJsonKey -> + val eventData = eventJson.get(eventJsonKey) + val valueData = valueJson.get(eventJsonKey) + if (eventData != valueData) { + nonValidValues.add( + "Event [$event], key [$eventJsonKey] does not match data in `event` [$eventData] and `value` [$valueData]" + ) + } + } + return nonValidValues +} + +internal fun Collection.verifyEvents(events: List): List { + if (this.size != events.size) { + return listOf("Events lists must have same sizes: List is ${this.size}, " + + "List is ${events.size}") + } + + return this.foldIndexed(mutableListOf()) { index, accumulateList, eventBase -> + accumulateList.apply { + addAll(eventBase.checkIfSubOf(events[index])) + } + } +} diff --git a/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/http/EventsRequestHandle.kt b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/http/EventsRequestHandle.kt new file mode 100644 index 00000000000..9b3500d5d7f --- /dev/null +++ b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/http/EventsRequestHandle.kt @@ -0,0 +1,14 @@ +package com.mapbox.navigation.instrumentation_tests.utils.http + +import com.mapbox.navigation.testing.ui.http.MockRequestHandler +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.RecordedRequest + +class EventsRequestHandle : MockRequestHandler { + override fun handle(request: RecordedRequest): MockResponse? { + if (request.path!!.startsWith("/events/v2")) { + return MockResponse().setResponseCode(204) + } + return null + } +} diff --git a/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/location/MockLocationReplayer.kt b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/location/MockLocationReplayer.kt index 079cd072364..dd01336f4b2 100644 --- a/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/location/MockLocationReplayer.kt +++ b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/location/MockLocationReplayer.kt @@ -77,6 +77,10 @@ class MockLocationReplayerRule(mockLocationUpdatesRule: MockLocationUpdatesRule) clearEvents() } } + + fun playbackSpeed(scale: Double) { + mapboxReplayer?.playbackSpeed(scale) + } } fun Location.setUpLocation(event: ReplayEventUpdateLocation) { diff --git a/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/location/TurfUtils.kt b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/location/TurfUtils.kt new file mode 100644 index 00000000000..f1070a9b1ae --- /dev/null +++ b/instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/location/TurfUtils.kt @@ -0,0 +1,10 @@ +package com.mapbox.navigation.instrumentation_tests.utils.location + +import com.mapbox.geojson.Point +import com.mapbox.turf.TurfConstants +import com.mapbox.turf.TurfMeasurement + +internal operator fun Int.compareTo(pointsDiff: Pair): Int { + val (p1, p2) = pointsDiff + return this - TurfMeasurement.distance(p1, p2, TurfConstants.UNIT_METERS).toInt() +} 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 3ebcbd18394..b407b43d3ce 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 @@ -138,6 +138,7 @@ import com.mapbox.navigator.ConfigHandle import com.mapbox.navigator.ElectronicHorizonOptions import com.mapbox.navigator.FallbackVersionsObserver import com.mapbox.navigator.IncidentsOptions +import com.mapbox.navigator.Navigator import com.mapbox.navigator.NavigatorConfig import com.mapbox.navigator.PollingConfig import com.mapbox.navigator.RouterInterface @@ -551,7 +552,7 @@ class MapboxNavigation @VisibleForTesting internal constructor( MapboxMetricsReporter.init( navigationOptions.applicationContext, token, - USER_AGENT, + Navigator.getUserAgentFragment(), ) MapboxMetricsReporter.toggleLogging(navigationOptions.isDebugLoggingEnabled) } diff --git a/libnavigation-metrics/src/main/java/com/mapbox/navigation/metrics/MapboxMetricsReporter.kt b/libnavigation-metrics/src/main/java/com/mapbox/navigation/metrics/MapboxMetricsReporter.kt index 8fb55cda7f8..ed61fcef342 100644 --- a/libnavigation-metrics/src/main/java/com/mapbox/navigation/metrics/MapboxMetricsReporter.kt +++ b/libnavigation-metrics/src/main/java/com/mapbox/navigation/metrics/MapboxMetricsReporter.kt @@ -36,7 +36,7 @@ object MapboxMetricsReporter : MetricsReporter { private const val LOG_CATEGORY = "MapboxMetricsReporter" private val gson = Gson() -// private lateinit var eventsManager: EventsServiceInterfacesManager + private lateinit var eventsManager: EventsServiceInterfacesManager private lateinit var telemetryService: TelemetryService @Volatile @@ -86,10 +86,10 @@ object MapboxMetricsReporter : MetricsReporter { userAgent: String ) { isTelemetryInitialized = true -// eventsManager = TelemetryEventsProvider.getOrCreateTelemetryEventsManager(accessToken) + eventsManager = TelemetryEventsProvider.getOrCreateEventsServiceInterfacesManager(accessToken, userAgent) val eventsServerOptions = EventsServerOptions(accessToken, userAgent, null) telemetryService = TelemetryServiceProvider.provideTelemetryService(eventsServerOptions) -// eventsManager.nativeEventsServiceInterface.registerObserver(eventsServiceObserver) + eventsManager.nativeEventsServiceInterface.registerObserver(eventsServiceObserver) } /** @@ -111,7 +111,7 @@ object MapboxMetricsReporter : MetricsReporter { fun disable() { isTelemetryInitialized = false removeObserver() -// eventsManager.nativeEventsServiceInterface.unregisterObserver(eventsServiceObserver) + eventsManager.nativeEventsServiceInterface.unregisterObserver(eventsServiceObserver) ioJobController.job.cancelChildren() } @@ -127,13 +127,13 @@ object MapboxMetricsReporter : MetricsReporter { ) return } -// eventsManager.nativeEventsServiceInterface.sendEvent( -// Event(eventsPriority, metricEvent.toValue(), null) -// ) { -// if (it != null) { -// logE("Failed to send event ${metricEvent.metricName}: $it", LOG_CATEGORY) -// } -// } + eventsManager.nativeEventsServiceInterface.sendEvent( + Event(eventsPriority, metricEvent.toValue(), null) + ) { + if (it != null) { + logE("Failed to send event ${metricEvent.metricName}: $it", LOG_CATEGORY) + } + } ioJobController.scope.launch { metricsObserver?.onMetricUpdated(metricEvent.metricName, metricEvent.toJson(gson)) diff --git a/libnavigation-metrics/src/main/java/com/mapbox/navigation/metrics/events/EventsServiceInterfacesManager.kt b/libnavigation-metrics/src/main/java/com/mapbox/navigation/metrics/events/EventsServiceInterfacesManager.kt index 32bfec64033..d4818659d97 100644 --- a/libnavigation-metrics/src/main/java/com/mapbox/navigation/metrics/events/EventsServiceInterfacesManager.kt +++ b/libnavigation-metrics/src/main/java/com/mapbox/navigation/metrics/events/EventsServiceInterfacesManager.kt @@ -24,7 +24,6 @@ class EventsServiceInterfacesManager( override fun didSendEvents(events: Value) { observers.forEach { it.onEvents(events) } } - } fun registerEventsObserver(eventsObserver: EventsObserver) { @@ -49,4 +48,3 @@ class EventsServiceInterfacesManager( } } } - diff --git a/libnavigation-metrics/src/main/java/com/mapbox/navigation/metrics/events/TelemetryEventsProvider.kt b/libnavigation-metrics/src/main/java/com/mapbox/navigation/metrics/events/TelemetryEventsProvider.kt index faa10cb5731..c7507eeedef 100644 --- a/libnavigation-metrics/src/main/java/com/mapbox/navigation/metrics/events/TelemetryEventsProvider.kt +++ b/libnavigation-metrics/src/main/java/com/mapbox/navigation/metrics/events/TelemetryEventsProvider.kt @@ -4,10 +4,13 @@ import com.mapbox.common.EventsServerOptions import com.mapbox.common.EventsService object TelemetryEventsProvider { - fun getOrCreateEventsServiceInterfacesManager(accessToken: String): EventsServiceInterfacesManager = + fun getOrCreateEventsServiceInterfacesManager( + accessToken: String, + userAgent: String, + ): EventsServiceInterfacesManager = EventsServiceInterfacesManager( EventsService.getOrCreate( - EventsServerOptions(accessToken, "MapboxNavigationNative", null) + EventsServerOptions(accessToken, userAgent, null) ) ) } diff --git a/libnavigator/src/main/java/com/mapbox/navigation/navigator/internal/NavigatorLoader.kt b/libnavigator/src/main/java/com/mapbox/navigation/navigator/internal/NavigatorLoader.kt index aec20a8879f..02eccbd2e4b 100644 --- a/libnavigator/src/main/java/com/mapbox/navigation/navigator/internal/NavigatorLoader.kt +++ b/libnavigator/src/main/java/com/mapbox/navigation/navigator/internal/NavigatorLoader.kt @@ -20,6 +20,8 @@ import com.mapbox.navigator.RouterInterface import com.mapbox.navigator.RouterType import com.mapbox.navigator.SettingsProfile import com.mapbox.navigator.TilesConfig +import org.json.JSONException +import org.json.JSONObject /** * This class is expected to gain more responsibility as we define [customConfig]. @@ -34,7 +36,7 @@ object NavigatorLoader { ConfigFactory.build( settingsProfile(deviceProfile), navigatorConfig, - deviceProfile.customConfig, + enableNativeTelemetryEventsAndSwitchToImmediatePriority(deviceProfile.customConfig), ) fun createHistoryRecorderHandles( @@ -152,4 +154,40 @@ object NavigatorLoader { val router: RouterInterface, val routeAlternativesController: RouteAlternativesControllerInterface, ) + + private fun enableNativeTelemetryEventsAndSwitchToImmediatePriority(config: String): String { + val rootJsonObj = if (config.isNotBlank()) { + try { + JSONObject(config) + } catch (e: JSONException) { + logE(msg = "custom config json does not valid: $e") + logE(msg = "custom config: [$config]") + JSONObject() + } + } else { + JSONObject() + } + + val featuresJsonObj = if (rootJsonObj.has("features")) { + rootJsonObj.getJSONObject("features") + } else { + JSONObject().also { + rootJsonObj.put("features", it) + } + } + + featuresJsonObj.put("useTelemetryNavigationEvents", true) + + val telemetryJsonObj = if (rootJsonObj.has("telemetry")) { + rootJsonObj.getJSONObject("telemetry") + } else { + JSONObject().also { + rootJsonObj.put("telemetry", it) + } + } + + telemetryJsonObj.put("eventsPriority", "Immediate") + + return rootJsonObj.toString() + } } diff --git a/libtesting-ui/src/main/java/com/mapbox/navigation/testing/ui/utils/coroutines/Adapters.kt b/libtesting-ui/src/main/java/com/mapbox/navigation/testing/ui/utils/coroutines/Adapters.kt index 21e94c282ae..744c74a50a0 100644 --- a/libtesting-ui/src/main/java/com/mapbox/navigation/testing/ui/utils/coroutines/Adapters.kt +++ b/libtesting-ui/src/main/java/com/mapbox/navigation/testing/ui/utils/coroutines/Adapters.kt @@ -8,6 +8,7 @@ import com.mapbox.api.directions.v5.models.BannerInstructions import com.mapbox.api.directions.v5.models.RouteOptions import com.mapbox.api.directions.v5.models.VoiceInstructions import com.mapbox.bindgen.Expected +import com.mapbox.geojson.Point import com.mapbox.maps.MapboxMap import com.mapbox.maps.Style import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI @@ -41,10 +42,16 @@ import com.mapbox.navigation.ui.maps.route.line.api.RoutesRenderedResult import com.mapbox.navigation.ui.maps.route.line.model.RouteLineClearValue import com.mapbox.navigation.ui.maps.route.line.model.RouteLineError import com.mapbox.navigation.ui.maps.route.line.model.RouteSetValue +import com.mapbox.navigation.utils.internal.toPoint +import com.mapbox.turf.TurfConstants +import com.mapbox.turf.TurfMeasurement import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.runningFold +import kotlinx.coroutines.flow.sample import kotlinx.coroutines.suspendCancellableCoroutine import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine @@ -115,6 +122,21 @@ fun MapboxNavigation.rawLocationUpdates(): Flow { ) } +fun Flow.passed(meters: Double): Flow { + return runningFold(mutableListOf()) { acc, location -> + acc.apply { + add(location.toPoint()) + } + } + .sample(periodMillis = 500) + .map { points -> + TurfMeasurement.length(points, TurfConstants.UNIT_METERS) + } + .map { passedDistance -> + meters <= passedDistance + } +} + sealed interface NavigationRouteAlternativesResult { data class OnRouteAlternatives( val routeProgress: RouteProgress,