Skip to content

Commit

Permalink
Allow custom LocationEngine from NavigationViewOptions (#1257)
Browse files Browse the repository at this point in the history
  • Loading branch information
danesfeder committed Sep 18, 2018
1 parent 1ebe002 commit 7a3892f
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import android.support.annotation.Nullable;
import android.text.TextUtils;

import com.mapbox.android.core.location.LocationEngine;
import com.mapbox.api.directions.v5.models.BannerInstructions;
import com.mapbox.api.directions.v5.models.DirectionsRoute;
import com.mapbox.api.directions.v5.models.RouteOptions;
Expand Down Expand Up @@ -184,8 +185,8 @@ MapboxNavigation initialize(NavigationViewOptions options) {
initializeDistanceFormatter(options);
initializeNavigationSpeechPlayer(options);
if (!isRunning) {
locationEngineConductor.initializeLocationEngine(getApplication(), options.shouldSimulateRoute());
initializeNavigation(getApplication(), navigationOptions);
LocationEngine locationEngine = initializeLocationEngineFrom(options);
initializeNavigation(getApplication(), navigationOptions, locationEngine);
addMilestones(options);
}
navigationViewRouteEngine.extractRouteOptions(options);
Expand Down Expand Up @@ -267,9 +268,15 @@ private SpeechPlayerProvider initializeSpeechPlayerProvider(boolean voiceLanguag
return new SpeechPlayerProvider(getApplication(), language, voiceLanguageSupported, accessToken);
}

private void initializeNavigation(Context context, MapboxNavigationOptions options) {
navigation = new MapboxNavigation(context, accessToken, options);
navigation.setLocationEngine(locationEngineConductor.obtainLocationEngine());
private LocationEngine initializeLocationEngineFrom(NavigationViewOptions options) {
LocationEngine locationEngine = options.locationEngine();
boolean shouldReplayRoute = options.shouldSimulateRoute();
locationEngineConductor.initializeLocationEngine(getApplication(), locationEngine, shouldReplayRoute);
return locationEngineConductor.obtainLocationEngine();
}

private void initializeNavigation(Context context, MapboxNavigationOptions options, LocationEngine locationEngine) {
navigation = new MapboxNavigation(context, accessToken, options, locationEngine);
addNavigationListeners();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import android.support.design.widget.BottomSheetBehavior.BottomSheetCallback;

import com.google.auto.value.AutoValue;
import com.mapbox.android.core.location.LocationEngine;
import com.mapbox.api.directions.v5.DirectionsCriteria;
import com.mapbox.api.directions.v5.models.DirectionsRoute;
import com.mapbox.services.android.navigation.ui.v5.listeners.BannerInstructionsListener;
Expand Down Expand Up @@ -58,6 +59,9 @@ public abstract class NavigationViewOptions extends NavigationUiOptions {
@Nullable
public abstract SpeechPlayer speechPlayer();

@Nullable
public abstract LocationEngine locationEngine();

@AutoValue.Builder
public abstract static class Builder {

Expand Down Expand Up @@ -97,6 +101,8 @@ public abstract static class Builder {

public abstract Builder speechPlayer(SpeechPlayer speechPlayer);

public abstract Builder locationEngine(LocationEngine locationEngine);

public abstract NavigationViewOptions build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ public void onDestroy() {
deactivateLocationEngine();
}

public void initializeLocationEngine(Context context, boolean shouldReplayRoute) {
initLocationEngine(context, shouldReplayRoute);
public void initializeLocationEngine(Context context, LocationEngine locationEngine, boolean shouldReplayRoute) {
initialize(context, locationEngine, shouldReplayRoute);
}

public void updateSimulatedRoute(DirectionsRoute route) {
Expand All @@ -45,20 +45,34 @@ public LocationEngine obtainLocationEngine() {
return locationEngine;
}

private void initLocationEngine(Context context, boolean simulateRoute) {
if (simulateRoute) {
locationEngine = new ReplayRouteLocationEngine();
private void initialize(Context context, LocationEngine locationEngine, boolean simulateRoute) {
if (locationEngine != null) {
this.locationEngine = locationEngine;
} else if (simulateRoute) {
this.locationEngine = new ReplayRouteLocationEngine();
} else {
LocationEngineProvider locationEngineProvider = new LocationEngineProvider(context.getApplicationContext());
locationEngine = locationEngineProvider.obtainBestLocationEngineAvailable();
locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY);
locationEngine.setFastestInterval(FASTEST_INTERVAL_IN_MILLIS);
locationEngine.setInterval(INTERVAL_IN_MILLIS);
updateLastLocation();
this.locationEngine = buildLocationEngine(context);
}
updateLastLocation();
activateLocationEngine();
}

private LocationEngine buildLocationEngine(Context context) {
LocationEngineProvider locationEngineProvider = new LocationEngineProvider(context.getApplicationContext());
LocationEngine locationEngine = locationEngineProvider.obtainBestLocationEngineAvailable();
locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY);
locationEngine.setFastestInterval(FASTEST_INTERVAL_IN_MILLIS);
locationEngine.setInterval(INTERVAL_IN_MILLIS);
return locationEngine;
}

@SuppressWarnings( {"MissingPermission"})
private void updateLastLocation() {
if (locationEngine.getLastLocation() != null) {
listener.onLocationUpdate(locationEngine.getLastLocation());
}
}

private void activateLocationEngine() {
if (isValidLocationEngine()) {
locationEngine.addLocationEngineListener(locationEngineListener);
Expand All @@ -74,13 +88,6 @@ private void deactivateLocationEngine() {
}
}

@SuppressWarnings( {"MissingPermission"})
private void updateLastLocation() {
if (locationEngine.getLastLocation() != null) {
listener.onLocationUpdate(locationEngine.getLastLocation());
}
}

private boolean isValidLocationEngine() {
return locationEngine != null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,60 @@

import org.junit.Test;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class LocationEngineConductorTest extends BaseTest {

@Test
public void sanity() throws Exception {
public void sanity() {
LocationEngineConductorListener mockCallback = mock(LocationEngineConductorListener.class);
LocationEngineConductor locationEngineConductor = new LocationEngineConductor(mockCallback);

assertNotNull(locationEngineConductor);
}

@Test
public void onInitWithSimulation_mockLocationEngineIsActivated() throws Exception {
public void onInitWithSimulation_replayEngineIsProvided() {
LocationEngineConductorListener mockCallback = mock(LocationEngineConductorListener.class);
LocationEngineConductor locationEngineConductor = new LocationEngineConductor(mockCallback);
boolean simulateRouteEnabled = true;
locationEngineConductor.initializeLocationEngine(createMockContext(), simulateRouteEnabled);
locationEngineConductor.initializeLocationEngine(createMockContext(), null, simulateRouteEnabled);

LocationEngine locationEngine = locationEngineConductor.obtainLocationEngine();

assertTrue(locationEngine instanceof ReplayRouteLocationEngine);
}

@Test
public void onInitWithSimulationAndCustomEngine_customEngineIsProvided() {
LocationEngineConductorListener mockCallback = mock(LocationEngineConductorListener.class);
LocationEngineConductor locationEngineConductor = new LocationEngineConductor(mockCallback);
LocationEngine customEngine = mock(LocationEngine.class);
boolean simulateRouteEnabled = true;
locationEngineConductor.initializeLocationEngine(createMockContext(), customEngine, simulateRouteEnabled);

LocationEngine locationEngine = locationEngineConductor.obtainLocationEngine();

assertEquals(customEngine, locationEngine);
}

@Test
public void onInitWithCustomEngine_customEngineIsActivated() {
LocationEngineConductorListener mockCallback = mock(LocationEngineConductorListener.class);
LocationEngineConductor locationEngineConductor = new LocationEngineConductor(mockCallback);
LocationEngine customEngine = mock(LocationEngine.class);
boolean simulateRouteEnabled = false;

locationEngineConductor.initializeLocationEngine(createMockContext(), customEngine, simulateRouteEnabled);

verify(customEngine).activate();
}

@NonNull
private Context createMockContext() {
Context mockContext = mock(Context.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public class MapboxNavigation implements ServiceConnection {
* {@link #MapboxNavigation(Context, String, MapboxNavigationOptions)} if all the default options
* fit your needs.
* <p>
* Initialization will also add the default milestones and create a new LOST location engine
* Initialization will also add the default milestones and create a new location engine
* which will be used during navigation unless a different engine gets passed in through
* {@link #setLocationEngine(LocationEngine)}.
* </p>
Expand All @@ -87,7 +87,7 @@ public MapboxNavigation(@NonNull Context context, @NonNull String accessToken) {
* that your app requires special tweaking. Once this class is initialized, the options specified
* through the options class cannot be modified.
* <p>
* Initialization will also add the default milestones and create a new LOST location engine
* Initialization will also add the default milestones and create a new location engine
* which will be used during navigation unless a different engine gets passed in through
* {@link #setLocationEngine(LocationEngine)}.
* </p>
Expand All @@ -106,6 +106,28 @@ public MapboxNavigation(@NonNull Context context, @NonNull String accessToken,
initialize();
}

/**
* Constructs a new instance of this class using a custom built options class. Building a custom
* {@link MapboxNavigationOptions} object and passing it in allows you to further customize the
* user experience. Once this class is initialized, the options specified
* through the options class cannot be modified.
*
* @param context required in order to create and bind the navigation service
* @param accessToken a valid Mapbox access token
* @param options a custom built {@code MapboxNavigationOptions} class
* @param locationEngine a LocationEngine to provide Location updates
* @see MapboxNavigationOptions
* @since 0.19.0
*/
public MapboxNavigation(@NonNull Context context, @NonNull String accessToken,
@NonNull MapboxNavigationOptions options, @NonNull LocationEngine locationEngine) {
initializeContext(context);
this.accessToken = accessToken;
this.options = options;
this.locationEngine = locationEngine;
initialize();
}

// Package private (no modifier) for testing purposes
MapboxNavigation(@NonNull Context context, @NonNull String accessToken,
@NonNull MapboxNavigationOptions options, NavigationTelemetry navigationTelemetry,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class NavigationLocationEngineUpdater {
}

void updateLocationEngine(LocationEngine locationEngine) {
removeLocationEngineListener();
this.locationEngine = locationEngine;
locationEngine.addLocationEngineListener(listener);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public class NavigationService extends Service {

private final IBinder localBinder = new LocalBinder();
private RouteProcessorBackgroundThread thread;
private NavigationLocationEngineUpdater locationProvider;
private NavigationLocationEngineUpdater locationEngineUpdater;
private RouteFetcher routeFetcher;
private NavigationNotificationProvider notificationProvider;

Expand Down Expand Up @@ -65,15 +65,15 @@ public void onDestroy() {
void startNavigation(MapboxNavigation mapboxNavigation) {
initialize(mapboxNavigation);
startForegroundNotification(notificationProvider.retrieveNotification());
locationProvider.forceLocationUpdate(mapboxNavigation.getRoute());
locationEngineUpdater.forceLocationUpdate(mapboxNavigation.getRoute());
}

/**
* Removes the location / route listeners and quits the thread.
*/
void endNavigation() {
routeFetcher.clearListeners();
locationProvider.removeLocationEngineListener();
locationEngineUpdater.removeLocationEngineListener();
notificationProvider.shutdown(getApplication());
thread.quit();
}
Expand All @@ -85,7 +85,7 @@ void endNavigation() {
* @param locationEngine to update the provider
*/
void updateLocationEngine(LocationEngine locationEngine) {
locationProvider.updateLocationEngine(locationEngine);
locationEngineUpdater.updateLocationEngine(locationEngine);
}

private void initialize(MapboxNavigation mapboxNavigation) {
Expand Down Expand Up @@ -124,7 +124,7 @@ private void initializeLocationProvider(MapboxNavigation mapboxNavigation) {
NavigationLocationEngineListener listener = new NavigationLocationEngineListener(
thread, mapboxNavigation, locationEngine, validator
);
locationProvider = new NavigationLocationEngineUpdater(locationEngine, listener);
locationEngineUpdater = new NavigationLocationEngineUpdater(locationEngine, listener);
}

private void startForegroundNotification(NavigationNotification navigationNotification) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ public void updateLocationEngine_engineListenerIsAdded() {
verify(newLocationEngine).addLocationEngineListener(eq(listener));
}

@Test
public void updateLocationEngine_oldEngineListenerIsRemoved() {
LocationEngine locationEngine = mock(LocationEngine.class);
NavigationLocationEngineListener listener = mock(NavigationLocationEngineListener.class);
NavigationLocationEngineUpdater provider = new NavigationLocationEngineUpdater(locationEngine, listener);
LocationEngine newLocationEngine = mock(LocationEngine.class);

provider.updateLocationEngine(newLocationEngine);

verify(locationEngine).removeLocationEngineListener(eq(listener));
}

@Test
public void forceLocationUpdate_nonNullLastLocationIsSent() {
LocationEngine locationEngine = mock(LocationEngine.class);
Expand Down

0 comments on commit 7a3892f

Please sign in to comment.