Skip to content

Commit

Permalink
add periodic refresh (every 5 minutes) functionality to update traffi…
Browse files Browse the repository at this point in the history
…c and etas on a regular basis when in navigation
  • Loading branch information
Guardiola31337 committed Apr 2, 2019
1 parent 8dd2fae commit b322c74
Show file tree
Hide file tree
Showing 11 changed files with 344 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
import timber.log.Timber;

import static com.mapbox.services.android.navigation.v5.navigation.NavigationConstants.BANNER_INSTRUCTION_MILESTONE_ID;
import static com.mapbox.services.android.navigation.v5.navigation.NavigationConstants.NON_NULL_APPLICATION_CONTEXT_REQUIRED;
import static com.mapbox.services.android.navigation.v5.navigation.NavigationConstants
.NON_NULL_APPLICATION_CONTEXT_REQUIRED;
import static com.mapbox.services.android.navigation.v5.navigation.NavigationConstants.VOICE_INSTRUCTION_MILESTONE_ID;

/**
Expand Down Expand Up @@ -67,6 +68,7 @@ public class MapboxNavigation implements ServiceConnection {
private final String accessToken;
private Context applicationContext;
private boolean isBound;
private RouteRefresher routeRefresher;

static {
NavigationLibraryLoader.load();
Expand Down Expand Up @@ -845,6 +847,11 @@ LocationEngineRequest retrieveLocationEngineRequest() {
return locationEngineRequest;
}

@Nullable
RouteRefresher retrieveRouteRefresher() {
return routeRefresher;
}

private void initializeForTest() {
// Initialize event dispatcher and add internal listeners
navigationEventDispatcher = new NavigationEventDispatcher();
Expand Down Expand Up @@ -926,6 +933,7 @@ private LocationEngineRequest obtainLocationEngineRequest() {
private void startNavigationWith(@NonNull DirectionsRoute directionsRoute, DirectionsRouteType routeType) {
ValidationUtils.validDirectionsRoute(directionsRoute, options.defaultMilestonesEnabled());
this.directionsRoute = directionsRoute;
routeRefresher = new RouteRefresher(this, new RouteRefresh(accessToken));
mapboxNavigator.updateRoute(directionsRoute, routeType);
if (!isBound) {
navigationTelemetry.startSession(directionsRoute, locationEngine);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import static com.mapbox.services.android.navigation.v5.navigation.NavigationConstants
.NAVIGATION_LOCATION_ENGINE_INTERVAL_LAG;
import static com.mapbox.services.android.navigation.v5.navigation.NavigationConstants.ROUNDING_INCREMENT_FIFTY;
import static com.mapbox.services.android.navigation.v5.navigation.NavigationConstants.ROUTE_REFRESH_INTERVAL;

/**
* Immutable and can't be changed after passing into {@link MapboxNavigation}.
Expand All @@ -21,6 +22,10 @@ public abstract class MapboxNavigationOptions {

public abstract boolean enableAutoIncrementLegIndex();

public abstract boolean enableRefreshRoute();

public abstract long refreshIntervalInMilliseconds();

public abstract boolean isFromNavigationUi();

public abstract boolean isDebugLoggingEnabled();
Expand All @@ -47,6 +52,10 @@ public abstract static class Builder {

public abstract Builder enableAutoIncrementLegIndex(boolean enableAutoIncrementLegIndex);

public abstract Builder enableRefreshRoute(boolean enableRefreshRoute);

public abstract Builder refreshIntervalInMilliseconds(long intervalInMilliseconds);

public abstract Builder isFromNavigationUi(boolean isFromNavigationUi);

public abstract Builder isDebugLoggingEnabled(boolean debugLoggingEnabled);
Expand All @@ -66,6 +75,8 @@ public static Builder builder() {
return new AutoValue_MapboxNavigationOptions.Builder()
.enableFasterRouteDetection(false)
.enableAutoIncrementLegIndex(true)
.enableRefreshRoute(true)
.refreshIntervalInMilliseconds(ROUTE_REFRESH_INTERVAL)
.defaultMilestonesEnabled(true)
.isFromNavigationUi(false)
.isDebugLoggingEnabled(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ private NavigationConstants() {
*/
static final int NAVIGATION_LOCATION_ENGINE_INTERVAL_LAG = 1500;

static final long ROUTE_REFRESH_INTERVAL = 5 * 60 * 1000L;

/**
* Defines the minimum zoom level of the displayed map.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,8 @@ public NavigationRoute build() {
.voiceInstructions(true)
.bannerInstructions(true)
.roundaboutExits(true)
.eventListener(eventListener);
.eventListener(eventListener)
.enableRefresh(true);
return new NavigationRoute(directionsBuilder.build());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,17 @@ private void process() {
MapboxNavigationOptions options = navigation.options();
DirectionsRoute route = navigation.getRoute();

NavigationStatus status = mapboxNavigator.retrieveStatus(new Date(),
Date date = new Date();
NavigationStatus status = mapboxNavigator.retrieveStatus(date,
options.navigationLocationEngineIntervalLagInMilliseconds());
status = checkForNewLegIndex(mapboxNavigator, route, status, options.enableAutoIncrementLegIndex());
RouteProgress routeProgress = routeProcessor.buildNewRouteProgress(mapboxNavigator, status, route);

RouteRefresher routeRefresher = navigation.retrieveRouteRefresher();
if (routeRefresher != null && routeRefresher.check(date)) {
routeRefresher.refresh(routeProgress);
}

NavigationEngineFactory engineFactory = navigation.retrieveEngineFactory();
final boolean userOffRoute = isUserOffRoute(options, status, rawLocation, routeProgress, engineFactory);
final Location snappedLocation = findSnappedLocation(status, rawLocation, routeProgress, engineFactory);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,77 @@
import com.mapbox.api.directionsrefresh.v1.MapboxDirectionsRefresh;
import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress;

import timber.log.Timber;

/**
* This class allows the developer to interact with the Directions Refresh API, receiving updated
* annotations for a route previously requested with the enableRefresh flag.
*/
public final class RouteRefresh {
private final RefreshCallback refreshCallback;
private RefreshCallback refreshCallback;
private final String accessToken;

/**
* Creates a {@link RouteRefresh} object which calls the {@link RefreshCallback} with updated
* routes.
*
* @param accessToken mapbox access token
* @param accessToken mapbox access token
* @param refreshCallback to call with updated routes
* @deprecated use {@link RouteRefresh(String)} instead
*/
@Deprecated
public RouteRefresh(String accessToken, RefreshCallback refreshCallback) {
this.accessToken = accessToken;
this.refreshCallback = refreshCallback;
}

/**
* Creates a {@link RouteRefresh} object.
*
* @param accessToken mapbox access token
*/
public RouteRefresh(String accessToken) {
this.accessToken = accessToken;
}

/**
* Refreshes the {@link DirectionsRoute} included in the {@link RouteProgress} and returns it
* to the callback that was originally passed in. The client will then have to update their
* {@link DirectionsRoute} with the {@link com.mapbox.api.directions.v5.models.LegAnnotation}s
* returned in this response. The leg annotations start at the current leg index of the
* {@link RouteProgress}
* <p>
* Note that if {@link RefreshCallback} is not passed in {@link RouteRefresh(String, RefreshCallback)} this call
* will be ignored.
* </p>
*
* @param routeProgress to refresh via the route and current leg index
* @deprecated use {@link RouteRefresh#refresh(RouteProgress, RefreshCallback)} instead
*/
@Deprecated
public void refresh(RouteProgress routeProgress) {
refresh(routeProgress.directionsRoute(), routeProgress.legIndex());
if (refreshCallback == null) {
Timber.e("RefreshCallback cannot be null.");
return;
}
refresh(routeProgress.directionsRoute(), routeProgress.legIndex(), refreshCallback);
}

/**
* Refreshes the {@link DirectionsRoute} included in the {@link RouteProgress} and returns it
* to the callback that was originally passed in. The client will then have to update their
* {@link DirectionsRoute} with the {@link com.mapbox.api.directions.v5.models.LegAnnotation}s
* returned in this response. The leg annotations start at the current leg index of the
* {@link RouteProgress}
*
* @param routeProgress to refresh via the route and current leg index
* @param refreshCallback to call with updated routes
*/
public void refresh(RouteProgress routeProgress, RefreshCallback refreshCallback) {
refresh(routeProgress.directionsRoute(), routeProgress.legIndex(), refreshCallback);
}

private void refresh(final DirectionsRoute directionsRoute, final int legIndex) {
private void refresh(final DirectionsRoute directionsRoute, final int legIndex, RefreshCallback refreshCallback) {
MapboxDirectionsRefresh.builder()
.requestId(directionsRoute.routeOptions().requestUuid())
.routeIndex(Integer.valueOf(directionsRoute.routeIndex()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ class RouteRefreshCallback implements Callback<DirectionsRefreshResponse> {
private final int legIndex;
private final RefreshCallback refreshCallback;

RouteRefreshCallback(DirectionsRoute directionsRoute,
int legIndex, RefreshCallback refreshCallback) {
RouteRefreshCallback(DirectionsRoute directionsRoute, int legIndex, RefreshCallback refreshCallback) {
this(new RouteAnnotationUpdater(), directionsRoute, legIndex, refreshCallback);
}

Expand All @@ -31,13 +30,12 @@ public void onResponse(Call<DirectionsRefreshResponse> call, Response<Directions
if (response.body() == null || response.body().route() == null) {
refreshCallback.onError(new RefreshError(response.message()));
} else {
refreshCallback.onRefresh(routeAnnotationUpdater.update(directionsRoute,
response.body().route(), legIndex));
refreshCallback.onRefresh(routeAnnotationUpdater.update(directionsRoute, response.body().route(), legIndex));
}
}

@Override
public void onFailure(Call<DirectionsRefreshResponse> call, Throwable throwable) {
refreshCallback.onError(new RefreshError("There was a network error."));
refreshCallback.onError(new RefreshError(throwable.getMessage()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.mapbox.services.android.navigation.v5.navigation;

import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress;

import java.util.Date;

class RouteRefresher {
private final MapboxNavigation mapboxNavigation;
private final RouteRefresh routeRefresh;
private final long refreshIntervalInMilliseconds;
private Date lastRefreshedDate;
private boolean isChecking;
private boolean isRefreshRouteEnabled;

RouteRefresher(MapboxNavigation mapboxNavigation, RouteRefresh routeRefresh) {
this.mapboxNavigation = mapboxNavigation;
this.routeRefresh = routeRefresh;
this.refreshIntervalInMilliseconds = mapboxNavigation.options().refreshIntervalInMilliseconds();
this.lastRefreshedDate = new Date();
this.isRefreshRouteEnabled = mapboxNavigation.options().enableRefreshRoute();
}

boolean check(Date currentDate) {
if (isChecking || !isRefreshRouteEnabled) {
return false;
}
long millisSinceLastRefresh = currentDate.getTime() - lastRefreshedDate.getTime();
return millisSinceLastRefresh > refreshIntervalInMilliseconds;
}

void refresh(RouteProgress routeProgress) {
updateIsChecking(true);
routeRefresh.refresh(routeProgress, new RouteRefresherCallback(mapboxNavigation, this));
}

void updateLastRefresh(Date date) {
lastRefreshedDate = date;
}

void updateIsChecking(boolean isChecking) {
this.isChecking = isChecking;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.mapbox.services.android.navigation.v5.navigation;

import com.mapbox.api.directions.v5.models.DirectionsRoute;

import java.util.Date;

import timber.log.Timber;

class RouteRefresherCallback implements RefreshCallback {
private final MapboxNavigation mapboxNavigation;
private final RouteRefresher routeRefresher;

RouteRefresherCallback(MapboxNavigation mapboxNavigation, RouteRefresher routeRefresher) {
this.mapboxNavigation = mapboxNavigation;
this.routeRefresher = routeRefresher;
}

@Override
public void onRefresh(DirectionsRoute directionsRoute) {
mapboxNavigation.startNavigation(directionsRoute, DirectionsRouteType.FRESH_ROUTE);
routeRefresher.updateLastRefresh(new Date());
routeRefresher.updateIsChecking(false);
}

@Override
public void onError(RefreshError error) {
Timber.w(error.getMessage());
routeRefresher.updateIsChecking(false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.mapbox.services.android.navigation.v5.navigation;

import com.mapbox.api.directions.v5.models.DirectionsRoute;

import org.junit.Test;

import java.util.Date;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

public class RouteRefresherCallbackTest {

@Test
public void checksStartNavigationWithRefreshedRouteIsCalledWhenOnRefresh() {
MapboxNavigation mockedMapboxNavigation = mock(MapboxNavigation.class);
RouteRefresher mockedRouteRefresher = mock(RouteRefresher.class);
RouteRefresherCallback theRouteRefresherCallback = new RouteRefresherCallback(mockedMapboxNavigation,
mockedRouteRefresher);
DirectionsRoute anyRoute = mock(DirectionsRoute.class);

theRouteRefresherCallback.onRefresh(anyRoute);

verify(mockedMapboxNavigation).startNavigation(eq(anyRoute), eq(DirectionsRouteType.FRESH_ROUTE));
}

@Test
public void checksUpdateLastRefreshDateIsCalledWhenOnRefresh() {
MapboxNavigation mockedMapboxNavigation = mock(MapboxNavigation.class);
RouteRefresher mockedRouteRefresher = mock(RouteRefresher.class);
RouteRefresherCallback theRouteRefresherCallback = new RouteRefresherCallback(mockedMapboxNavigation,
mockedRouteRefresher);
DirectionsRoute anyRoute = mock(DirectionsRoute.class);

theRouteRefresherCallback.onRefresh(anyRoute);

verify(mockedRouteRefresher).updateLastRefresh(any(Date.class));
}

@Test
public void checksUpdateIsNotCheckingAfterOnRefresh() {
MapboxNavigation mockedMapboxNavigation = mock(MapboxNavigation.class);
RouteRefresher mockedRouteRefresher = mock(RouteRefresher.class);
RouteRefresherCallback theRouteRefresherCallback = new RouteRefresherCallback(mockedMapboxNavigation,
mockedRouteRefresher);
DirectionsRoute anyRoute = mock(DirectionsRoute.class);

theRouteRefresherCallback.onRefresh(anyRoute);

verify(mockedRouteRefresher).updateIsChecking(eq(false));
}

@Test
public void checksUpdateIsNotCheckingIfOnError() {
MapboxNavigation mockedMapboxNavigation = mock(MapboxNavigation.class);
RouteRefresher mockedRouteRefresher = mock(RouteRefresher.class);
RouteRefresherCallback theRouteRefresherCallback = new RouteRefresherCallback(mockedMapboxNavigation,
mockedRouteRefresher);
RefreshError anyRefreshError = mock(RefreshError.class);

theRouteRefresherCallback.onError(anyRefreshError);

verify(mockedRouteRefresher).updateIsChecking(eq(false));
}
}
Loading

0 comments on commit b322c74

Please sign in to comment.