mapbox / mapbox-navigation-android

Mapbox Navigation SDK for Android
https://docs.mapbox.com/android/navigation/overview/
Other
622 stars 319 forks source link

Can’t start turn-by-turn navigation within custom NavigationViewActivity/NavigationViewFragment #1524

Closed JaninaMattes closed 5 years ago

JaninaMattes commented 6 years ago

Hey, I implemented the custom NavigationActivity following part of the example in the Mapbox documentation and can show the navigation UI as map so far but can't start the navigation itself nor show the user’s location. The same problem exists when implementing it via a NavigationFragment. How can I pass the missing information when I call my NavigationActivity/NavigationFragment from my MainActivity and start the navigation correctly? Thanks a lot for any help!

I create the directionsRoute and call navigationView.startNavigation(options) with NavigationViewOptions.builder() like this:

fun onNavigationReady(isRunning: Boolean) {
...
calculateRoute(origin, destination)
} 
private fun calculateRoute(origin: Point , destination: Point) {
       NavigationRoute.builder(this.context)
               .accessToken(Mapbox.getAccessToken()!!)
               .origin(origin)
               .destination(destination)
               .build()
               .getRoute( object : Callback<DirectionsResponse> {
                   // Send request to Direction API
                   override fun onFailure(call: Call<DirectionsResponse>? , t: Throwable?) {
                   }
                   override fun onResponse(call: Call<DirectionsResponse>? ,
                                           response: Response<DirectionsResponse>?) {
                       if (response?.body() == null || response.body()?.routes()?.size!! < 1) {
                           return
                       }
                       directionsRoute = response.body()!!.routes()[0]
                       startNavigation()
                   }
               })
   }

private fun startNavigation() {
       if (directionsRoute == null) return
       val options = NavigationViewOptions.builder()
               .directionsRoute(directionsRoute)
               .shouldSimulateRoute(true)
               .navigationListener(this)
               .build()

       navigationView.startNavigation(options)
   }
danesfeder commented 6 years ago

Hey @JaninaMattes 👋 I don't see anything immediately wrong with this code. Which version of the SDK are you using?

but can't start the navigation itself nor show the user’s location

Would can you post a screenshot or gif of the behavior you're seeing? This will help us debug, thanks!

JaninaMattes commented 6 years ago

Hey @danesfeder thank you for your fast replay! This is really helping me out. I am using the mapbox-android-sdk version 6.6.2. My build.gradle module file looks like the following:

android {
    compileSdkVersion 27
...

defaultConfig {
minSdkVersion 23
        targetSdkVersion 27
...
}
dependencies{
// mapbox sdk
    implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:6.6.2'
// navigation sdk
    implementation 'com.mapbox.mapboxsdk:mapbox-android-navigation:0.15.0'
    implementation('com.mapbox.mapboxsdk:mapbox-android-navigation-ui:0.15.0') {
        transitive = true
    }
...
}

I call my NavigationFragment from my MainActivty when the start_navigation button is hit by

private fun addNavigationFragment(navigationFragment: Fragment) {
        ...
        val transaction = supportFragmentManager.beginTransaction()
        transaction.disallowAddToBackStack()
        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
        transaction.replace(R.id.fragment_container, navigationFragment)
        transaction.commit()
    }

Currently I transfer additional location information via a Bundle argument from my MainActivty to my NavigationFragment to provide originPoint and destinationPoint. As I read in the NavigationView class this should be unnecessary?

NavigationViewFragment.newInstance(
                            originPoint.latitude(),
                            originPoint.longitude(),
                            destinationPoint.latitude(),
                            destinationPoint.longitude()) as Fragment)

The user location by now is retrieved via:

private fun retrieveArgs(){
        val args = Bundle()
        originPoint = Point.fromLngLat(args.getDouble("originpoint longitude"),
                                       args.getDouble("originpoint latitude"))
        destinationPoint = Point.fromLngLat(args.getDouble("destinationpoint latitude"),
                                            args.getDouble("destinationpoint latitude"))
    }

Here is a screenshot of the view I am seeing after I started the navigation. I can neither display the user location puck nor the directionRoute which was rendered in my MainActivity.

mapbox_navigationview
danesfeder commented 6 years ago

Hey @JaninaMattes 👋 it looks like the SDK version you're using is pretty dated (0.15.0). Would you mind updating to 0.23.0 and giving this another shot? Is there anything blocking you from updating to 0.23.0?

Btw, the only dependency you'll need is:

implementation 'com.mapbox.mapboxsdk:mapbox-android-navigation-ui:0.23.0'

The dependency will transitively bring in the Maps SDK and the core navigation libraries. This helps prevents conflicts. Thanks!

JaninaMattes commented 6 years ago

Hi @danesfeder, yes I tried to update all versions and run into an issue with gradle.build file which I couldn't solve yet. The error message refers to:

Failed to resolve: com.mapbox.navigator:mapbox-navigation-native:3.3.1 Show in Project Structure: {include=[*jar], dir=libs}

+--- com.mapbox.mapboxsdk:mapbox-android-navigation-ui:0.23.0 | +--- com.mapbox.mapboxsdk:mapbox-android-navigation:0.23.0 | | +--- com.mapbox.mapboxsdk:mapbox-android-telemetry:3.5.2 () | | +--- com.mapbox.mapboxsdk:mapbox-sdk-services:4.0.0 | | | +--- com.mapbox.mapboxsdk:mapbox-sdk-core:4.0.0 () | | | --- com.mapbox.mapboxsdk:mapbox-sdk-geojson:4.0.0 () | | +--- com.mapbox.mapboxsdk:mapbox-sdk-turf:4.0.0 () | | +--- com.mapbox.navigator:mapbox-navigation-native:3.3.1 FAILED | | +--- com.android.support:appcompat-v7:27.1.1 () | | --- com.jakewharton.timber:timber:4.7.1 ()

JaninaMattes commented 6 years ago

adding url 'https://mapbox.bintray.com/mapbox' to my build.script helped and solved the above mentioned problem.

allprojects {
    repositories {
        google()
        mavenCentral()
        jcenter()
        maven { url 'https://mapbox.bintray.com/mapbox' }
    }
}

Now I can actually see the user location and the route. But turn-by-turn navigation does not start @danesfeder in my NavigationFragment. The only error I get while running is:

`2018-11-13 15:28:03.704 3928-3928/com.app_map_box_map_nav E/Mbgl-NativeMapView: Exception in MapView.OnMapChangedListener
    java.lang.ArrayIndexOutOfBoundsException: length=0; index=-1
        at java.util.ArrayList.get(ArrayList.java:439)
        at com.mapbox.services.android.navigation.ui.v5.route.NavigationMapRoute.addDirectionWaypoints(NavigationMapRoute.java:453)
        at com.mapbox.services.android.navigation.ui.v5.route.NavigationMapRoute.onMapChanged(NavigationMapRoute.java:1019)
        at com.mapbox.mapboxsdk.maps.MapView$6.onMapChanged(MapView.java:334)
        at com.mapbox.mapboxsdk.maps.NativeMapView.onMapChanged(NativeMapView.java:917)
        at com.mapbox.mapboxsdk.maps.NativeMapView.onDidFinishLoadingStyle(NativeMapView.java:999)
        at android.os.MessageQueue.nativePollOnce(Native Method)
        at android.os.MessageQueue.next(MessageQueue.java:325)
        at android.os.Looper.loop(Looper.java:142)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
`

mapboxdemo

danesfeder commented 6 years ago

Hey @JaninaMattes yeah this was a good catch and a 🐛 on our part - we just merged #1530 to address this. You can test this now by compiling a SNAPSHOT of our SDK https://github.com/mapbox/mapbox-navigation-android#using-snapshots

The fix is available in that version (0.24.0-SNAPSHOT). If this fixes your issue, then I think we can go ahead and close here.

JaninaMattes commented 6 years ago

Hey @danesfeder 👍 thank you for the quick fix I added the SNAPSHOT of your SDK but I am running into the same error again. It seems like the new updates are not properly imported. I deleted my libraries, cleaned and invalidated the caches and restarted Android - it didn't help.

I added the dependency to my project root build.gradle file:

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ext.kotlin_version = '1.2.71'

    repositories {
        google()
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        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()
        jcenter()
        mavenCentral()
        maven { url 'https://mapbox.bintray.com/mapbox' }
        // Snapshot to use latest bug fixes
        maven { url "http://oss.sonatype.org/content/repositories/snapshots/" }
    }
}

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

And into my app build.gradle file:

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 27

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    defaultConfig {
        applicationId "com.bmwgroup.app_map_box_map_nav"
        minSdkVersion 23
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    // android exinterface
    implementation 'com.android.support:exifinterface:27.1.1'
    // mapbox sdk
    implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:6.7.0'
    // navigation sdk will transitively bring maps-sdk
    implementation 'com.mapbox.mapboxsdk:mapbox-android-navigation-ui:0.23.0'
    // real-time traffic plugin
    implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-traffic:0.6.0'
    // places plugin
    implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-places:0.4.0'
    // annotation plug
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    // use of latest bug fixes that havent been packaged and released yet
    implementation 'com.mapbox.mapboxsdk:mapbox-android-navigation:0.24.0-SNAPSHOT'
}

The code from NavigationViewFragment which I am calling from my MainActivity:

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.fragment_navigation, container, false)
        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        navigationView = view.findViewById(R.id.navigationView) as NavigationView
        navigationView.onCreate(savedInstanceState)
        val initialPosition = CameraPosition.Builder()
                .target(LatLng(originPoint.longitude(), originPoint.latitude()))
                .zoom(defaultRouteZoom)
                .build()
        navigationView.initialize(this, initialPosition)
    }

    override fun onStart() {
        super.onStart()
        navigationView.onStart()
    }

    override fun onResume() {
        super.onResume()
        navigationView.onResume()
    }

    override fun onPause() {
        super.onPause()
        navigationView.onPause()
    }

    override fun onStop() {
        super.onStop()
        navigationView.onStop()
    }

    override fun onLowMemory() {
        super.onLowMemory()
        navigationView.onLowMemory()
    }

    override fun onDestroyView() {
        super.onDestroyView()
        navigationView.onDestroy()
    }

    override fun onSaveInstanceState(outState : Bundle) {
        navigationView.onSaveInstanceState(outState)
        super.onSaveInstanceState(outState)
    }

    override fun onViewStateRestored(savedInstanceState : Bundle?) {
        super.onViewStateRestored(savedInstanceState)
        if (savedInstanceState != null) {
            navigationView.onRestoreInstanceState(savedInstanceState)
        }
    }

    override fun onNavigationReady(isRunning: Boolean) {
        val origin = Point.fromLngLat(originPoint.longitude() , originPoint.latitude())
        val destination = Point.fromLngLat(destinationPoint.longitude(), destinationPoint.latitude())
        calculateRoute(origin, destination)
    }

    override fun onCancelNavigation() {
        if (activity != null) activity!!.finish()
        stopNavigation()
    }

    override fun onNavigationFinished() {
        if (activity != null) activity!!.finish()
    }

    override fun onNavigationRunning() {
        // no-op
    }

    private fun calculateRoute(origin: Point , destination: Point) {
        NavigationRoute.builder(this.context)
                .accessToken(Mapbox.getAccessToken()!!)
                .origin(origin)
                .destination(destination)
                .build()
                .getRoute( object : Callback<DirectionsResponse> {

                    // Send request to Direction API
                    override fun onFailure(call: Call<DirectionsResponse>? , t: Throwable?) {
                    }

                    override fun onResponse(call: Call<DirectionsResponse>? ,
                                            response: Response<DirectionsResponse>?) {
                        if (response?.body() == null || response.body()?.routes()?.size!! < 1) {
                            return
                        }
                        directionsRoute = response.body()!!.routes()[0]
                        startNavigation()
                    }
                })
    }

    private fun startNavigation() {
        if (directionsRoute == null) return
        val options = NavigationViewOptions.builder()
                .directionsRoute(directionsRoute)
                .shouldSimulateRoute(simulateRoute)
                .navigationListener(this)
                .build()

        // start camera zooms to the beginning of the route
        navigationView.startCamera(directionsRoute)
        navigationView.startNavigation(options)
    }

    private fun stopNavigation() {
        val activity = activity as MainActivity
        if (activity != null && activity is MainActivity) {
            activity.showPlaceholderFragment()
            activity.showNavigationFab()
        }
    }

    override fun onProgressChange(location : Location? , routeProgress : RouteProgress?) {
        val currentState = routeProgress?.currentState()
        if(currentState != null && currentState == RouteProgressState.ROUTE_ARRIVED){
            // Arrived at the end of the leg!
        }
    }
2018-11-14 09:09:43.943 29035-29035/com.app_map_box_map_nav E/Mbgl-NativeMapView: Exception in MapView.OnMapChangedListener
    java.lang.ArrayIndexOutOfBoundsException: length=0; index=-1
        at java.util.ArrayList.get(ArrayList.java:413)
        at com.mapbox.services.android.navigation.ui.v5.route.NavigationMapRoute.addDirectionWaypoints(NavigationMapRoute.java:453)
        at com.mapbox.services.android.navigation.ui.v5.route.NavigationMapRoute.onMapChanged(NavigationMapRoute.java:1019)
        at com.mapbox.mapboxsdk.maps.MapView$6.onMapChanged(MapView.java:334)
        at com.mapbox.mapboxsdk.maps.NativeMapView.onMapChanged(NativeMapView.java:917)
        at com.mapbox.mapboxsdk.maps.NativeMapView.onDidFinishLoadingStyle(NativeMapView.java:999)
        at android.os.MessageQueue.nativePollOnce(Native Method)
        at android.os.MessageQueue.next(MessageQueue.java:323)
        at android.os.Looper.loop(Looper.java:136)
        at android.app.ActivityThread.main(ActivityThread.java:6119)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
danesfeder commented 5 years ago

Hey @JaninaMattes 👋 the ArrayIndexOutOfBoundsException you're seeing was fixed by https://github.com/mapbox/mapbox-navigation-android/pull/1530. Stay tuned for the upcoming release which should land this week. You can also test this fix with the SNAPSHOT. Thanks for your patience 👍

kbt77722 commented 3 years ago

Can we customize navigationlauncher.Startnavigation activity?

Adityasai1431 commented 2 years ago

Hey @kbt77722 did you implement turn-by-turn navigation? Please let me know, I can't complete mine. Please help me.