2

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!

Faris
  • 23
  • 2

2 Answers2

1

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();
Moritz
  • 1,710
  • 1
  • 8
  • 13
  • 1
    Thanks so much for your help! The navigation now works, though it starts off zoomed waaay out to the world map level. I'm wondering if theres something I should change in my xml or within my code itself for that, but I'll try to figure it out myself. Thanks again! – Faris Nov 17 '20 at 19:39
0

Regarding the zoom:

I would recommend to set the zoom on the locationComponent as explained here: https://docs.mapbox.com/android/maps/overview/location-component/#cameramode

When instantiating the LocationComponent for the first time, the map's max/min zoom levels will be set toLocationComponentOptions#MAX_ZOOM_DEFAULT and LocationComponentOptions#MIN_ZOOM_DEFAULT respectively. Adjust the zoom range with the LocationComponentOptions#maxZoom() and LocationComponentOptions#minZoom() methods in the LocationComponentOptions class.

Moritz
  • 1,710
  • 1
  • 8
  • 13
  • I ended up using the other initializer for navigationviews where you pass in a cameraposition, but thank you! One last question: is there any way to make a text representation also appear on the navigationview? Rght now, it's just displaying the route on screen and using audio but no text regarding directions e.g. "right in 200ft on streetname". I've enabled bannerInstructions to true in the MapboxDirections builder but it has no effect on it -- any insight? – Faris Nov 19 '20 at 21:35
  • I believe this is described here: https://docs.mapbox.com/android/navigation/overview/manuever-instructions/#banner-instructions ```val bannerInstructionsListener = object: BannerInstructionsListener { override fun willDisplay(instructions: BannerInstructions?): BannerInstructions { return instructions!! } }``` – Moritz Nov 20 '20 at 09:07
  • Sorry to bother you again! I've tried to implement that in my NavigationViewOption's builder, as well as adding .bannerInstructions(true) and .steps(true) to the MapboxDirections builder, but it still doesn't seem to display anything. Specifically, my listener looks like: `public BannerInstructions willDisplay(BannerInstructions instructions) { return instructions; }` Any ideas on what I could be doing wrong? Your help, as always, is much appreciated! – Faris Nov 26 '20 at 21:07