Copilot
Note
This example is a part of the Navigation SDK Examples. You can find the values for all referenced resources in the res
directory. For example, see res/values/strings.xml
for R.string.*
references used in this example.
package com.mapbox.navigation.examples.preview.copilot import android.annotation.SuppressLintimport android.content.Contextimport android.os.Bundleimport android.view.Viewimport android.widget.Toastimport androidx.annotation.RawResimport androidx.appcompat.app.AppCompatActivityimport androidx.lifecycle.ViewModelProviderimport com.mapbox.api.directions.v5.models.DirectionsResponseimport com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPIimport com.mapbox.navigation.base.options.CopilotOptionsimport com.mapbox.navigation.base.options.NavigationOptionsimport com.mapbox.navigation.base.route.RouterOriginimport com.mapbox.navigation.base.route.toNavigationRoutesimport com.mapbox.navigation.copilot.HistoryPointimport com.mapbox.navigation.copilot.MapboxCopilotimport com.mapbox.navigation.copilot.SearchResultUsedimport com.mapbox.navigation.copilot.SearchResultUsedEventimport com.mapbox.navigation.copilot.SearchResultsimport com.mapbox.navigation.copilot.SearchResultsEventimport com.mapbox.navigation.core.DeveloperMetadataObserverimport com.mapbox.navigation.core.MapboxNavigationimport com.mapbox.navigation.core.lifecycle.MapboxNavigationAppimport com.mapbox.navigation.core.lifecycle.MapboxNavigationObserverimport com.mapbox.navigation.core.lifecycle.requireMapboxNavigationimport com.mapbox.navigation.core.telemetry.events.FeedbackEventimport com.mapbox.navigation.core.trip.session.NavigationSessionStateimport com.mapbox.navigation.core.trip.session.NavigationSessionStateObserverimport com.mapbox.navigation.examples.preview.Rimport com.mapbox.navigation.examples.preview.databinding.MapboxActivityCopilotBinding /*** The example demonstrates how to integrate and work with [MapboxCopilot].* See [CopilotViewModel] to learn about [MapboxCopilot]'s lifecycle and* when to [MapboxCopilot.start] / [MapboxCopilot.stop].** Copilot is a [MapboxNavigationObserver], so it's tied to the [MapboxNavigation] lifecycle automatically.* We recommended tracking the [DeveloperMetadata.copilotSessionId] (see [DeveloperMetadataObserver]) so that* Mapbox teams can better act on specific end-user feedback.* This ID helps Mapbox teams find the respective traces and troubleshoot issues faster.** **As the application developer, you are responsible for communicating to drivers about the data that is being* collected from their drives, including what kind of data is being collected and when it is collected.**** Nav SDK exposes configuration settings (see [NavigationOptions.copilotOptions]) to use Copilot in two ways:* 1) Automatic data collection:* - Enable Copilot for all trips performed by a specific driver (default option).* 2) Manual data collection:* - Copilot data is only sent when an end user submits negative feedback about a specific route to help* take action on the issue.* Data collection for Copilot is tightly coupled to the Navigation SDK Feedback, which means this is only effective* if the feedback events are pushed through [MapboxNavigation] Feedback APIs* (see [Feedback documentation](https://docs.mapbox.com/android/navigation/guides/feedback/) and* [MapboxNavigation.postUserFeedback] / [MapboxNavigation.provideFeedbackMetadataWrapper])** If you would like to provide Search analytics into Copilot, you can send the Search events over to* Copilot (see [MapboxCopilot.push]).* This information would include whether a routable point for navigation was available.* Copilot helps understand the impact of search results to a navigation session (arrival experience, routable points).** WARNING: Mapbox Copilot is currently in public-preview. Copilot-related entities and APIs are currently marked* as [ExperimentalPreviewMapboxNavigationAPI] and subject to change.* These markings will be removed when the feature is generally available.** Copilot is a library included in the Navigation SDK that Processes full-trip-trace longitude and* latitude data ("**Copilot**"). Copilot is turned off by default and optionally enabled by Customer at the* application developer level to improve feedback resolution. If Customer enables Copilot, Customer shall obtain* and maintain all necessary consents and permissions, including providing notice to and obtaining End* Users' affirmative express consent before any access or use of Copilot.** Before running the example make sure you have put your access_token in the correct place* inside [app-preview/src/main/res/values/mapbox_access_token.xml]. If not present then add* this file at the location mentioned above and add the following content to it** <?xml version="1.0" encoding="utf-8"?>* <resources xmlns:tools="http://schemas.android.com/tools">* <string name="mapbox_access_token"><PUT_YOUR_ACCESS_TOKEN_HERE></string>* </resources>** How to use the example:* - Start the example* - Look at how Navigation session state is Idle* - Click on Play button - starts Free Drive trip session* - Look at how Navigation session state changes to Free Drive and Copilot session ID is generated* - Click on Push Feedback button - pushes FeedbackEvent* - Click on Push Search button - pushes SearchResultsEvent and SearchResultUsedEvent* - Click on Set route button - session transitions to Active Guidance* - Look at how Navigation session state changes to Active Guidance and Copilot session ID is re-generated* - Click on Push Feedback button - pushes FeedbackEvent* - Click on Push Search button - pushes SearchResultsEvent and SearchResultUsedEvent* - Click on Stop button - session transitions to Free Drive* - Look at how Navigation session state changes to Active Guidance and Copilot session ID is re-generated* - Click on Stop button - session transitions to Idle* - Look at how Navigation session state changes to Idle and Copilot session ID empty*/@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)class CopilotActivity : AppCompatActivity() { private lateinit var binding: MapboxActivityCopilotBindingprivate lateinit var copilotViewModel: CopilotViewModelprivate var navigationSessionState: NavigationSessionState = NavigationSessionState.Idleprivate val navigationSessionStateObserver = NavigationSessionStateObserver {navigationSessionState = itval sessionStateText = "Navigation session state:"when (it) {is NavigationSessionState.Idle -> {binding.navigationSessionState.text ="$sessionStateText Idle"binding.startStopNavigation.setImageResource(R.drawable.ic_start)binding.setRoutes.visibility = View.GONEbinding.feedbackPush.visibility = View.GONEbinding.searchPush.visibility = View.GONE}is NavigationSessionState.FreeDrive -> {binding.navigationSessionState.text ="$sessionStateText Free Drive"binding.startStopNavigation.setImageResource(R.drawable.ic_stop)binding.setRoutes.visibility = View.VISIBLEbinding.feedbackPush.visibility = View.VISIBLEbinding.searchPush.visibility = View.VISIBLE}is NavigationSessionState.ActiveGuidance -> {binding.navigationSessionState.text ="$sessionStateText Active Guidance"binding.startStopNavigation.setImageResource(R.drawable.ic_stop)binding.setRoutes.visibility = View.GONEbinding.feedbackPush.visibility = View.VISIBLEbinding.searchPush.visibility = View.VISIBLE}}}private val developerMetadataObserver = DeveloperMetadataObserver {val copilotSessionIdText = "Copilot session ID:"binding.copilotSessionId.text = "$copilotSessionIdText ${it.copilotSessionId}"}private val mapboxNavigation by requireMapboxNavigation(onCreatedObserver = object : MapboxNavigationObserver {override fun onAttached(mapboxNavigation: MapboxNavigation) {mapboxNavigation.registerNavigationSessionStateObserver(navigationSessionStateObserver)mapboxNavigation.registerDeveloperMetadataObserver(developerMetadataObserver)} override fun onDetached(mapboxNavigation: MapboxNavigation) {mapboxNavigation.unregisterNavigationSessionStateObserver(navigationSessionStateObserver)mapboxNavigation.unregisterDeveloperMetadataObserver(developerMetadataObserver)}}) {MapboxNavigationApp.setup(NavigationOptions.Builder(this).accessToken(getString(R.string.mapbox_access_token)).copilotOptions(// Set shouldSendHistoryOnlyWithFeedback to true if you want to sent Copilot traces// only when an end user submits negative feedbackCopilotOptions.Builder().shouldSendHistoryOnlyWithFeedback(false).build()).build())}private lateinit var routeResponse: DirectionsResponse @SuppressLint("MissingPermission")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = MapboxActivityCopilotBinding.inflate(layoutInflater)setContentView(binding.root) copilotViewModel = ViewModelProvider(this)[CopilotViewModel::class.java]routeResponse =DirectionsResponse.fromJson(readRawFileText(this, R.raw.multiple_routes)) binding.startStopNavigation.setOnClickListener {when (navigationSessionState) {is NavigationSessionState.Idle -> {mapboxNavigation.setNavigationRoutes(emptyList())mapboxNavigation.startTripSession()}is NavigationSessionState.FreeDrive -> {mapboxNavigation.stopTripSession()}is NavigationSessionState.ActiveGuidance -> {mapboxNavigation.setNavigationRoutes(emptyList())}}} binding.setRoutes.setOnClickListener {mapboxNavigation.setNavigationRoutes(routeResponse.routes().toNavigationRoutes(RouterOrigin.Onboard),)} binding.feedbackPush.setOnClickListener {// Call postUserFeedback every time user submits feedbackmapboxNavigation.postUserFeedback(FeedbackEvent.POSITIONING_ISSUE,"Test feedback",FeedbackEvent.UI,"encoded_screenshot",)Toast.makeText(this, "Feedback event pushed!", Toast.LENGTH_SHORT).show()} binding.searchPush.setOnClickListener {// Push a SearchResultsEvent every time Search results response is retrievedMapboxCopilot.push(SearchResultsEvent(SearchResults("mapbox", "https://mapbox.com", null, null, "?query=test1", null)))// Push a SearchResultUsedEvent every time a Search result is selectedMapboxCopilot.push(SearchResultUsedEvent(SearchResultUsed("mapbox","test_id","mapbox_poi","mapbox_address",HistoryPoint(-77.03396910343713, 38.89992797324407),null,)))Toast.makeText(this, "Search events pushed!", Toast.LENGTH_SHORT).show()}} private fun readRawFileText(context: Context, @RawRes res: Int): String =context.resources.openRawResource(res).bufferedReader().use { it.readText() }}
package com.mapbox.navigation.examples.preview.copilot import androidx.lifecycle.ViewModelimport com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPIimport com.mapbox.navigation.copilot.MapboxCopilotimport com.mapbox.navigation.core.MapboxNavigationimport com.mapbox.navigation.core.lifecycle.MapboxNavigationObserver /*** [ViewModel] used in [CopilotActivity]. It handles [MapboxCopilot]'s lifecycle by* managing when to call [MapboxCopilot.start] / [MapboxCopilot.stop] APIs.** Copilot is a [MapboxNavigationObserver], so it's tied to the [MapboxNavigation] lifecycle automatically.** Copilot is an opt-in feature (see [MapboxCopilot.start] and [MapboxCopilot.stop]), which means you* have the choice to enable it for your users (drivers). Depending on the use case, you can enable Copilot* for either all drivers (for example, during a pilot) or a subset of them.** We recommend to tie [MapboxCopilot.start] and [MapboxCopilot.stop] APIs to app's lifecycle.** WARNING: Mapbox Copilot is currently in public-preview. Copilot-related entities and APIs are currently marked* as [ExperimentalPreviewMapboxNavigationAPI] and subject to change.* These markings will be removed when the feature is generally available.** Copilot is a library included in the Navigation SDK that Processes full-trip-trace longitude and* latitude data ("**Copilot**"). Copilot is turned off by default and optionally enabled by Customer at the* application developer level to improve feedback resolution. If Customer enables Copilot, Customer shall obtain* and maintain all necessary consents and permissions, including providing notice to and obtaining End* Users' affirmative express consent before any access or use of Copilot.*/@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)class CopilotViewModel : ViewModel() { init {MapboxCopilot.start()} override fun onCleared() {MapboxCopilot.stop()}}
<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_height="match_parent"android:layout_width="match_parent"> <TextViewandroid:id="@+id/navigationSessionState"android:layout_height="wrap_content"android:layout_width="wrap_content"tools:text="Navigation Session State: Idle"android:gravity="center"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /> <TextViewandroid:id="@+id/copilotSessionId"android:layout_height="wrap_content"android:layout_width="wrap_content"tools:text="Copilot session ID: e0b2dea9-7246-48e4-ac2e-bb6407535cc9"android:gravity="center"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toBottomOf="@+id/navigationSessionState" /> <com.google.android.material.floatingactionbutton.FloatingActionButtonandroid:id="@+id/startStopNavigation"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="8dp"android:layout_marginStart="16dp"app:layout_constraintStart_toStartOf="parent"app:layout_constraintBottom_toBottomOf="parent"app:srcCompat="@drawable/ic_start" /> <com.google.android.material.floatingactionbutton.FloatingActionButtonandroid:id="@+id/setRoutes"android:layout_width="wrap_content"android:layout_height="wrap_content"app:srcCompat="@drawable/ic_route"android:visibility="visible"android:layout_marginBottom="8dp"android:layout_marginStart="8dp"app:layout_constraintStart_toEndOf="@id/startStopNavigation"app:layout_constraintBottom_toBottomOf="parent" /> <com.google.android.material.floatingactionbutton.FloatingActionButtonandroid:id="@+id/feedbackPush"android:layout_width="wrap_content"android:layout_height="wrap_content"app:srcCompat="@drawable/ic_feedback"android:visibility="visible"android:layout_marginBottom="8dp"android:layout_marginStart="8dp"app:layout_constraintStart_toEndOf="@id/setRoutes"app:layout_constraintBottom_toBottomOf="parent" /> <com.google.android.material.floatingactionbutton.FloatingActionButtonandroid:id="@+id/searchPush"android:layout_width="wrap_content"android:layout_height="wrap_content"app:srcCompat="@drawable/ic_search"android:visibility="visible"android:layout_marginBottom="8dp"android:layout_marginStart="8dp"app:layout_constraintStart_toEndOf="@id/feedbackPush"app:layout_constraintBottom_toBottomOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>