Your app can listen to events that occur as the user navigates along a route.

## Overview

Use the following interfaces to listen for navigation events:

- [`Navigator.ArrivalListener`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator.ArrivalListener) provides the `onArrival()` callback, triggered when the device arrives at a destination.
- [`RoadSnappedLocationProvider.LocationListener`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/RoadSnappedLocationProvider.LocationListener) provides the `onLocationChanged()` callback, triggered when the device's location changes.
- [`Navigator.RemainingTimeOrDistanceChangedListener`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator.RemainingTimeOrDistanceChangedListener) provides the `onRemainingTimeOrDistanceChanged()` callback, triggered when the time or distance to the next destination changes by more than a given amount.
- [`Navigator.RouteChangedListener`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator.RouteChangedListener) provides the `onRouteChanged()` callback, triggered when the route changes.

## See the code

Show/Hide the Java code
for the navigation activity.

```
package com.example.navsdkmultidestination;

import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.google.android.gms.maps.GoogleMap.CameraPerspective;
import com.google.android.libraries.navigation.ArrivalEvent;
import com.google.android.libraries.navigation.AudioGuidanceSettings;
import com.google.android.libraries.navigation.ListenableResultFuture;
import com.google.android.libraries.navigation.NavigationApi;
import com.google.android.libraries.navigation.NavigationFragment;
import com.google.android.libraries.navigation.Navigator;
import com.google.android.libraries.navigation.RoadSnappedLocationProvider;
import com.google.android.libraries.navigation.SimulationOptions;
import com.google.android.libraries.navigation.TimeAndDistance;
import com.google.android.libraries.navigation.Waypoint;
import java.util.ArrayList;
import java.util.List;

/**
 * An activity that displays a map and a navigation UI, guiding the user from their current location
 * to multiple destinations, also known as waypoints.
 */
public class NavigationActivityMultiDestination extends AppCompatActivity {

  private static final String TAG = NavigationActivityMultiDestination.class.getSimpleName();
  private static final String DISPLAY_BOTH = "both";
  private static final String DISPLAY_TOAST = "toast";
  private static final String DISPLAY_LOG = "log";

  private Navigator mNavigator;
  private RoadSnappedLocationProvider mRoadSnappedLocationProvider;
  private NavigationFragment mNavFragment;
  private final List<Waypoint> mWaypoints = new ArrayList<>();

  private Navigator.ArrivalListener mArrivalListener;
  private Navigator.RouteChangedListener mRouteChangedListener;
  private Navigator.RemainingTimeOrDistanceChangedListener mRemainingTimeOrDistanceChangedListener;
  private RoadSnappedLocationProvider.LocationListener mLocationListener;

  private Bundle mSavedInstanceState;
  private static final String KEY_JOURNEY_IN_PROGRESS = "journey_in_progress";
  private boolean mJourneyInProgress = false;

  // Set fields for requesting location permission.
  private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1;
  private boolean mLocationPermissionGranted;

  /**
   * Sets up the navigator when the activity is created.
   *
   * @param savedInstanceState The activity state bundle.
   */
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Save the navigator state, used to determine whether a journey is in progress.
    mSavedInstanceState = savedInstanceState;
    if (mSavedInstanceState != null && mSavedInstanceState.containsKey(KEY_JOURNEY_IN_PROGRESS)) {
      mJourneyInProgress = (mSavedInstanceState.getInt(KEY_JOURNEY_IN_PROGRESS) != 0);
    }

    setContentView(R.layout.activity_main);

    // Initialize the Navigation SDK.
    initializeNavigationSdk();
  }

  /** Releases navigation listeners when the activity is destroyed. */
  @Override
  protected void onDestroy() {
    super.onDestroy();

    if ((mJourneyInProgress) && (this.isFinishing())) {
      mNavigator.removeArrivalListener(mArrivalListener);
      mNavigator.removeRouteChangedListener(mRouteChangedListener);
      mNavigator.removeRemainingTimeOrDistanceChangedListener(
          mRemainingTimeOrDistanceChangedListener);
      if (mRoadSnappedLocationProvider != null) {
        mRoadSnappedLocationProvider.removeLocationListener(mLocationListener);
      }
      displayMessage("OnDestroy: Released navigation listeners.", DISPLAY_LOG);
    }
  }

  /** Saves the state of the app when the activity is paused. */
  @Override
  protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if (mJourneyInProgress) {
      outState.putInt(KEY_JOURNEY_IN_PROGRESS, 1);
    } else {
      outState.putInt(KEY_JOURNEY_IN_PROGRESS, 0);
    }
  }

  /**
   * Starts the Navigation SDK and sets the camera to follow the device's location. Calls the
   * navigateToPlaces() method when the navigator is ready.
   */
  private void initializeNavigationSdk() {
    /*
     * Request location permission, so that we can get the location of the
     * device. The result of the permission request is handled by a callback,
     * onRequestPermissionsResult.
     */
    if (ContextCompat.checkSelfPermission(
            this.getApplicationContext(), android.Manifest.permission.ACCESS_FINE_LOCATION)
        == PackageManager.PERMISSION_GRANTED) {
      mLocationPermissionGranted = true;
    } else {
      ActivityCompat.requestPermissions(
          this,
          new String[] {android.Manifest.permission.ACCESS_FINE_LOCATION},
          PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
    }

    if (!mLocationPermissionGranted) {
      displayMessage(
          "Error loading Navigation SDK: " + "The user has not granted location permission.",
          DISPLAY_BOTH);
      return;
    }

    // Get a navigator.
    NavigationApi.getNavigator(
        this,
        new NavigationApi.NavigatorListener() {
          /** Sets up the navigation UI when the navigator is ready for use. */
          @Override
          public void onNavigatorReady(Navigator navigator) {
            displayMessage("Navigator ready.", DISPLAY_BOTH);
            mNavigator = navigator;

            mNavFragment =
                (NavigationFragment)
                    getSupportFragmentManager().findFragmentById(R.id.navigation_fragment);

            // Set the camera to follow the device location with 'TILTED' driving view.
            mNavFragment.getMapAsync(
                googleMap -> googleMap.followMyLocation(CameraPerspective.TILTED));

            // Navigate to the specified places.
            navigateToPlaces();
          }

          /**
           * Handles errors from the Navigation SDK.
           *
           * @param errorCode The error code returned by the navigator.
           */
          @Override
          public void onError(@NavigationApi.ErrorCode int errorCode) {
            switch (errorCode) {
              case NavigationApi.ErrorCode.NOT_AUTHORIZED:
                displayMessage(
                    "Error loading Navigation SDK: Your API key is "
                        + "invalid or not authorized to use the Navigation SDK.",
                    DISPLAY_BOTH);
                break;
              case NavigationApi.ErrorCode.TERMS_NOT_ACCEPTED:
                displayMessage(
                    "Error loading Navigation SDK: User did not accept "
                        + "the Navigation Terms of Use.",
                    DISPLAY_BOTH);
                break;
              case NavigationApi.ErrorCode.NETWORK_ERROR:
                displayMessage("Error loading Navigation SDK: Network error.");
                break;
              case NavigationApi.ErrorCode.LOCATION_PERMISSION_MISSING:
                displayMessage(
                    "Error loading Navigation SDK: Location permission " + "is missing.",
                    DISPLAY_BOTH);
                break;
              default:
                displayMessage("Error loading Navigation SDK: " + errorCode, DISPLAY_BOTH);
            }
          }
        });
  }

  /** Requests directions from the user's current location to a list of waypoints. */
  private void navigateToPlaces() {

    // Set up a waypoint for each place that we want to go to.
    createWaypoint("ChIJq6qq6jauEmsRJAf7FjrKnXI", "Sydney Star");
    createWaypoint("ChIJ3S-JXmauEmsRUcIaWtf4MzE", "Sydney Opera House");
    createWaypoint("ChIJLwgLFGmuEmsRzpDhHQuyyoU", "Sydney Conservatorium of Music");

    // If this journey is already in progress, no need to restart navigation.
    // This can happen when the user rotates the device, or sends the app to the background.
    if (mSavedInstanceState != null
        && mSavedInstanceState.containsKey(KEY_JOURNEY_IN_PROGRESS)
        && mSavedInstanceState.getInt(KEY_JOURNEY_IN_PROGRESS) == 1) {
      return;
    }

    // Create a future to await the result of the asynchronous navigator task.
    ListenableResultFuture<Navigator.RouteStatus> pendingRoute =
        mNavigator.setDestinations(mWaypoints);

    // Define the action to perform when the SDK has determined the route.
    pendingRoute.setOnResultListener(
        new ListenableResultFuture.OnResultListener<Navigator.RouteStatus>() {
          @Override
          public void onResult(Navigator.RouteStatus code) {
            switch (code) {
              case OK:
                mJourneyInProgress = true;
                // Hide the toolbar to maximize the navigation UI.
                if (getActionBar() != null) {
                  getActionBar().hide();
                }

                // Register some listeners for navigation events.
                registerNavigationListeners();

                // Display the time and distance to each waypoint.
                displayTimesAndDistances();

                // Enable voice audio guidance (through the device speaker).
                AudioGuidanceSettings audioSettings =
                    AudioGuidanceSettings.builder()
                        .setGuidanceMode(
                            AudioGuidanceSettings.GuidanceMode.VOICE_ALERTS_AND_GUIDANCE)
                        .build();
                mNavigator.setAudioGuidanceSettings(audioSettings);

                // Simulate vehicle progress along the route for demo/debug builds.
                if (BuildConfig.DEBUG) {
                  mNavigator
                      .getSimulator()
                      .simulateLocationsAlongExistingRoute(
                          new SimulationOptions().speedMultiplier(5));
                }

                // Start turn-by-turn guidance along the current route.
                mNavigator.startGuidance();
                break;
              // Handle error conditions returned by the navigator.
              case NO_ROUTE_FOUND:
                displayMessage("Error starting navigation: No route found.", DISPLAY_BOTH);
                break;
              case NETWORK_ERROR:
                displayMessage("Error starting navigation: Network error.", DISPLAY_BOTH);
                break;
              case ROUTE_CANCELED:
                displayMessage("Error starting navigation: Route canceled.", DISPLAY_BOTH);
                break;
              case ROUTING_TIMEOUT:
                displayMessage("Error starting navigation: Routing timed out.", DISPLAY_BOTH);
                break;
              default:
                displayMessage("Error starting navigation: " + String.valueOf(code), DISPLAY_BOTH);
            }
          }
        });
  }

  /**
   * Creates a waypoint from a given place ID and title.
   *
   * @param placeId The ID of the place to be converted to a waypoint.
   * @param title A descriptive title for the waypoint.
   */
  private void createWaypoint(String placeId, String title) {
    try {
      mWaypoints.add(Waypoint.builder().setPlaceIdString(placeId).setTitle(title).build());
    } catch (Waypoint.UnsupportedPlaceIdException e) {
      displayMessage(
          "Error starting navigation: Place ID is not supported: " + placeId, DISPLAY_BOTH);
    }
  }

  /** Displays the calculated travel time and distance to each waypoint. */
  private void displayTimesAndDistances() {
    List<TimeAndDistance> timesAndDistances = mNavigator.getTimeAndDistanceList();
    int leg = 1;
    String message = "You're on your way!";
    for (TimeAndDistance timeAndDistance : timesAndDistances) {
      message =
          message
              + "\nRoute leg: "
              + leg++
              + ": Travel time (seconds): "
              + timeAndDistance.getSeconds()
              + ". Distance (meters): "
              + timeAndDistance.getMeters();
    }
    displayMessage(message, DISPLAY_BOTH);
  }

  /**
   * Registers some event listeners to show a message and take other necessary steps when specific
   * navigation events occur.
   */
  private void registerNavigationListeners() {
    mArrivalListener =
        new Navigator.ArrivalListener() {
          @Override
          public void onArrival(ArrivalEvent arrivalEvent) {
            displayMessage(
                "onArrival: You've arrived at a waypoint: "
                    + mNavigator.getCurrentRouteSegment().getDestinationWaypoint().getTitle(),
                DISPLAY_BOTH);
            // Start turn-by-turn guidance for the next leg of the route.
            if (arrivalEvent.isFinalDestination()) {
              displayMessage("onArrival: You've arrived at the final destination.", DISPLAY_BOTH);
            } else {
              mNavigator.continueToNextDestination();
              mNavigator.startGuidance();
            }
          }
        };
    // Listens for arrival at a waypoint.
    mNavigator.addArrivalListener(mArrivalListener);

    mRouteChangedListener =
        new Navigator.RouteChangedListener() {
          @Override
          public void onRouteChanged() {
            displayMessage(
                "onRouteChanged: The driver's route has changed. Current waypoint: "
                    + mNavigator.getCurrentRouteSegment().getDestinationWaypoint().getTitle(),
                DISPLAY_LOG);
          }
        };
    // Listens for changes in the route.
    mNavigator.addRouteChangedListener(mRouteChangedListener);

    // Listens for road-snapped location updates.
    mRoadSnappedLocationProvider = NavigationApi.getRoadSnappedLocationProvider(getApplication());
    mLocationListener =
        new RoadSnappedLocationProvider.LocationListener() {
          @Override
          public void onLocationChanged(Location location) {
            displayMessage(
                "onLocationUpdated: Navigation engine has provided a new"
                    + " road-snapped location: "
                    + location.toString(),
                DISPLAY_LOG);
          }

          @Override
          public void onRawLocationUpdate(Location location) {
            displayMessage(
                "onLocationUpdated: Navigation engine has provided a new"
                    + " raw location: "
                    + location.toString(),
                DISPLAY_LOG);
          }
        };
    if (mRoadSnappedLocationProvider != null) {
      mRoadSnappedLocationProvider.addLocationListener(mLocationListener);
    } else {
      displayMessage("ERROR: Failed to get a location provider", DISPLAY_LOG);
    }

    mRemainingTimeOrDistanceChangedListener =
        new Navigator.RemainingTimeOrDistanceChangedListener() {
          @Override
          public void onRemainingTimeOrDistanceChanged() {
            displayMessage(
                "onRemainingTimeOrDistanceChanged: Time or distance estimate" + " has changed.",
                DISPLAY_LOG);
          }
        };
    // Listens for changes in time or distance.
    mNavigator.addRemainingTimeOrDistanceChangedListener(
        60, 100, mRemainingTimeOrDistanceChangedListener);
  }

  /** Handles the result of the request for location permissions. */
  @Override
  public void onRequestPermissionsResult(
      int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    mLocationPermissionGranted = false;
    switch (requestCode) {
      case PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION:
        {
          // If request is canceled, the result arrays are empty.
          if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            mLocationPermissionGranted = true;
          }
        }
    }
  }

  /**
   * Shows a message on screen and in the log. Used when something goes wrong.
   *
   * @param errorMessage The message to display.
   */
  private void displayMessage(String errorMessage, String displayMedium) {
    if (displayMedium.equals(DISPLAY_BOTH) || displayMedium.equals(DISPLAY_TOAST)) {
      Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show();
    }

    if (displayMedium.equals(DISPLAY_BOTH) || displayMedium.equals(DISPLAY_LOG)) {
      Log.d(TAG, errorMessage);
    }
  }
}
```

## Detect arrival at a destination

Here, a destination refers to either a final destination or a waypoint. To
detect arrival, call [`Navigator.addArrivalListener()`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator#addArrivalListener(com.google.android.libraries.navigation.Navigator.ArrivalListener)),
which registers for a callback when the device reaches a destination.

Upon arrival, the Navigation SDK for Android fires the
[`onArrival()`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator.ArrivalListener) callback, which can then be used to either stop the
turn-by-turn guidance using [`Navigator.stopGuidance()`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator#stopGuidance()), or
advance to the next waypoint using
[`Navigator.continueToNextDestination()`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator#continueToNextDestination()). If the
navigation session only included a single destination, or if there are no
destinations left in the list of destinations, calling
[`Navigator.continueToNextDestination()`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator#continueToNextDestination())
automatically stops guidance.

When you call `continueToNextDestination()`, the navigator discards all
information about the previous destination. If you want to analyze information
about the previous route segment, you must retrieve the information from the
navigator before calling `continueToNextDestination()`.

To avoid memory leaks, you must call
[`removeArrivalListener(listener)`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator#removeArrivalListener(com.google.android.libraries.navigation.Navigator.ArrivalListener)) when you no longer
need the listener.

    mNavigator.addArrivalListener(new Navigator.ArrivalListener() {
        @Override
        public void onArrival(ArrivalEvent arrivalEvent) {
            displayMessage("onArrival: You've arrived at a waypoint: "
                    +   mNavigator.getCurrentRouteSegment().getDestinationWaypoint().getTitle(),
                    DISPLAY_BOTH);
            // Start turn-by-turn guidance for the next leg of the route.
            if (arrivalEvent.isFinalDestination()) {
                displayMessage("onArrival: You've arrived at the final destination.",
                        DISPLAY_BOTH);
            } else {
                mNavigator.continueToNextDestination();
                mNavigator.startGuidance();
            }
        }
    });

## Receive location updates

Obtain a [`RoadSnappedLocationProvider`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/RoadSnappedLocationProvider) from the
`NavigationApi`, then call
[`RoadSnappedLocationProvider.addLocationListener()`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/RoadSnappedLocationProvider#addLocationListener(com.google.android.libraries.navigation.RoadSnappedLocationProvider.LocationListener)) to
register for a callback when the device's location or heading changes. Note that
this location is snapped to a road, and so it may be different from the location
returned by the [fused location provider](https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi) in the Google Play services
location APIs.

The Navigation SDK provides location updates as often
as possible. When a location update is available, the
Navigation SDK fires the
[`onLocationChanged()`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/RoadSnappedLocationProvider.LocationListener#onLocationChanged(com.google.android.libraries.navigation.LocationEvent)) callback.

Road-snapped location updates are independent of navigation, and can continue
even after navigation has stopped. Subscribing to location updates may cause
battery drain, a memory leak, or unintentional collection of device location
data if you leave the location updates running in the background. Call
[`RoadSnappedLocationProvider.removeLocationListener`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/RoadSnappedLocationProvider#removeLocationListener(com.google.android.libraries.navigation.RoadSnappedLocationProvider.LocationListener))
when you no longer need the listener.

    mRoadSnappedLocationProvider =
            NavigationApi.getRoadSnappedLocationProvider(getApplication());
    if (mRoadSnappedLocationProvider != null) {
        mRoadSnappedLocationProvider.addLocationListener(
                new RoadSnappedLocationProvider.LocationListener() {
                    @Override
                    public void onLocationChanged(Location location) {
                        displayMessage("onLocationUpdated: Navigation engine has provided a new"
                                        +   " road-snapped location: "
                                        +   location.toString(),
                                DISPLAY_LOG);
                    }
                });
    } else {
        displayMessage("ERROR: Failed to get a location provider", DISPLAY_LOG);
    }

## Receive time and distance updates

Call
[`Navigator.addRemainingTimeOrDistanceChangedListener()`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator#addRemainingTimeOrDistanceChangedListener(int,%20int,%20com.google.android.libraries.navigation.Navigator.RemainingTimeOrDistanceChangedListener))
to register for a callback when the remaining time (seconds) or distance
(meters) changes by more than a given threshold.

When the change in time or distance exceeds the specified amount, the
Navigation SDK fires the
[`onRemainingTimeOrDistanceChanged()`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator.RemainingTimeOrDistanceChangedListener)
callback.

To find the remaining time and distance, call
[`Navigator.getTimeAndDistanceList()`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator#getTimeAndDistanceList()). Note that the
times and distances in the list are cumulative: they show the time and distance
from the current location to each waypoint, not from one waypoint to another.
The `TimeAndDistance` object also returns `delaySeverity`. This enum is
either `HEAVY`, `MEDIUM`, `LIGHT`, or `UNKNOWN`. This corresponds to the color
you see for ETA in the Maps UI (heavy = red, medium = yellow, light = green).
This is helpful if you need to create your own ETA footer.

To avoid memory leaks, you must call
[`Navigator.removeRemainingTimeOrDistanceChangedListener(listener)`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator#removeRemainingTimeOrDistanceChangedListener(com.google.android.libraries.navigation.Navigator.RemainingTimeOrDistanceChangedListener))
when you no longer need the listener.

The following sample requests a callback if the remaining time changes by more
than 60 seconds or the remaining distance changes by more than 100 meters.

    mNavigator.addRemainingTimeOrDistanceChangedListener(60, 100,
            new Navigator.RemainingTimeOrDistanceChangedListener() {
                @Override
                public void onRemainingTimeOrDistanceChanged() {
                    displayMessage("onRemainingTimeOrDistanceChanged: Time or distance estimate"
                            +   " has changed.",
                            DISPLAY_LOG);
                }
            });

You can show remaining time and distance information using the built-in display
using the [`setEtaCardEnabled()`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/NavigationFragment#setEtaCardEnabled(boolean)) method and passing a
parameter with the value `TRUE`. To suppress the time and distance display, set
this value to `FALSE`.

You can also expose the ETA for multiple waypoints using the
[`getTimeAndDistanceList()`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator#getTimeAndDistanceList()) method.

## Receive route updates

Call [`Navigator.addRouteChangedListener()`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator#addRouteChangedListener(com.google.android.libraries.navigation.Navigator.RouteChangedListener)) to
register for a callback when the route changes.

When the route changes, the Navigation SDK fires the
[`onRouteChanged()`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator.RouteChangedListener) callback. You can call
[`Navigator.getRouteSegments`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator#getRouteSegments()) and
[`Navigator.getCurrentRouteSegment()`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator#getCurrentRouteSegment()) to find the new
route.

To avoid memory leaks, you must call
[`removeRouteChangedListener(listener)`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/Navigator#removeRouteChangedListener(com.google.android.libraries.navigation.Navigator.RouteChangedListener)) when you no
longer need the listener.

    mNavigator.addRouteChangedListener(new Navigator.RouteChangedListener() {
        @Override
        public void onRouteChanged() {
            displayMessage("onRouteChanged: The driver's route has changed. Current waypoint: "
                    +   mNavigator.getCurrentRouteSegment().getDestinationWaypoint().getTitle(),
                    DISPLAY_LOG);
        }
    });

## Detect when night mode changes

Call
[`NavigationView.addOnNightModeChangedListener`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/NavigationView#addOnNightModeChangedListener(com.google.android.libraries.navigation.NavigationView.OnNightModeChangedListener))
or
[`NavigationFragment.addOnNightModeChangedListener`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/NavigationFragment#addOnNightModeChangedListener(com.google.android.libraries.navigation.NavigationView.OnNightModeChangedListener))
to register for a callback when night mode changes.

> [!NOTE]
> **Note:** In most cases, we recommend using [`NavigationFragment`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/NavigationFragment), which is a wrapper for [`NavigationView`](https://developers.google.com/maps/documentation/navigation/android-sdk/reference/com/google/android/libraries/navigation/NavigationView), instead of interacting directly with `NavigationView`. For more information, see [Navigation map interaction best practices](https://developers.google.com/maps/documentation/navigation/android-sdk/nav-map-interactions-best-practices).

The following example shows listening for night mode changes on a navigation
fragment.

    mNavFragment.addOnNightModeChangedListener(new NavigationView.OnNightModeChangedListener() {
        @Override
        public void onNightModeChanged(NightModeChangedEvent nightModeChangedEvent) {
            displayMessage("Night mode is active: " + nightModeChangedEvent.inNightMode(),
            DISPLAY_LOG);
        }
    });

You can also set night mode programmatically. For more information, see [Set
night mode](https://developers.google.com/maps/documentation/navigation/android-sdk/controls#set_night_mode).