Search code examples
androidmapboxmapbox-android

Mapbox NavigationView.startNavigation crash -- Index 0;Size 0


The title is about as straightforward as it gets, I've been trying to start navigation with the Navigation SDK UI component for Java. It generates the route (which I can guarantee is not-null) but seems to fall apart when I try to begin navigation. It begins on a button click from a separate activity that passes in 2 points as JSON.

Stacktrace:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.b31project.aanproject, PID: 8148
    java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
        at java.util.ArrayList.get(ArrayList.java:437)
        at com.mapbox.navigation.ui.route.MapRouteLine$MapRouteLineSupport.buildWayPointFeatureFromLeg(MapRouteLine.kt:1333)
        at com.mapbox.navigation.ui.route.MapRouteLine$MapRouteLineSupport.buildWayPointFeatureCollection(MapRouteLine.kt:1310)
        at com.mapbox.navigation.ui.route.MapRouteLine.drawWayPoints(MapRouteLine.kt:694)
        at com.mapbox.navigation.ui.route.MapRouteLine.reinitializeWithRoutes(MapRouteLine.kt:466)
        at com.mapbox.navigation.ui.route.MapRouteLine.draw(MapRouteLine.kt:430)
        at com.mapbox.navigation.ui.route.NavigationMapRoute.addRoutes(NavigationMapRoute.java:199)
        at com.mapbox.navigation.ui.route.NavigationMapRoute.addRoute(NavigationMapRoute.java:179)
        at com.mapbox.navigation.ui.map.NavigationMapboxMap.drawRoute(NavigationMapboxMap.java:556)
        at com.mapbox.navigation.ui.NavigationView.startNavigation(NavigationView.java:452)
        at com.b31project.aanproject.Navigation.onResponse(Navigation.java:112)
        at com.mapbox.api.directions.v5.MapboxDirections$1.onResponse(MapboxDirections.java:186)
        at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall$1.lambda$onResponse$0$DefaultCallAdapterFactory$ExecutorCallbackCall$1(DefaultCallAdapterFactory.java:81)
        at retrofit2.-$$Lambda$DefaultCallAdapterFactory$ExecutorCallbackCall$1$3wC8FyV4pyjrzrYL5U0mlYiviZw.run(Unknown Source:6)
        at android.os.Handler.handleCallback(Handler.java:789)
        at android.os.Handler.dispatchMessage(Handler.java:98)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6541)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

XML:

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:mapbox="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.mapbox.navigation.ui.NavigationView
        android:id="@+id/navigationView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        mapbox:mapbox_cameraZoom="1"
        />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

Navigation.java


package com.b31project.aanproject;

import android.content.Intent;
import android.graphics.Path;
import android.location.Location;
import android.os.Bundle;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.mapbox.android.core.location.LocationEngineProvider;
import com.mapbox.api.directions.v5.DirectionsCriteria;
import com.mapbox.api.directions.v5.MapboxDirections;
import com.mapbox.api.directions.v5.models.DirectionsResponse;
import com.mapbox.api.directions.v5.models.RouteOptions;
import com.mapbox.mapboxsdk.Mapbox;

import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.api.directions.v5.models.DirectionsRoute;
import com.mapbox.navigation.base.internal.route.RouteUrl;
import com.mapbox.navigation.base.options.NavigationOptions;
import com.mapbox.navigation.core.MapboxNavigation;
import com.mapbox.navigation.core.MapboxNavigationProvider;
import com.mapbox.navigation.ui.NavigationView;
import com.mapbox.navigation.ui.NavigationViewOptions;
import com.mapbox.navigation.ui.OnNavigationReadyCallback;
import com.mapbox.geojson.Point;
import com.mapbox.navigation.ui.listeners.NavigationListener;
import com.mapbox.navigation.ui.map.NavigationMapboxMap;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import timber.log.Timber;


public class Navigation extends AppCompatActivity implements OnNavigationReadyCallback,
        NavigationListener, Callback<DirectionsResponse> {
      private NavigationView navigationView;
      private NavigationMapboxMap navigationMapboxMap;
      private MapboxNavigation navigator;
      private Point origin;
      private Point destination;
      private DirectionsRoute currRoute;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Mapbox.getInstance(this, getString(R.string.mapbox_access_token));
        setContentView(R.layout.activity_navigation);
        setTheme(R.style.Theme_AppCompat_Light_NoActionBar);
        navigationView = findViewById(R.id.navigationView);
        Intent intent = getIntent();
        origin = Point.fromJson(intent.getStringExtra("origin"));
        destination = Point.fromJson(intent.getStringExtra("destination"));
        navigationView.initialize(this);
    }

    @Override
    public void onNavigationReady(boolean isRunning) {
        if(navigationView.retrieveMapboxNavigation() != null){
            navigator = navigationView.retrieveMapboxNavigation();
        }else{
            NavigationOptions n = MapboxNavigation.defaultNavigationOptionsBuilder(this, getString(R.string.mapbox_access_token)).build();
            navigator = new MapboxNavigation(n);
        }
        navigationMapboxMap = navigationView.retrieveNavigationMapboxMap();
        getRoute(origin,destination);
    }

    private void getRoute(Point origin, Point destination){
        MapboxDirections directions = MapboxDirections.builder()
                .accessToken(getString(R.string.mapbox_access_token))
                .origin(origin)
                .destination(destination)
                .overview(DirectionsCriteria.OVERVIEW_FULL)
                .profile(DirectionsCriteria.PROFILE_WALKING)
                .voiceInstructions(true)
                .build();
        directions.enqueueCall(this);
    }


    @Override
    public void onResponse(Call<DirectionsResponse> call, Response<DirectionsResponse> response) {
        if(response.body() == null){
            Timber.e("No route found");
            this.onNavigationFinished();
        }else if(response.body().routes().size() < 1){
            Timber.e("Response empty");
            this.onNavigationFinished();
        }else{
            currRoute = response.body().routes().get(0);
        }
        System.out.println(currRoute);
        NavigationViewOptions navigationViewOptions = NavigationViewOptions.builder(this)
                .navigationListener(this)
                .directionsRoute(currRoute)
                .build();
        navigationView.startNavigation(navigationViewOptions);
    }

    @Override
    public void onFailure(Call<DirectionsResponse> call, Throwable t) {
        Timber.e("Error: " + t.getMessage());
        this.onNavigationFinished();
    }

    @Override
    public void onCancelNavigation() {
        navigationView.stopNavigation();
        finish();
    }

    @Override
    public void onNavigationFinished() {
        finish();
    }

    @Override
    public void onNavigationRunning() {

    }
}

app level gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.2"
    ndkVersion "21.0.6113669"


    defaultConfig {
        applicationId "com.b31project.aanproject"
        minSdkVersion 26
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    compileOptions{
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    repositories{
        mavenCentral()
        maven{url 'https://mapbox.bintray.com/mapbox'}
    }
}

dependencies {
    implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0'
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    implementation 'com.mapbox.mapboxsdk:mapbox-sdk-services:5.7.0'
    implementation 'com.google.android.gms:play-services-location:17.1.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-places-v9:0.12.0'
    implementation 'com.mapbox.navigation:ui:1.1.0'
}

The error is right a the end of onResponse. Any help is appreciated!


Solution

  • The error you are facing seems to stem from not setting steps to true:

    Please change your MapboxDirections.builder() to:

    MapboxDirections directions = MapboxDirections.builder()
                    .accessToken(getString(R.string.mapbox_access_token))
                    .origin(origin)
                    .steps(true)
                    .destination(destination)
                    .overview(DirectionsCriteria.OVERVIEW_FULL)
                    .profile(DirectionsCriteria.PROFILE_WALKING)
                    .voiceInstructions(true)
                    .build();

    Telling from https://docs.mapbox.com/api/navigation/#retrieve-directions you need to set steps = true, otherwise voiceInstructions are not available. This probably leads to the problem.

    Second problem with your code:

    You need to specify a unitType for the voice instructions. Therefore add the unit type in your MapboxDirections.builder():

     MapboxDirections directions = MapboxDirections.builder()
                    .accessToken(getString(R.string.mapbox_access_token))
                    .origin(origin)
                    .steps(true)
                    .destination(destination)
                    .overview(DirectionsCriteria.OVERVIEW_FULL)
                    .profile(DirectionsCriteria.PROFILE_WALKING)
                    .voiceInstructions(true)
                    .voiceUnits(VoiceUnit.METRIC)
                    .build();