Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial Mapbox Navigator libandroid-navigation integration #1265

Merged
merged 6 commits into from
Sep 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.mapbox.services.android.navigation.v5.location.replay.ReplayRouteLocationEngine;
import com.mapbox.services.android.navigation.v5.milestone.Milestone;
import com.mapbox.services.android.navigation.v5.milestone.MilestoneEventListener;
import com.mapbox.services.android.navigation.v5.milestone.VoiceInstructionMilestone;
import com.mapbox.services.android.navigation.v5.navigation.MapboxNavigation;
import com.mapbox.services.android.navigation.v5.navigation.MapboxNavigationOptions;
import com.mapbox.services.android.navigation.v5.navigation.NavigationEventListener;
Expand Down Expand Up @@ -226,7 +227,9 @@ public void onProgressChange(Location location, RouteProgress routeProgress) {

@Override
public void onMilestoneEvent(RouteProgress routeProgress, String instruction, Milestone milestone) {
Timber.d("onMilestoneEvent - Current Instruction: " + instruction);
if (milestone instanceof VoiceInstructionMilestone) {
Snackbar.make(contentLayout, instruction, Snackbar.LENGTH_SHORT).show();
}
}

@Override
Expand All @@ -249,7 +252,7 @@ public void onResponse(Call<DirectionsResponse> call, Response<DirectionsRespons

@Override
public void onFailure(Call<DirectionsResponse> call, Throwable throwable) {
Timber.e("Getting directions failed: ", throwable);
Timber.e(throwable);
}

private void getRoute(Point origin, Point destination, Float bearing) {
Expand All @@ -270,12 +273,9 @@ private void drawRoute(DirectionsRoute route) {
}

if (!points.isEmpty()) {

if (polyline != null) {
mapboxMap.removePolyline(polyline);
}

// Draw polyline on map
polyline = mapboxMap.addPolyline(new PolylineOptions()
.addAll(points)
.color(Color.parseColor("#4264fb"))
Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ allprojects {
google()
jcenter()
maven { url 'https://plugins.gradle.org/m2' }
maven { url 'https://mapbox.bintray.com/mapbox' }
}

group = GROUP
Expand Down
2 changes: 2 additions & 0 deletions gradle/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ ext {
mapboxMapSdk : '6.5.0',
mapboxSdkServices : '3.4.1',
mapboxEvents : '3.2.0',
mapboxNavigator : '2.0.0',
locationLayerPlugin: '0.8.1',
autoValue : '1.5.4',
autoValueParcel : '0.2.5',
Expand Down Expand Up @@ -51,6 +52,7 @@ ext {
mapboxSdkServices : "com.mapbox.mapboxsdk:mapbox-sdk-services:${version.mapboxSdkServices}",
mapboxSdkTurf : "com.mapbox.mapboxsdk:mapbox-sdk-turf:${version.mapboxSdkServices}",
mapboxEvents : "com.mapbox.mapboxsdk:mapbox-android-telemetry:${version.mapboxEvents}",
mapboxNavigator : "com.mapbox.navigator:mapbox-navigation-native:${version.mapboxNavigator}",
locationLayerPlugin : "com.mapbox.mapboxsdk:mapbox-android-plugin-locationlayer:${version.locationLayerPlugin}",

// AutoValue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ public void userOffRoute(Location location) {
if (hasNetworkConnection()) {
speechPlayer.onOffRoute();
Point newOrigin = Point.fromLngLat(location.getLongitude(), location.getLatitude());
sendEventOffRoute(newOrigin);
handleOffRouteEvent(newOrigin);
}
}
};
Expand Down Expand Up @@ -446,7 +446,7 @@ private void sendEventArrival(RouteProgress routeProgress, Milestone milestone)
}
}

private void sendEventOffRoute(Point newOrigin) {
private void handleOffRouteEvent(Point newOrigin) {
if (navigationViewEventDispatcher != null && navigationViewEventDispatcher.allowRerouteFrom(newOrigin)) {
navigationViewEventDispatcher.onOffRoute(newOrigin);
OffRouteEvent event = new OffRouteEvent(newOrigin, routeProgress);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ RouteProgress buildTestRouteProgress(DirectionsRoute route,
.intersectionDistancesAlongStep(intersectionDistances)
.stepIndex(stepIndex)
.legIndex(legIndex)
.inTunnel(false)
.build();
}

Expand Down
3 changes: 3 additions & 0 deletions libandroid-navigation/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ dependencies {
api dependenciesList.mapboxSdkServices
api dependenciesList.mapboxSdkTurf

// Navigator
implementation dependenciesList.mapboxNavigator

// Support
implementation dependenciesList.supportAppcompatV7

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package com.mapbox.services.android.navigation.v5.milestone;

import com.mapbox.api.directions.v5.models.DirectionsRoute;
import com.mapbox.api.directions.v5.models.LegStep;
import com.mapbox.api.directions.v5.models.VoiceInstructions;
import com.mapbox.services.android.navigation.v5.instruction.Instruction;
import com.mapbox.services.android.navigation.v5.navigation.VoiceInstructionLoader;
import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress;
import com.mapbox.services.android.navigation.v5.utils.RouteUtils;

/**
* A default milestone that is added to {@link com.mapbox.services.android.navigation.v5.navigation.MapboxNavigation}
Expand All @@ -18,39 +15,25 @@
public class VoiceInstructionMilestone extends Milestone {

private static final String EMPTY_STRING = "";

private VoiceInstructions instructions;
private DirectionsRoute currentRoute;
private RouteUtils routeUtils;
private String announcement = EMPTY_STRING;
private String ssmlAnnouncement = EMPTY_STRING;

VoiceInstructionMilestone(Builder builder) {
super(builder);
routeUtils = new RouteUtils();
}

@Override
public boolean isOccurring(RouteProgress previousRouteProgress, RouteProgress routeProgress) {
if (isNewRoute(routeProgress)) {
cacheInstructions(routeProgress, true);
}
LegStep currentStep = routeProgress.currentLegProgress().currentStep();
double stepDistanceRemaining = routeProgress.currentLegProgress().currentStepProgress().distanceRemaining();
VoiceInstructions instructions = routeUtils.findCurrentVoiceInstructions(currentStep, stepDistanceRemaining);
if (shouldBeVoiced(instructions, stepDistanceRemaining)) {
return updateInstructions(routeProgress, instructions);
}
return false;
checkForNewRoute(previousRouteProgress, routeProgress);
return updateCurrentAnnouncement(routeProgress);
}

@Override
public Instruction getInstruction() {
return new Instruction() {
@Override
public String buildInstruction(RouteProgress routeProgress) {
if (instructions == null) {
return routeProgress.currentLegProgress().currentStep().name();
}
return instructions.announcement();
return announcement;
}
};
}
Expand All @@ -65,10 +48,7 @@ public String buildInstruction(RouteProgress routeProgress) {
* @since 0.8.0
*/
public String getSsmlAnnouncement() {
if (instructions == null) {
return EMPTY_STRING;
}
return instructions.ssmlAnnouncement();
return ssmlAnnouncement;
}

/**
Expand All @@ -80,55 +60,7 @@ public String getSsmlAnnouncement() {
* @since 0.12.0
*/
public String getAnnouncement() {
if (instructions == null) {
return EMPTY_STRING;
}
return instructions.announcement();
}

/**
* Looks to see if we have a new route.
*
* @param routeProgress provides updated route information
* @return true if new route, false if not
*/
private boolean isNewRoute(RouteProgress routeProgress) {
boolean newRoute = currentRoute == null || !currentRoute.equals(routeProgress.directionsRoute());
currentRoute = routeProgress.directionsRoute();
return newRoute;
}

/**
* Checks if the current instructions are different from the instructions
* determined by the step distance remaining.
*
* @param instructions the current voice instructions from the list of step instructions
* @param stepDistanceRemaining the current step distance remaining
* @return true if time to voice the announcement, false if not
*/
private boolean shouldBeVoiced(VoiceInstructions instructions, double stepDistanceRemaining) {
boolean isNewInstruction = this.instructions == null || !this.instructions.equals(instructions);
boolean isValidNewInstruction = instructions != null && isNewInstruction;
return isValidNewInstruction && instructions.distanceAlongGeometry() >= stepDistanceRemaining;
}

private boolean updateInstructions(RouteProgress routeProgress, VoiceInstructions instructions) {
cacheInstructions(routeProgress, false);
this.instructions = instructions;
return true;
}

/**
* Caches the instructions in the VoiceInstructionLoader if it has been initialized
*
* @param routeProgress containing the instructions
* @param isFirst whether it's the first routeProgress of the route
*/
private void cacheInstructions(RouteProgress routeProgress, boolean isFirst) {
VoiceInstructionLoader voiceInstructionLoader = VoiceInstructionLoader.getInstance();
devotaaabel marked this conversation as resolved.
Show resolved Hide resolved
if (voiceInstructionLoader != null) {
voiceInstructionLoader.cacheInstructions(routeProgress, isFirst);
}
return announcement;
}

public static final class Builder extends Milestone.Builder {
Expand All @@ -155,4 +87,29 @@ public VoiceInstructionMilestone build() {
return new VoiceInstructionMilestone(this);
}
}

private void checkForNewRoute(RouteProgress previousRouteProgress, RouteProgress routeProgress) {
DirectionsRoute previousRoute = previousRouteProgress.directionsRoute();
DirectionsRoute currentRoute = routeProgress.directionsRoute();
if (!previousRoute.equals(currentRoute)) {
cacheInstructions(routeProgress, true);
}
}

private void cacheInstructions(RouteProgress routeProgress, boolean isFirst) {
VoiceInstructionLoader voiceInstructionLoader = VoiceInstructionLoader.getInstance();
if (voiceInstructionLoader != null) {
voiceInstructionLoader.cacheInstructions(routeProgress, isFirst);
}
}

private boolean updateCurrentAnnouncement(RouteProgress routeProgress) {
if (!announcement.equals(routeProgress.currentAnnouncement())) {
announcement = routeProgress.currentAnnouncement();
ssmlAnnouncement = routeProgress.currentSsmlAnnouncement();
cacheInstructions(routeProgress, false);
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.mapbox.android.core.location.LocationEnginePriority;
import com.mapbox.android.core.location.LocationEngineProvider;
import com.mapbox.api.directions.v5.models.DirectionsRoute;
import com.mapbox.navigator.Navigator;
import com.mapbox.services.android.navigation.v5.milestone.BannerInstructionMilestone;
import com.mapbox.services.android.navigation.v5.milestone.Milestone;
import com.mapbox.services.android.navigation.v5.milestone.MilestoneEventListener;
Expand Down Expand Up @@ -52,15 +53,20 @@ public class MapboxNavigation implements ServiceConnection {

private NavigationEventDispatcher navigationEventDispatcher;
private NavigationEngineFactory navigationEngineFactory;
private NavigationTelemetry navigationTelemetry = null;
private NavigationService navigationService;
private MapboxNavigator mapboxNavigator;
private DirectionsRoute directionsRoute;
private MapboxNavigationOptions options;
private LocationEngine locationEngine = null;
private Set<Milestone> milestones;
private final String accessToken;
private Context applicationContext;
private boolean isBound;
private NavigationTelemetry navigationTelemetry = null;

static {
NavigationLibraryLoader.load();
}

/**
* Constructs a new instance of this class using the default options. This should be used over
Expand Down Expand Up @@ -115,7 +121,7 @@ public MapboxNavigation(@NonNull Context context, @NonNull String accessToken,
this.options = options;
this.navigationTelemetry = navigationTelemetry;
this.locationEngine = locationEngine;
initialize();
initializeForTest();
}

// Package private (no modifier) for testing purposes
Expand All @@ -126,7 +132,22 @@ public MapboxNavigation(@NonNull Context context, @NonNull String accessToken,
this.options = MapboxNavigationOptions.builder().build();
this.navigationTelemetry = navigationTelemetry;
this.locationEngine = locationEngine;
initialize();
initializeForTest();
danesfeder marked this conversation as resolved.
Show resolved Hide resolved
}

private void initializeForTest() {
// Initialize event dispatcher and add internal listeners
navigationEventDispatcher = new NavigationEventDispatcher();
navigationEngineFactory = new NavigationEngineFactory();
initializeDefaultLocationEngine();
initializeTelemetry();

// Create and add default milestones if enabled.
milestones = new HashSet<>();
if (options.defaultMilestonesEnabled()) {
addMilestone(new VoiceInstructionMilestone.Builder().setIdentifier(VOICE_INSTRUCTION_MILESTONE_ID).build());
addMilestone(new BannerInstructionMilestone.Builder().setIdentifier(BANNER_INSTRUCTION_MILESTONE_ID).build());
}
}

/**
Expand All @@ -136,6 +157,7 @@ public MapboxNavigation(@NonNull Context context, @NonNull String accessToken,
*/
private void initialize() {
// Initialize event dispatcher and add internal listeners
mapboxNavigator = new MapboxNavigator(new Navigator());
navigationEventDispatcher = new NavigationEventDispatcher();
navigationEngineFactory = new NavigationEngineFactory();
initializeDefaultLocationEngine();
Expand Down Expand Up @@ -366,28 +388,7 @@ public LocationEngine getLocationEngine() {
* @since 0.1.0
*/
public void startNavigation(@NonNull DirectionsRoute directionsRoute) {
ValidationUtils.validDirectionsRoute(directionsRoute, options.defaultMilestonesEnabled());
this.directionsRoute = directionsRoute;
Timber.d("MapboxNavigation startNavigation called.");
if (!isBound) {
// Begin telemetry session
navigationTelemetry.startSession(directionsRoute);

// Start the NavigationService
Intent intent = getServiceIntent();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
applicationContext.startForegroundService(intent);
} else {
applicationContext.startService(intent);
}
applicationContext.bindService(intent, this, Context.BIND_AUTO_CREATE);

// Send navigation event running: true
navigationEventDispatcher.onNavigationEvent(true);
} else {
// Update telemetry directions route
navigationTelemetry.updateSessionRoute(directionsRoute);
}
startNavigationWith(directionsRoute);
}

/**
Expand Down Expand Up @@ -791,10 +792,37 @@ NavigationEventDispatcher getEventDispatcher() {
return navigationEventDispatcher;
}

NavigationEngineFactory retrieveEngineProvider() {
NavigationEngineFactory retrieveEngineFactory() {
return navigationEngineFactory;
}

MapboxNavigator retrieveMapboxNavigator() {
return mapboxNavigator;
}

private void startNavigationWith(@NonNull DirectionsRoute directionsRoute) {
ValidationUtils.validDirectionsRoute(directionsRoute, options.defaultMilestonesEnabled());
this.directionsRoute = directionsRoute;
mapboxNavigator.updateRoute(directionsRoute.toJson());
if (!isBound) {
navigationTelemetry.startSession(directionsRoute);
startNavigationService();
navigationEventDispatcher.onNavigationEvent(true);
} else {
navigationTelemetry.updateSessionRoute(directionsRoute);
}
}

private void startNavigationService() {
Intent intent = getServiceIntent();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
applicationContext.startForegroundService(intent);
} else {
applicationContext.startService(intent);
}
applicationContext.bindService(intent, this, Context.BIND_AUTO_CREATE);
}

private Intent getServiceIntent() {
return new Intent(applicationContext, NavigationService.class);
}
Expand Down
Loading