SDK for Android Developer's Guide

Navigation Events

NavigationManager Listeners

NavigationManager contains a number of listeners that are responsible for monitoring navigation status and getting instructions during navigation. The following table shows the name of the available listeners and the information provided to them.

Listener Name Purpose
PositionListener Whether the current position has been updated
NavigationManagerEventListener Whether the navigation session has started, updated, or ended
NewInstructionEventListener Whether a new navigation instruction is available to be fetched
GpsSignalListener Whether the system has lost its GPS signal
RerouteListener Whether a route recalculation has begun as a result of the current position deviating from the original route
TrafficRerouteListener Whether a route recalculation to avoid traffic has begun
SpeedWarningListener Whether the user has exceeded the speed limit
SafetySpotListener Whether safety spots such as speed cameras and red light cameras are upcoming
LaneInformationListener Whether lane information should be presented
RealisticViewListener Listens for events related to a realistic view image
AudioFeedbackListener Whether a voice command or vibration alert is available
AlternativeRoutesListener Provides alternative route update
SchoolZoneListener Provides callback of school zone related events.

Listener instances can be added to Navigation Manger through their respective add and remove methods. For example, LaneInformationListener can be added and removed by using addLaneInformationListener(WeakReference<LaneInformationListener>) and removeLaneInformationListener(WeakReference<LaneInformationListener>).

Note: NavigationManager.SpeedWarningListener only returns car speed warnings. Truck speed warnings are not currently supported.

New Instructions and Maneuvers

Maneuver class represents the action required to go from one segment to the next within a calculated Route. Each Maneuver object provides information such as:
  • Location of the maneuver
  • Action required to complete the maneuver
  • Distance between maneuvers
  • Current road
  • Next road
  • Estimated time of the maneuver
  • Signposts (if any) indicating entrance, exit, or merge information
NavigationManager provides a new Maneuver object after every onNewInstructionEvent() callback. You can implement NewInstructionEventListener and implement this callback to provide display logic. For example:

@Override
public void onNewInstructionEvent() {
  Maneuver maneuver = navigationManager.getNextManeuver();
  if (maneuver != null) {
    if (maneuver.getAction() == Maneuver.Action.END) {
      // notify the user that the route is complete
    }

    // display current or next road information
    // display maneuver.getDistanceToNextManeuver()
  }
}

Rerouting

While navigation is in progress, the following three types of rerouting can occur:
  • Basic Route Recalculation - This is performed automatically by the guidance engine. The guidance engine checks the current position to see if it is on the route and approaching the target destination. If it is not, then it triggers a route recalculation and updates the navigation session. RerouteListener.onRerouteBegin() and RerouteListener.onRerouteEnd(RouteResult, RoutingError) callbacks also occur.
  • Dynamic Traffic Reroute - This mode is enabled by default. In this mode HERE SDK regularly requests a traffic-aware route recalculation from the server and the navigation manager switches to this route automatically. For more information, see Traffic-Aware Navigation.
  • Manual Traffic Reroute - This mode can be optionally enabled. In this mode HERE SDK also requests a traffic-aware route recalculation from the server but it notifies the client before using the new route in the navigation manager. For more information, see Traffic-Aware Navigation.
NavigationManager provides a new Route object after every rerouting. You can implement NavigationManagerEventListener and implement onRouteUpdated callback to update the route on the map. For example:

@Override
public void onRouteUpdated(@NonNull Route updatedRoute) {
  // remove old MapRoute object from the map
  m_map.removeMapObject(m_currentMapRoute);
  // create a new MapRoute object
  m_currentMapRoute = new MapRoute(updatedRoute);
  // display new route on the map
  m_map.addMapObject(m_currentMapRoute);
}

You can find example that demonstrates the rerouting feature in Advanced Navigation project at https://github.com/heremaps/.

Lane Information

NavigationManager.LaneInformationListener provides onLaneInformation(List<LaneInformation>, RoadElement) callback method. This callback occurs when the user has arrived at a point in the route where lane information should be presented, e.g. before a highway exit. LaneInformation class represents a lane turn direction and whether this lane is on the current route. For example, an application may receive onLaneInformation(List<LaneInformation>, RoadElement) callback as the user navigates to an intersection. If the route requires a left turn and the current road has three lanes — a left-turn lane and two straight lanes — then the callback returns with three LaneInformation objects. Since LaneInformation objects are always returned in the callback method in a left-to-right order, the first LaneInformation has a direction of LEFT and LaneInformation.getRecommendationState() returns a recommendation on whether the lane can be taken for the current route. If there isn't enough data to determine whether the lane is on or off-route, getRecommendationState() returns NOT_AVAILABLE.

Note: Information about lanes requires extra map data for a route. The application must first download the required map data using {MapDataPrefetcher or MapLoader. Otherwise, lane information will not be returned properly.

School zone

NavigationManager.SchoolZoneListener provides callback methods to check whether there is school zone ahead, whether user enters or leaves school zone. Callback onSchoolZoneAhead(SchoolZoneNotification notification) occurs when there is school zone ahead if user follows current route direction, it will be triggered in about 100 meters before each RoadElement with school zone. Callback onSchoolZoneUpdated occurs either when user entered school zone or left. For example:

@Override
public void onSchoolZoneAhead(@NonNull SchoolZoneNotification notification) {
  // distance to the road element with school zone
  int distance = notification.getDistance();
  // road element associated with this school zone
  RoadElement roadElement = notification.getRoadElement();
  // begin time, e.g. Monday 09:00
  Date timeBegin = notification.getSchoolZoneInfo().getTimeBegin();
  // end time, e.g. Monday 17:00
  Date timeEnd = notification.getSchoolZoneInfo().getTimeEnd();
  float speedLimit = notification.getSchoolZoneInfo().getSpeedLimit();
}

@Override
public void onSchoolZoneUpdated(@Nullable SchoolZoneInfo info) {
  if (info != null) {
    // user enters school zone
    float speedLimit = info.getSpeedLimit();
  } else {
    // user left school zone
  }
}
        
Developer can also use utility methods from SchoolZoneRestrictionsChecker to check whether specific road element has school zone. For example:

// Get road element from geo coordinate
RoadElement roadElement = RoadElement.getRoadElement(geoCoordinate, "MAC");
if (roadElement != null) {
  // Check whether road element has active school zone
  SchoolZoneInfo info = SchoolZoneRestrictionsChecker.getSchoolZoneInfo(roadElement);
  if (info != null) {
    // provided road element has school zone
  }

  // Check whether school zone is active at specific time
  SimpleDateFormat format = new SimpleDateFormat("HH:mm dd-MM-yyyy");
  Date date = format.parse("15:00 16-09-2020");
  SchoolZoneInfo infoWithSpecificDate = SchoolZoneRestrictionsChecker
    .getSchoolZoneInfo(roadElement, date);
  if (infoWithSpecificDate != null) {
    // school zone is active at provided time
  }
} else {
  // RoadElement is null either because there is no road at the provided location or map
  // data is not loaded. Use MapLoader API to download map area, or use road element from
  // another provider, e.g. from the calculated route.
}
        

Realistic View: 2D Signposts and Junction View

In addition to the data offered through the Lane Info feature, SDK for Android also offers image previews of signposts and junctions on certain highways. These two features together are known as Realistic View.

Figure 1. An Example of a 2D Signpost
Figure 2. An Example of a Junction View

The 2D Signpost feature provides images that illustrate road signposts. For example, as a user approaches a fork on a highway, your application can show a preview of the instruction signs above the highway. The Junction View feature provides images that illustrate road junctions. For example, as a user approaches a junction on a highway, your application can show the lanes near the junction with indicator arrows highlighting the correct lane to take. Signpost and junction view images are provided as SVG images through the following RealisticViewListener callback methods which occur just after the previous maneuver and before entering the junction:

  • onRealisticViewShow(AspectRatio, Image, Image)
  • onRealisticViewNextManeuver(AspectRatio, Image, Image)

Realistic view is disabled by default. To enable it, call NavigationManager.setRealisticViewMode(RealisticViewMode) and set the view mode to RealisticViewMode.DAY or RealisticViewMode.NIGHT. Next, register the desired image aspect ratios by using addRealisticViewAspectRatio(AspectRatio ratio). After adding your listener implementation, your application begins to receive the above event callbacks as HERE SDK arrives to a highway section that supports Realistic View.

Note: It is possible to add multiple aspect ratios and receive multiple images of the same signpost or junction view.

The following is an example of onJunctionViewShow() and enabling realistic view:


// In the RealisticViewListener implementation
public void onRealisticViewShow(AspectRatio ratio, Image junction, Image signpost) {
  if (junction.getType() == Image.Type.SVG) {
    // full size is too big (will cover most of the screen), so cut the size in half
    Bitmap bmpImage = junction.getBitmap((int) (junction.getWidth() * 0.5),
        (int) (junction.getHeight() * 0.5));
    if (bmpImage != null) {
      //
      // show bmpImage on-screen
      //
    }
  }
  ...
}

navigationManager.setRealisticViewMode(NavigationManager.RealisticViewMode.DAY);
navigationManager.addRealisticViewAspectRatio(AspectRatio.AR_4x3);
navigationManager.addRealisticViewListener(
  new WeakReference<NavigationManager.RealisticViewListener>(viewListener));