• Optimization
  • Navigation
  • Tracking
  • Maps
  • Places

Launch Custom NavigationView

A complete turn-by-turn experience using custom NavigationView

This example shows:

  • How to fetch a route using RouteFetcher.getRoute with RouteRequestParams.Builder

  • How to initialize a NavigationView and bind it to the activity lifecycle

  • How to config NavEngineConfig.Builder and NavViewConfig.Builder in the onNavigationReady() callback

  • How to start navigation using navigationView.startNavigation

For all code examples, refer to Navigation Code Examples

activity_custom_navigation.xml view source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <Button
            android:id="@+id/fetchRoute"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/fetch_route"/>

        <Button
            android:id="@+id/startNav"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="15dp"
            android:text="@string/start_navigation"/>
    </LinearLayout>

    <TextView
        android:id="@+id/routeGeometry"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"/>

    <ProgressBar
        android:id="@+id/progress"
        android:visibility="gone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>

</LinearLayout>

activity_navigation_view.xml view source

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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_width="match_parent"
    android:layout_height="match_parent">
    <ai.nextbillion.navigation.ui.NavigationView
        android:id="@+id/navigation_view"
        app:navigationLightTheme="@style/Theme.CustomNavigationLight"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

CustomNavigationActivity view source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package ai.nextbillion.navigation.demo.activity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

import java.io.Serializable;
import java.util.List;

import ai.nextbillion.kits.directions.models.DirectionsResponse;
import ai.nextbillion.kits.directions.models.DirectionsRoute;
import ai.nextbillion.kits.directions.models.RouteRequestParams;
import ai.nextbillion.kits.geojson.Point;
import ai.nextbillion.navigation.core.routefetcher.RouteFetcher;
import ai.nextbillion.navigation.demo.R;
import androidx.appcompat.app.AppCompatActivity;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class CustomNavigationActivity extends AppCompatActivity implements View.OnClickListener {

    private Button fetchRoute;
    private Button startNav;
    private TextView routeGeometry;
    private DirectionsRoute directionsRoute;
    private List<DirectionsRoute> directionsRoutes;
    private ProgressBar progress;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_custom_navigation);
        fetchRoute = findViewById(R.id.fetchRoute);
        startNav = findViewById(R.id.startNav);
        routeGeometry = findViewById(R.id.routeGeometry);
        progress = findViewById(R.id.progress);
        fetchRoute.setOnClickListener(this);
        startNav.setOnClickListener(this);
        startNav.setEnabled(false);

    }

    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.fetchRoute) {
            progress.setVisibility(View.VISIBLE);
            Point origin = Point.fromLngLat(103.75986708439264, 1.312533169133601);
            Point destination = Point.fromLngLat(103.77982271935586, 1.310473772283314);

            RouteRequestParams.Builder builder = RouteRequestParams.builder()
                    .origin(origin)
                    .destination(destination)
                    .language("en")
                    .departureTime((int) (System.currentTimeMillis()/1000));

            RouteFetcher.getRoute(builder.build(), new Callback<DirectionsResponse>() {
                @Override
                public void onResponse(Call<DirectionsResponse> call, Response<DirectionsResponse> response) {
                    progress.setVisibility(View.GONE);
                    //start navigation with the route we just fetched.
                    if (response.body() != null && !response.body().routes().isEmpty()) {
                        directionsRoute = response.body().routes().get(0);
                        directionsRoutes = response.body().routes();
                        routeGeometry.setText(String.format("Route Geometry: %s", directionsRoute.geometry()));
                        startNav.setEnabled(true);
                    }
                }

                @Override
                public void onFailure(Call<DirectionsResponse> call, Throwable t) {
                    progress.setVisibility(View.GONE);
                }
            });
        } else if (view.getId() == R.id.startNav) {
            Intent intent = new Intent(this,  NavigationViewActivity.class);
            intent.putExtra("route", directionsRoute);
            intent.putExtra("routes", (Serializable) directionsRoutes);
            startActivity(intent);
        }
    }
}

NavigationViewActivity view source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package ai.nextbillion.navigation.demo.activity;

import android.Manifest;
import android.os.Build;
import android.os.Bundle;
import android.view.WindowInsets;

import java.util.List;

import ai.nextbillion.kits.directions.models.DirectionsRoute;
import ai.nextbillion.maps.location.modes.RenderMode;
import ai.nextbillion.navigation.core.navigation.NavEngineConfig;
import ai.nextbillion.navigation.demo.R;
import ai.nextbillion.navigation.ui.NavViewConfig;
import ai.nextbillion.navigation.ui.NavigationView;
import ai.nextbillion.navigation.ui.OnNavigationReadyCallback;
import ai.nextbillion.navigation.ui.listeners.NavigationListener;
import ai.nextbillion.navigation.ui.utils.StatusBarUtils;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import pub.devrel.easypermissions.EasyPermissions;
import pub.devrel.easypermissions.PermissionRequest;

public class NavigationViewActivity extends AppCompatActivity implements OnNavigationReadyCallback, EasyPermissions.PermissionCallbacks, NavigationListener, StatusBarUtils.OnWindowInsetsChange {

    NavigationView navigationView;

    private String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setTheme(ai.nextbillion.navigation.ui.R.style.Theme_AppCompat_NoActionBar);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_navigation_view);

        navigationView = findViewById(R.id.navigation_view);
        navigationView.onCreate(savedInstanceState);
        StatusBarUtils.transparentStatusBar(this,this);
        if (navigationView.isNightTheme()) {
            StatusBarUtils.setDarkMode(this);
        } else {
            StatusBarUtils.setLightMode(this);
        }

        requestPermissions();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }

    private void requestPermissions() {
        EasyPermissions.requestPermissions(new PermissionRequest.Builder(this, 200, permissions)
                .setRationale("Navigation requires location access")
                .setNegativeButtonText("No")
                .setPositiveButtonText("OK")
                .build());
    }

    @Override
    public void onNavigationReady(boolean b) {
        NavEngineConfig.Builder navConfig = NavEngineConfig.builder();
        NavViewConfig.Builder viewConfigBuilder = NavViewConfig.builder();
        viewConfigBuilder.navigationListener(this);

        configRoute(viewConfigBuilder);
        viewConfigBuilder.shouldSimulateRoute(true);
        viewConfigBuilder.showSpeedometer(true);
        viewConfigBuilder.locationLayerRenderMode(RenderMode.GPS);
        viewConfigBuilder.navConfig(navConfig.build());

        navigationView.startNavigation(viewConfigBuilder.build());
    }

    private void configRoute(NavViewConfig.Builder viewConfigBuilder) {
        DirectionsRoute route = (DirectionsRoute) getIntent().getSerializableExtra("route");
        List<DirectionsRoute> routes = (List<DirectionsRoute>) getIntent().getSerializableExtra("routes");
        viewConfigBuilder.route(route);
        viewConfigBuilder.routes(routes);
    }


    @Override
    public void onStart() {
        super.onStart();
        navigationView.onStart();
    }

    @Override
    public void onResume() {
        super.onResume();
        navigationView.onResume();
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        navigationView.onLowMemory();
    }

    @Override
    public void onBackPressed() {
        // If the navigation view didn't need to do anything, call super
        if (!navigationView.onBackPressed()) {
            super.onBackPressed();
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        navigationView.onSaveInstanceState(outState);
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        navigationView.onRestoreInstanceState(savedInstanceState);
    }

    @Override
    public void onPause() {
        super.onPause();
        navigationView.onPause();
    }

    @Override
    public void onStop() {
        super.onStop();
        navigationView.onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        navigationView.onDestroy();
    }

    @Override
    public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
        navigationView.initialize(this);

    }

    @Override
    public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {

    }

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

    @Override
    public void onNavigationFinished() {

    }

    @Override
    public void onNavigationRunning() {

    }

    @Override
    public void onApplyWindowInsets(WindowInsets windowInsets) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
            navigationView.fitSystemWindow(windowInsets);
        }
    }
}

Code summary

This code example shows how to customize the NavigationView and launch it

  • The code provided an AppCompatActivity called NavigationViewActivity that demonstrates the usage of the NavigationView class for navigation purposes.

  • The onCreate() method is overridden to set up the activity layout and initialize the NavigationView

    • StatusBarUtils.transparentStatusBar(this,this): sets the status bar to be transparent

    • StatusBarUtils.setLightMode/DarkMode(this); set the status bar text and icons to be displayed in light/dark color, making them visible against the light/dark background.

  • The requestPermissions() method is called to request location permissions.

    • The onRequestPermissionsResult() method handles the result of the permission request.

    • navigationView.initialize(this) was called after the location permission was granted. The onNavigationReady() method is implemented from the OnNavigationReadyCallback interface, which is called when the navigation is ready to start.

  • The configRoute() method is used to configure the navigation route by extracting route information from the intent extras.

    • Various configuration options are set using NavViewConfig.Builder, such as enabling route simulation, showing a speedometer, setting the location layer render mode, and providing the navigation configuration.

    • The startNavigation() method is called on the NavigationView instance to begin the navigation.

  • Lifecycle methods (onStart(), onResume(), onLowMemory(), etc.) are overridden to forward the calls to the NavigationView instance.

    • The onBackPressed() method is overridden to handle the back button press and allows the NavigationView to handle it if necessary.

    • Methods for handling instance state, permissions granted/denied, navigation callbacks, and window insets are implemented.

    • The activity's lifecycle methods (onSaveInstanceState(), onRestoreInstanceState(), etc.) are overridden to propagate the corresponding calls to the NavigationView.

    • the activity is destroyed by calling onDestroy() on the NavigationView.