mapbox / mapbox-navigation-android-examples

Other
55 stars 46 forks source link

Unresolved reference:databinding #143

Open olgacarmona opened 2 years ago

olgacarmona commented 2 years ago

Hi,

I am trying to use the turn-by-turn navigation example, but I cannot resolve the reference:

import com.mapbox.navigation.examples.databinding.MapboxActivityTurnByTurnExperienceBinding

I am having a hard time just to fix this. Could anyone help me, please?

Here is gradle (project):

buildscript { ext.kotlin_version = '1.7.20-RC' repositories { google() mavenCentral()

}
dependencies {
    classpath "com.android.tools.build:gradle:7.0.4"
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0"
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
}

} allprojects { repositories { google() maven { url "http://oss.sonatype.org/content/repositories/snapshots/" } maven { url 'https://oss.jfrog.org/artifactory/oss-snapshot-local/' } maven { url 'https://api.mapbox.com/downloads/v2/releases/maven' authentication { basic(BasicAuthentication) } credentials { username = "mapbox" password = System.getenv("MAPBOX_DOWNLOADS_TOKEN") ?: project.property("MAPBOX_DOWNLOADS_TOKEN") as String } } } }

task clean(type: Delete) { delete rootProject.buildDir }


gradle (app)

plugins { id 'com.android.application'

} apply plugin: 'kotlin-android'

android { compileSdk 32

defaultConfig {
    applicationId "com.example.android.testenavegacao"
    minSdk 21
    targetSdk 32
    versionCode 1
    versionName "1.0"
    multiDexEnabled true

    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    vectorDrawables.useSupportLibrary = true
}

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}

buildFeatures {
    viewBinding true
}

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

sourceSets {
    main {
        def srcDirs = file("$projectDir/src/main/java/com/mapbox/navigation/examples")
                .listFiles({ it.isDirectory() } as FileFilter)
                .collect { it.name }

        res.srcDirs = ['src/main/res']
        srcDirs.forEach {
            res.srcDirs += 'src/main/java/com/mapbox/navigation/examples/' + it + '/res'
        }
    }
}

configurations {
    all*.exclude group: 'com.mapbox.mapboxsdk', module: 'mapbox-android-telemetry'
}

}

dependencies {

implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.5.0'
implementation 'androidx.core:core:1.8.0'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.appcompat:appcompat:1.5.0'
implementation 'com.google.firebase:firebase-crashlytics-buildtools:2.9.1'
implementation 'com.google.android.gms:play-services-maps:18.1.0'
implementation 'com.google.android.libraries.maps:maps:3.1.0-beta'
implementation 'com.google.firebase:firebase-config:21.1.1'
implementation 'androidx.databinding:databinding-adapters:7.2.2'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

implementation "androidx.core:core-ktx:1.8.0"
implementation "com.google.android.gms:play-services-location:20.0.0"
implementation "androidx.multidex:multidex:2.0.1"
implementation "com.squareup.leakcanary:leakcanary-android:2.8.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1"

implementation 'javax.annotation:javax.annotation-api:1.3.2'

implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.cardview:cardview:1.0.0'

implementation 'com.android.volley:volley:1.2.1'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.github.bumptech.glide:glide:3.7.0'

implementation 'com.mapbox.maps:android:10.7.0'
implementation 'com.mapbox.mapboxsdk:mapbox-android-core:5.0.2'
implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-locationlayer:0.11.0'

implementation 'com.mapbox.mapboxsdk:mapbox-sdk-services:6.7.0'
implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.7.0'

    / Mapbox Navigation Android Auto SDK Developer Preview /
implementation("com.mapbox.navigation:ui-androidauto:0.8.0")
implementation "com.mapbox.navigation:ui-dropin:2.7.0"
implementation "com.mapbox.navigation:android:2.7.0"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1'
implementation 'com.mapbox.mapboxsdk:mapbox-android-navigation-ui:0.42.6'
implementation "androidx.core:core-ktx:+"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.0.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

} repositories { mavenCentral() }


settings.gradle

dependencyResolutionManagement { dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) repositories { google() mavenCentral() jcenter() // Warning: this repository is going to shut down soon maven { url 'https://api.mapbox.com/downloads/v2/releases/maven' authentication { basic(BasicAuthentication) } credentials { // Do not change the username below. // This should always be mapbox (not your username). username = "mapbox" // Use the secret token you stored in gradle.properties as the password password = "pass-" } } } } } rootProject.name = "testenavegacao" include ':app'

ShaunRigby commented 2 years ago

I too have this issue. I must say, the documentation and examples are absolutely appaling.

mleong25 commented 1 year ago

@ShaunRigby @olgacarmona Not sure if you are both still tracking on this issue. My team and I ran into this issue as well but it was just a bit of unfamiliarity with Kotlin / Java. Still though, the docs should be much better than they are. I used this guide about databinding to successfully build the turn by turn into our application.

Hope this helps!

https://appdevnotes.com/android-databinding-tutorial-for-beginners-in-kotlin/

yuriiyell commented 1 year ago

@ShaunRigby @olgacarmona Not sure if you are both still tracking on this issue. My team and I ran into this issue as well but it was just a bit of unfamiliarity with Kotlin / Java. Still though, the docs should be much better than they are. I used this guide about databinding to successfully build the turn by turn into our application.

Hope this helps!

https://appdevnotes.com/android-databinding-tutorial-for-beginners-in-kotlin/

@mleong25 How were you able to implement using that. I tried using that but it just shows an empty white screen.

Here's our activity

import android.annotation.SuppressLint import android.content.res.Configuration import android.content.res.Resources import android.location.Location import android.os.Bundle import android.view.View import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import com.example.turnbyturnnavigationtesting.databinding.ActivityMainBinding import com.mapbox.api.directions.v5.models.Bearing import com.mapbox.api.directions.v5.models.RouteOptions import com.mapbox.bindgen.Expected import com.mapbox.geojson.Point import com.mapbox.maps.EdgeInsets import com.mapbox.maps.plugin.LocationPuck2D import com.mapbox.maps.plugin.animation.camera import com.mapbox.maps.plugin.gestures.gestures import com.mapbox.maps.plugin.locationcomponent.location import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI import com.mapbox.navigation.base.TimeFormat import com.mapbox.navigation.base.extensions.applyDefaultNavigationOptions import com.mapbox.navigation.base.extensions.applyLanguageAndVoiceUnitOptions import com.mapbox.navigation.base.formatter.DistanceFormatterOptions import com.mapbox.navigation.base.options.NavigationOptions import com.mapbox.navigation.base.route.NavigationRoute import com.mapbox.navigation.base.route.NavigationRouterCallback import com.mapbox.navigation.base.route.RouterFailure import com.mapbox.navigation.base.route.RouterOrigin import com.mapbox.navigation.core.MapboxNavigation import com.mapbox.navigation.core.directions.session.RoutesObserver import com.mapbox.navigation.core.formatter.MapboxDistanceFormatter import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp import com.mapbox.navigation.core.lifecycle.MapboxNavigationObserver import com.mapbox.navigation.core.lifecycle.requireMapboxNavigation import com.mapbox.navigation.core.replay.MapboxReplayer import com.mapbox.navigation.core.replay.ReplayLocationEngine import com.mapbox.navigation.core.replay.route.ReplayProgressObserver import com.mapbox.navigation.core.replay.route.ReplayRouteMapper import com.mapbox.navigation.core.trip.session.LocationMatcherResult import com.mapbox.navigation.core.trip.session.LocationObserver import com.mapbox.navigation.core.trip.session.RouteProgressObserver import com.mapbox.navigation.core.trip.session.VoiceInstructionsObserver import com.mapbox.navigation.ui.base.util.MapboxNavigationConsumer import com.mapbox.navigation.ui.maneuver.api.MapboxManeuverApi import com.mapbox.navigation.ui.maneuver.view.MapboxManeuverView import com.mapbox.navigation.ui.maps.NavigationStyles import com.mapbox.navigation.ui.maps.camera.NavigationCamera import com.mapbox.navigation.ui.maps.camera.data.MapboxNavigationViewportDataSource import com.mapbox.navigation.ui.maps.camera.lifecycle.NavigationBasicGesturesHandler import com.mapbox.navigation.ui.maps.camera.state.NavigationCameraState import com.mapbox.navigation.ui.maps.camera.transition.NavigationCameraTransitionOptions import com.mapbox.navigation.ui.maps.location.NavigationLocationProvider import com.mapbox.navigation.ui.maps.route.arrow.api.MapboxRouteArrowApi import com.mapbox.navigation.ui.maps.route.arrow.api.MapboxRouteArrowView import com.mapbox.navigation.ui.maps.route.arrow.model.RouteArrowOptions import com.mapbox.navigation.ui.maps.route.line.api.MapboxRouteLineApi import com.mapbox.navigation.ui.maps.route.line.api.MapboxRouteLineView import com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineOptions import com.mapbox.navigation.ui.tripprogress.api.MapboxTripProgressApi import com.mapbox.navigation.ui.tripprogress.model.DistanceRemainingFormatter import com.mapbox.navigation.ui.tripprogress.model.EstimatedTimeToArrivalFormatter import com.mapbox.navigation.ui.tripprogress.model.PercentDistanceTraveledFormatter import com.mapbox.navigation.ui.tripprogress.model.TimeRemainingFormatter import com.mapbox.navigation.ui.tripprogress.model.TripProgressUpdateFormatter import com.mapbox.navigation.ui.tripprogress.view.MapboxTripProgressView import com.mapbox.navigation.ui.voice.api.MapboxSpeechApi import com.mapbox.navigation.ui.voice.api.MapboxVoiceInstructionsPlayer import com.mapbox.navigation.ui.voice.model.SpeechAnnouncement import com.mapbox.navigation.ui.voice.model.SpeechError import com.mapbox.navigation.ui.voice.model.SpeechValue import com.mapbox.navigation.ui.voice.model.SpeechVolume import java.util.Date import java.util.Locale

class MainActivity : AppCompatActivity() { @OptIn(ExperimentalPreviewMapboxNavigationAPI::class) class TurnByTurnExperienceActivity : AppCompatActivity() {

    private companion object {
        private const val BUTTON_ANIMATION_DURATION = 1500L
    }

    /**
     * Debug tool used to play, pause and seek route progress events that can be used to produce mocked location updates along the route.
     */
    private val mapboxReplayer = MapboxReplayer()

    /**
     * Debug tool that mocks location updates with an input from the [mapboxReplayer].
     */
    private val replayLocationEngine = ReplayLocationEngine(mapboxReplayer)

    /**
     * Debug observer that makes sure the replayer has always an up-to-date information to generate mock updates.
     */
    private val replayProgressObserver = ReplayProgressObserver(mapboxReplayer)

    /**
     * Bindings to the example layout.
     */
    private lateinit var binding: ActivityMainBinding

    /**
     * Used to execute camera transitions based on the data generated by the [viewportDataSource].
     * This includes transitions from route overview to route following and continuously updating the camera as the location changes.
     */
    private lateinit var navigationCamera: NavigationCamera

    /**
     * Produces the camera frames based on the location and routing data for the [navigationCamera] to execute.
     */
    private lateinit var viewportDataSource: MapboxNavigationViewportDataSource

    /*
    * Below are generated camera padding values to ensure that the route fits well on screen while
    * other elements are overlaid on top of the map (including instruction view, buttons, etc.)
    */
    private val pixelDensity = Resources.getSystem().displayMetrics.density
    private val overviewPadding: EdgeInsets by lazy {
        EdgeInsets(
            140.0 * pixelDensity,
            40.0 * pixelDensity,
            120.0 * pixelDensity,
            40.0 * pixelDensity
        )
    }
    private val landscapeOverviewPadding: EdgeInsets by lazy {
        EdgeInsets(
            30.0 * pixelDensity,
            380.0 * pixelDensity,
            110.0 * pixelDensity,
            20.0 * pixelDensity
        )
    }
    private val followingPadding: EdgeInsets by lazy {
        EdgeInsets(
            180.0 * pixelDensity,
            40.0 * pixelDensity,
            150.0 * pixelDensity,
            40.0 * pixelDensity
        )
    }
    private val landscapeFollowingPadding: EdgeInsets by lazy {
        EdgeInsets(
            30.0 * pixelDensity,
            380.0 * pixelDensity,
            110.0 * pixelDensity,
            40.0 * pixelDensity
        )
    }

    /**
     * Generates updates for the [MapboxManeuverView] to display the upcoming maneuver instructions
     * and remaining distance to the maneuver point.
     */
    private lateinit var maneuverApi: MapboxManeuverApi

    /**
     * Generates updates for the [MapboxTripProgressView] that include remaining time and distance to the destination.
     */
    private lateinit var tripProgressApi: MapboxTripProgressApi

    /**
     * Generates updates for the [routeLineView] with the geometries and properties of the routes that should be drawn on the map.
     */
    private lateinit var routeLineApi: MapboxRouteLineApi

    /**
     * Draws route lines on the map based on the data from the [routeLineApi]
     */
    private lateinit var routeLineView: MapboxRouteLineView

    /**
     * Generates updates for the [routeArrowView] with the geometries and properties of maneuver arrows that should be drawn on the map.
     */
    private val routeArrowApi: MapboxRouteArrowApi = MapboxRouteArrowApi()

    /**
     * Draws maneuver arrows on the map based on the data [routeArrowApi].
     */
    private lateinit var routeArrowView: MapboxRouteArrowView

    /**
     * Stores and updates the state of whether the voice instructions should be played as they come or muted.
     */
    private var isVoiceInstructionsMuted = false
        set(value) {
            field = value
            if (value) {
                binding.soundButton.muteAndExtend(BUTTON_ANIMATION_DURATION)
                voiceInstructionsPlayer.volume(SpeechVolume(0f))
            } else {
                binding.soundButton.unmuteAndExtend(BUTTON_ANIMATION_DURATION)
                voiceInstructionsPlayer.volume(SpeechVolume(1f))
            }

            }

    /**
     * Extracts message that should be communicated to the driver about the upcoming maneuver.
     * When possible, downloads a synthesized audio file that can be played back to the driver.
     */
    private lateinit var speechApi: MapboxSpeechApi

    /**
     * Plays the synthesized audio files with upcoming maneuver instructions
     * or uses an on-device Text-To-Speech engine to communicate the message to the driver.
     * NOTE: do not use lazy initialization for this class since it takes some time to initialize
     * the system services required for on-device speech synthesis. With lazy initialization
     * there is a high risk that said services will not be available when the first instruction
     * has to be played. [MapboxVoiceInstructionsPlayer] should be instantiated in
     * Activity#onCreate.
     */
    private lateinit var voiceInstructionsPlayer: MapboxVoiceInstructionsPlayer

    /**
     * Observes when a new voice instruction should be played.
     */
    private val voiceInstructionsObserver = VoiceInstructionsObserver { voiceInstructions ->
        speechApi.generate(voiceInstructions, speechCallback)
    }

    /**
     * Based on whether the synthesized audio file is available, the callback plays the file
     * or uses the fall back which is played back using the on-device Text-To-Speech engine.
     */
    private val speechCallback =
        MapboxNavigationConsumer<Expected<SpeechError, SpeechValue>> { expected ->
            expected.fold(
                { error ->

// play the instruction via fallback text-to-speech engine voiceInstructionsPlayer.play( error.fallback, voiceInstructionsPlayerCallback ) }, { value -> // play the sound file from the external generator voiceInstructionsPlayer.play( value.announcement, voiceInstructionsPlayerCallback ) } ) }

    /**
     * When a synthesized audio file was downloaded, this callback cleans up the disk after it was played.
     */
    private val voiceInstructionsPlayerCallback =
        MapboxNavigationConsumer<SpeechAnnouncement> { value ->

// remove already consumed file to free-up space speechApi.clean(value) }

    /**
     * [NavigationLocationProvider] is a utility class that helps to provide location updates generated by the Navigation SDK
     * to the Maps SDK in order to update the user location indicator on the map.
     */
    private val navigationLocationProvider = NavigationLocationProvider()

    /**
     * Gets notified with location updates.
     *
     * Exposes raw updates coming directly from the location services
     * and the updates enhanced by the Navigation SDK (cleaned up and matched to the road).
     */
    private val locationObserver = object : LocationObserver {
        var firstLocationUpdateReceived = false

        override fun onNewRawLocation(rawLocation: Location) {

// not handled }

        override fun onNewLocationMatcherResult(locationMatcherResult: LocationMatcherResult) {
            val enhancedLocation = locationMatcherResult.enhancedLocation

// update location puck's position on the map navigationLocationProvider.changePosition( location = enhancedLocation, keyPoints = locationMatcherResult.keyPoints, )

// update camera position to account for new location viewportDataSource.onLocationChanged(enhancedLocation) viewportDataSource.evaluate()

// if this is the first location update the activity has received, // it's best to immediately move the camera to the current user location if (!firstLocationUpdateReceived) { firstLocationUpdateReceived = true navigationCamera.requestNavigationCameraToOverview( stateTransitionOptions = NavigationCameraTransitionOptions.Builder() .maxDuration(0) // instant transition .build() ) } } }

    /**
     * Gets notified with progress along the currently active route.
     */
    private val routeProgressObserver = RouteProgressObserver { routeProgress ->

// update the camera position to account for the progressed fragment of the route viewportDataSource.onRouteProgressChanged(routeProgress) viewportDataSource.evaluate()

// draw the upcoming maneuver arrow on the map val style = binding.mapView.getMapboxMap().getStyle() if (style != null) { val maneuverArrowResult = routeArrowApi.addUpcomingManeuverArrow(routeProgress) routeArrowView.renderManeuverUpdate(style, maneuverArrowResult) }

// update top banner with maneuver instructions val maneuvers = maneuverApi.getManeuvers(routeProgress) maneuvers.fold( { error -> Toast.makeText( this@TurnByTurnExperienceActivity, error.errorMessage, Toast.LENGTH_SHORT ).show() }, { binding.maneuverView.visibility = View.VISIBLE binding.maneuverView.renderManeuvers(maneuvers) } )

// update bottom trip progress summary binding.tripProgressView.render( tripProgressApi.getTripProgress(routeProgress) ) }

    /**
     * Gets notified whenever the tracked routes change.
     *
     * A change can mean:
     * - routes get changed with [MapboxNavigation.setRoutes]
     * - routes annotations get refreshed (for example, congestion annotation that indicate the live traffic along the route)
     * - driver got off route and a reroute was executed
     */
    private val routesObserver = RoutesObserver { routeUpdateResult ->
        if (routeUpdateResult.navigationRoutes.isNotEmpty()) {

// generate route geometries asynchronously and render them routeLineApi.setNavigationRoutes( routeUpdateResult.navigationRoutes ) { value -> binding.mapView.getMapboxMap().getStyle()?.apply { routeLineView.renderRouteDrawData(this, value) } }

// update the camera position to account for the new route viewportDataSource.onRouteChanged(routeUpdateResult.navigationRoutes.first()) viewportDataSource.evaluate() } else { // remove the route line and route arrow from the map val style = binding.mapView.getMapboxMap().getStyle() if (style != null) { routeLineApi.clearRouteLine { value -> routeLineView.renderClearRouteLineValue( style, value ) } routeArrowView.render(style, routeArrowApi.clearArrows()) }

// remove the route reference from camera position evaluations viewportDataSource.clearRouteData() viewportDataSource.evaluate() } }

    private val mapboxNavigation: MapboxNavigation by requireMapboxNavigation(
        onResumedObserver = object : MapboxNavigationObserver {
            @SuppressLint("MissingPermission")
            override fun onAttached(mapboxNavigation: MapboxNavigation) {
                mapboxNavigation.registerRoutesObserver(routesObserver)
                mapboxNavigation.registerLocationObserver(locationObserver)
                mapboxNavigation.registerRouteProgressObserver(routeProgressObserver)
                mapboxNavigation.registerRouteProgressObserver(replayProgressObserver)
                mapboxNavigation.registerVoiceInstructionsObserver(voiceInstructionsObserver)

// start the trip session to being receiving location updates in free drive // and later when a route is set also receiving route progress updates mapboxNavigation.startTripSession() }

            override fun onDetached(mapboxNavigation: MapboxNavigation) {
                mapboxNavigation.unregisterRoutesObserver(routesObserver)
                mapboxNavigation.unregisterLocationObserver(locationObserver)
                mapboxNavigation.unregisterRouteProgressObserver(routeProgressObserver)
                mapboxNavigation.unregisterRouteProgressObserver(replayProgressObserver)
                mapboxNavigation.unregisterVoiceInstructionsObserver(voiceInstructionsObserver)
            }
        },
        onInitialize = this::initNavigation
    )

    @SuppressLint("MissingPermission")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

// initialize Navigation Camera viewportDataSource = MapboxNavigationViewportDataSource(binding.mapView.getMapboxMap()) navigationCamera = NavigationCamera( binding.mapView.getMapboxMap(), binding.mapView.camera, viewportDataSource ) // set the animations lifecycle listener to ensure the NavigationCamera stops // automatically following the user location when the map is interacted with binding.mapView.camera.addCameraAnimationsLifecycleListener( NavigationBasicGesturesHandler(navigationCamera) ) navigationCamera.registerNavigationCameraStateChangeObserver { navigationCameraState -> // shows/hide the recenter button depending on the camera state when (navigationCameraState) { NavigationCameraState.TRANSITION_TO_FOLLOWING, NavigationCameraState.FOLLOWING -> binding.recenter.visibility = View.INVISIBLE NavigationCameraState.TRANSITION_TO_OVERVIEW, NavigationCameraState.OVERVIEW, NavigationCameraState.IDLE -> binding.recenter.visibility = View.VISIBLE } } // set the padding values depending on screen orientation and visible view layout if (this.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { viewportDataSource.overviewPadding = landscapeOverviewPadding } else { viewportDataSource.overviewPadding = overviewPadding } if (this.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { viewportDataSource.followingPadding = landscapeFollowingPadding } else { viewportDataSource.followingPadding = followingPadding }

// make sure to use the same DistanceFormatterOptions across different features val distanceFormatterOptions = DistanceFormatterOptions.Builder(this).build()

// initialize maneuver api that feeds the data to the top banner maneuver view maneuverApi = MapboxManeuverApi( MapboxDistanceFormatter(distanceFormatterOptions) )

// initialize bottom progress view tripProgressApi = MapboxTripProgressApi( TripProgressUpdateFormatter.Builder(this) .distanceRemainingFormatter( DistanceRemainingFormatter(distanceFormatterOptions) ) .timeRemainingFormatter( TimeRemainingFormatter(this) ) .percentRouteTraveledFormatter( PercentDistanceTraveledFormatter() ) .estimatedTimeToArrivalFormatter( EstimatedTimeToArrivalFormatter(this, TimeFormat.NONE_SPECIFIED) ) .build() )

// initialize voice instructions api and the voice instruction player speechApi = MapboxSpeechApi( this, getString(R.string.mapbox_access_token), Locale.US.language ) voiceInstructionsPlayer = MapboxVoiceInstructionsPlayer( this, getString(R.string.mapbox_access_token), Locale.US.language )

// initialize route line, the withRouteLineBelowLayerId is specified to place // the route line below road labels layer on the map // the value of this option will depend on the style that you are using // and under which layer the route line should be placed on the map layers stack val mapboxRouteLineOptions = MapboxRouteLineOptions.Builder(this) .withRouteLineBelowLayerId("road-label-navigation") .build() routeLineApi = MapboxRouteLineApi(mapboxRouteLineOptions) routeLineView = MapboxRouteLineView(mapboxRouteLineOptions)

// initialize maneuver arrow view to draw arrows on the map val routeArrowOptions = RouteArrowOptions.Builder(this).build() routeArrowView = MapboxRouteArrowView(routeArrowOptions)

// load map style binding.mapView.getMapboxMap().loadStyleUri(NavigationStyles.NAVIGATION_DAY_STYLE) { // add long click listener that search for a route to the clicked destination binding.mapView.gestures.addOnMapLongClickListener { point -> findRoute(point) true } }

// initialize view interactions binding.stop.setOnClickListener { clearRouteAndStopNavigation() } binding.recenter.setOnClickListener { navigationCamera.requestNavigationCameraToFollowing() binding.routeOverview.showTextAndExtend(BUTTON_ANIMATION_DURATION) } binding.routeOverview.setOnClickListener { navigationCamera.requestNavigationCameraToOverview() binding.recenter.showTextAndExtend(BUTTON_ANIMATION_DURATION) } binding.soundButton.setOnClickListener { // mute/unmute voice instructions isVoiceInstructionsMuted = !isVoiceInstructionsMuted }

// set initial sounds button state binding.soundButton.unmute() }

    override fun onDestroy() {
        super.onDestroy()
        mapboxReplayer.finish()
        maneuverApi.cancel()
        routeLineApi.cancel()
        routeLineView.cancel()
        speechApi.cancel()
        voiceInstructionsPlayer.shutdown()
    }

    private fun initNavigation() {
        MapboxNavigationApp.setup(
            NavigationOptions.Builder(this)
                .accessToken(getString(R.string.mapbox_access_token))

// comment out the location engine setting block to disable simulation .locationEngine(replayLocationEngine) .build() )

// initialize location puck binding.mapView.location.apply { setLocationProvider(navigationLocationProvider) this.locationPuck = LocationPuck2D( bearingImage = ContextCompat.getDrawable( applicationContext, R.drawable.mapbox_navigation_puck_icon ) ) enabled = true }

        replayOriginLocation()
    }

    private fun replayOriginLocation() {
        mapboxReplayer.pushEvents(
            listOf(
                ReplayRouteMapper.mapToUpdateLocation(
                    Date().time.toDouble(),
                    Point.fromLngLat(-122.39726512303575, 37.785128345296805)
                )
            )
        )
        mapboxReplayer.playFirstLocation()
        mapboxReplayer.playbackSpeed(3.0)
    }

    private fun findRoute(destination: Point) {
        val originLocation = navigationLocationProvider.lastLocation
        val originPoint = originLocation?.let {
            Point.fromLngLat(it.longitude, it.latitude)
        } ?: return

// execute a route request // it's recommended to use the // applyDefaultNavigationOptions and applyLanguageAndVoiceUnitOptions // that make sure the route request is optimized // to allow for support of all of the Navigation SDK features mapboxNavigation.requestRoutes( RouteOptions.builder() .applyDefaultNavigationOptions() .applyLanguageAndVoiceUnitOptions(this) .coordinatesList(listOf(originPoint, destination)) // provide the bearing for the origin of the request to ensure // that the returned route faces in the direction of the current user movement .bearingsList( listOf( Bearing.builder() .angle(originLocation.bearing.toDouble()) .degrees(45.0) .build(), null ) ) .layersList(listOf(mapboxNavigation.getZLevel(), null)) .build(), object : NavigationRouterCallback { override fun onCanceled( routeOptions: RouteOptions, routerOrigin: RouterOrigin ) { // no impl }

                override fun onFailure(
                    reasons: List<RouterFailure>,
                    routeOptions: RouteOptions
                ) {

// no impl }

                override fun onRoutesReady(
                    routes: List<NavigationRoute>,
                    routerOrigin: RouterOrigin
                ) {
                    setRouteAndStartNavigation(routes)
                }
            }
        )
    }

    private fun setRouteAndStartNavigation(routes: List<NavigationRoute>) {

// set routes, where the first route in the list is the primary route that // will be used for active guidance mapboxNavigation.setNavigationRoutes(routes)

// show UI elements binding.soundButton.visibility = View.VISIBLE binding.routeOverview.visibility = View.VISIBLE binding.tripProgressCard.visibility = View.VISIBLE

// move the camera to overview when new route is available navigationCamera.requestNavigationCameraToOverview() }

    private fun clearRouteAndStopNavigation() {

// clear mapboxNavigation.setNavigationRoutes(listOf())

// stop simulation mapboxReplayer.stop()

// hide UI elements binding.soundButton.visibility = View.INVISIBLE binding.maneuverView.visibility = View.INVISIBLE binding.routeOverview.visibility = View.INVISIBLE binding.tripProgressCard.visibility = View.INVISIBLE } } }

And this is our xml

<?xml version="1.0" encoding="utf-8"?> <layout 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"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent">

    <com.mapbox.maps.MapView
        android:id="@+id/mapView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.cardview.widget.CardView
        android:id="@+id/tripProgressCard"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:visibility="invisible"
        app:cardElevation="8dp"
        app:cardUseCompatPadding="false"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent">

        <com.mapbox.navigation.ui.tripprogress.view.MapboxTripProgressView
            android:id="@+id/tripProgressView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <ImageView
            android:id="@+id/stop"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_gravity="end|center_vertical"
            android:layout_marginEnd="12dp"
            app:srcCompat="@android:drawable/ic_delete" />
    </androidx.cardview.widget.CardView>

    <com.mapbox.navigation.ui.maneuver.view.MapboxManeuverView
        android:id="@+id/maneuverView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="4dp"
        android:visibility="invisible"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.mapbox.navigation.ui.voice.view.MapboxSoundButton
        android:id="@+id/soundButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="16dp"
        android:visibility="invisible"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/maneuverView" />

    <com.mapbox.navigation.ui.maps.camera.view.MapboxRouteOverviewButton
        android:id="@+id/routeOverview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="16dp"
        android:visibility="invisible"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/soundButton" />

    <com.mapbox.navigation.ui.maps.camera.view.MapboxRecenterButton
        android:id="@+id/recenter"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/routeOverview" />

</androidx.constraintlayout.widget.ConstraintLayout>