diff --git a/build.gradle b/build.gradle index bb1bb44d8..34e2a1133 100644 --- a/build.gradle +++ b/build.gradle @@ -65,18 +65,19 @@ subprojects { } def TESTABLE_MODULES = ["services", - "services-core", - "services-directions", - "services-geocoding", - "services-geojson", - "services-matching", - "services-matrix", - "services-optimization", - "services-route-tiles", - "services-speech", - "services-staticmap", - "services-tilequery", - "services-turf"] + "services-core", + "services-directions", + "services-geocoding", + "services-geojson", + "services-matching", + "services-matrix", + "services-optimization", + "services-route-tiles", + "services-speech", + "services-staticmap", + "services-tilequery", + "services-turf", + "services-directions-refresh"] def RELEASE_MODULES = ["services", "services-core", @@ -85,9 +86,9 @@ def RELEASE_MODULES = ["services", subprojects { subproject -> if (TESTABLE_MODULES.contains(subproject.name)) { - subproject.apply plugin: "com.vanniktech.android.junit.jacoco" - subproject.apply from: "${rootDir}/gradle/jacoco.gradle" - subproject.apply from: "${rootDir}/gradle/checkstyle.gradle" + subproject.apply plugin: "com.vanniktech.android.junit.jacoco" + subproject.apply from: "${rootDir}/gradle/jacoco.gradle" + subproject.apply from: "${rootDir}/gradle/checkstyle.gradle" } if (RELEASE_MODULES.contains(subproject.name)) { diff --git a/samples/build.gradle b/samples/build.gradle index 9c2fdaf23..805ed4410 100644 --- a/samples/build.gradle +++ b/samples/build.gradle @@ -24,6 +24,7 @@ dependencies { implementation project(":services-staticmap") implementation project(":services-speech") implementation project(":services-tilequery") + implementation project(":services-directions-refresh") } buildConfig { diff --git a/samples/src/main/java/com/mapbox/samples/BasicDirectionsRefresh.java b/samples/src/main/java/com/mapbox/samples/BasicDirectionsRefresh.java new file mode 100644 index 000000000..c74063163 --- /dev/null +++ b/samples/src/main/java/com/mapbox/samples/BasicDirectionsRefresh.java @@ -0,0 +1,65 @@ +package com.mapbox.samples; + +import com.mapbox.api.directions.v5.MapboxDirections; +import com.mapbox.api.directions.v5.models.DirectionsResponse; +import com.mapbox.api.directionsrefresh.v1.MapboxDirectionsRefresh; +import com.mapbox.api.directionsrefresh.v1.models.DirectionsRefreshResponse; +import com.mapbox.geojson.Point; +import com.mapbox.sample.BuildConfig; + +import java.io.IOException; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +import static com.mapbox.api.directions.v5.DirectionsCriteria.ANNOTATION_CONGESTION; +import static com.mapbox.api.directions.v5.DirectionsCriteria.OVERVIEW_FULL; +import static com.mapbox.api.directions.v5.DirectionsCriteria.PROFILE_DRIVING_TRAFFIC; + +public class BasicDirectionsRefresh { + public static void main(String[] args) throws IOException { + String requestId = simpleMapboxDirectionsRequest(); + simpleMapboxDirectionsRefreshRequest(requestId); + } + + private static String simpleMapboxDirectionsRequest() throws IOException { + MapboxDirections directions = MapboxDirections.builder() + .accessToken(BuildConfig.MAPBOX_ACCESS_TOKEN) + .enableRefresh(true) + .origin(Point.fromLngLat(-95.6332, 29.7890)) + .destination(Point.fromLngLat(-95.3591, 29.7576)) + .overview(OVERVIEW_FULL) + .profile(PROFILE_DRIVING_TRAFFIC) + .annotations(ANNOTATION_CONGESTION).build(); + + Response response = directions.executeCall(); + System.out.println("Directions response: " + response); + String requestId = response.body().routes().get(0).routeOptions().requestUuid(); + + return requestId; + } + + private static void simpleMapboxDirectionsRefreshRequest(String requestId) { + MapboxDirectionsRefresh refresh = + MapboxDirectionsRefresh.builder() + .accessToken(BuildConfig.MAPBOX_ACCESS_TOKEN) + .requestId(requestId) + .routeIndex(0) + .legIndex(0) + .build(); + + refresh.enqueueCall(new Callback() { + @Override + public void onResponse(Call call, Response response) { + System.out.println("Refresh response: " + response); + } + + @Override + public void onFailure(Call call, Throwable throwable) { + System.out.println("" + call.request().url()); + System.out.printf("Failure: " + throwable); + } + }); + } +} diff --git a/services-directions-refresh/.gitignore b/services-directions-refresh/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/services-directions-refresh/.gitignore @@ -0,0 +1 @@ +/build diff --git a/services-directions-refresh/build.gradle b/services-directions-refresh/build.gradle new file mode 100644 index 000000000..cbeb4e439 --- /dev/null +++ b/services-directions-refresh/build.gradle @@ -0,0 +1,19 @@ +apply plugin: 'java-library' + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + api project(":services-core") + + // Annotations + compileOnly dependenciesList.supportAnnotation + + // AutoValue + compileOnly dependenciesList.autoValue + compileOnly dependenciesList.autoValueGson + implementation project(":services") + + // Test Dependencies + testImplementation dependenciesList.okhttp3Mockwebserver + testImplementation project(path: ':services-core', configuration: 'testOutput') +} + diff --git a/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/DirectionsRefreshService.java b/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/DirectionsRefreshService.java new file mode 100644 index 000000000..32613e158 --- /dev/null +++ b/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/DirectionsRefreshService.java @@ -0,0 +1,39 @@ +package com.mapbox.api.directionsrefresh.v1; + +import com.mapbox.api.directionsrefresh.v1.models.DirectionsRefreshResponse; + +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Header; +import retrofit2.http.Path; +import retrofit2.http.Query; + +/** + * Interface that defines the directions refresh service. This corresponds to v1 of the + * directions API, specifically driving directions. + * + * @since 4.4.0 + */ +public interface DirectionsRefreshService { + + /** + * Constructs the html call using the information passed in through the + * {@link MapboxDirectionsRefresh.Builder}. + * + * @param userAgent the user agent + * @param requestId a uuid specifying the request containing the route being refreshed + * @param routeIndex the index of the specified route + * @param legIndex the index of the leg to start the refresh response (inclusive) + * @param accessToken Mapbox access token + * @return the {@link DirectionsRefreshResponse} in a Call wrapper + * @since 4.4.0 + */ + @GET("directions-refresh/v1/mapbox/driving-traffic/{request_id}/{route_index}/{leg_index}") + Call getCall( + @Header("User-Agent") String userAgent, + @Path("request_id") String requestId, + @Path("route_index") int routeIndex, + @Path("leg_index") int legIndex, + @Query("access_token") String accessToken + ); +} diff --git a/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/MapboxDirectionsRefresh.java b/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/MapboxDirectionsRefresh.java new file mode 100644 index 000000000..fe0449d65 --- /dev/null +++ b/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/MapboxDirectionsRefresh.java @@ -0,0 +1,165 @@ +package com.mapbox.api.directionsrefresh.v1; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.google.auto.value.AutoValue; +import com.google.gson.GsonBuilder; +import com.mapbox.api.directions.v5.DirectionsAdapterFactory; +import com.mapbox.api.directionsrefresh.v1.models.DirectionsRefreshAdapterFactory; +import com.mapbox.api.directionsrefresh.v1.models.DirectionsRefreshResponse; +import com.mapbox.core.MapboxService; +import com.mapbox.core.constants.Constants; +import com.mapbox.core.utils.ApiCallHelper; + +import retrofit2.Call; + +/** + * The directions refresh API allows a route to be refreshed via it's annotations. The + * refreshEnabled parameter would have had to have been specified as true in the original + * directions request for a refresh to be possible. + * + * @since 4.4.0 + */ +@AutoValue +public abstract class MapboxDirectionsRefresh extends MapboxService { + + private static final int ZERO = 0; + + protected MapboxDirectionsRefresh() { + super(DirectionsRefreshService.class); + } + + @Override + protected Call initializeCall() { + return getService().getCall( + ApiCallHelper.getHeaderUserAgent(clientAppName()), + requestId(), + routeIndex(), + legIndex(), + accessToken() + ); + } + + abstract String requestId(); + + abstract int routeIndex(); + + abstract int legIndex(); + + abstract String accessToken(); + + @Nullable + abstract String clientAppName(); + + @NonNull + @Override + protected abstract String baseUrl(); + + @Override + protected GsonBuilder getGsonBuilder() { + return super.getGsonBuilder() + .registerTypeAdapterFactory(DirectionsRefreshAdapterFactory.create()) + .registerTypeAdapterFactory(DirectionsAdapterFactory.create()); + } + + /** + * Convert the current {@link MapboxDirectionsRefresh} to its builder holding the currently + * assigned values. This allows you to modify a single property and then rebuild the object + * resulting in an updated and modified {@link MapboxDirectionsRefresh}. + * + * @return a {@link MapboxDirectionsRefresh.Builder} with the same values + * @since 4.4.0 + */ + public abstract Builder toBuilder(); + + /** + * Build a new {@link MapboxDirectionsRefresh} builder with default initial values. + * + * @return a {@link Builder} for creating a default {@link MapboxDirectionsRefresh} + * @since 4.4.0 + */ + public static Builder builder() { + return new AutoValue_MapboxDirectionsRefresh.Builder() + .baseUrl(Constants.BASE_API_URL) + .routeIndex(ZERO) + .legIndex(ZERO); + } + + /** + * This builder is used to build a new request to the Mapbox Directions Refresh API. A request + * requires an access token and a request id. + * + * @since 4.4.0 + */ + @AutoValue.Builder + public abstract static class Builder { + + /** + * Specified here is the uuid of the initial directions request. The original request must + * have specified enableRefresh. + * + * @param requestId id of the original directions request. This is found in the + * {@link com.mapbox.api.directions.v5.models.RouteOptions} object. + * @return this builder + * @since 4.4.0 + */ + public abstract Builder requestId(String requestId); + + /** + * Index of original route in response. + * + * @param routeIndex index of route in response + * @return this builder + * @since 4.4.0 + */ + public abstract Builder routeIndex(@NonNull int routeIndex); + + /** + * Index of leg of which to start. The response will include the annotations of the specified + * leg through the end of the list of legs. + * + * @param legIndex index of leg + * @return this builder + * @since 4.4.0 + */ + public abstract Builder legIndex(@NonNull int legIndex); + + /** + * Required to call when this is being built. If no access token provided, + * {@link com.mapbox.core.exceptions.ServicesException} will be thrown. + * + * @param accessToken Mapbox access token + * @since 4.4.0 + */ + public abstract Builder accessToken(@NonNull String accessToken); + + /** + * Base package name or other simple string identifier. Used inside the calls user agent header. + * + * @param clientAppName base package name or other simple string identifier + * @return this builder for chaining options together + * @since 4.4.0 + */ + public abstract Builder clientAppName(@NonNull String clientAppName); + + /** + * Optionally change the APIs base URL to something other then the default Mapbox one. + * + * @param baseUrl base url used as endpoint + * @return this builder + * @since 4.4.0 + */ + public abstract Builder baseUrl(String baseUrl); + + /** + * Returns an instance of {@link MapboxDirectionsRefresh} for interacting with the endpoint + * with the specified values. + * + * @return instance of {@link MapboxDirectionsRefresh} with specified attributes + * @since 4.4.0 + */ + public abstract MapboxDirectionsRefresh build(); + } +} diff --git a/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/models/DirectionsRefreshAdapterFactory.java b/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/models/DirectionsRefreshAdapterFactory.java new file mode 100644 index 000000000..7c07ccd2a --- /dev/null +++ b/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/models/DirectionsRefreshAdapterFactory.java @@ -0,0 +1,24 @@ +package com.mapbox.api.directionsrefresh.v1.models; + +import com.google.gson.TypeAdapterFactory; +import com.ryanharter.auto.value.gson.GsonTypeAdapterFactory; + +/** + * Required so that AutoValue can generate specific type adapters when needed inside the + * directions refresh package. + * + * @since 4.4.0 + */ +@GsonTypeAdapterFactory +public abstract class DirectionsRefreshAdapterFactory implements TypeAdapterFactory { + + /** + * Creates a TypeAdapter that AutoValue uses to generate specific type adapters. + * + * @return a {@link TypeAdapterFactory} to deserialize {@link DirectionsRefreshResponse} + * @since 4.4.0 + */ + public static TypeAdapterFactory create() { + return new AutoValueGson_DirectionsRefreshAdapterFactory(); + } +} diff --git a/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/models/DirectionsRefreshResponse.java b/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/models/DirectionsRefreshResponse.java new file mode 100644 index 000000000..f71eefda3 --- /dev/null +++ b/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/models/DirectionsRefreshResponse.java @@ -0,0 +1,144 @@ +package com.mapbox.api.directionsrefresh.v1.models; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.google.auto.value.AutoValue; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.mapbox.api.directions.v5.DirectionsAdapterFactory; +import com.mapbox.api.directions.v5.models.DirectionsJsonObject; +import com.mapbox.api.directions.v5.models.DirectionsRoute; + +/** + * Response object for Directions Refresh requests. + * + * @since 4.4.0 + */ +@AutoValue +public abstract class DirectionsRefreshResponse extends DirectionsJsonObject { + + /** + * String indicating the state of the response. This is a separate code than the HTTP status code. + * On normal valid responses, the value will be Ok. The possible responses are listed below: + *
    + *
  • Ok:200 Normal success case
  • + *
  • NoRoute: 200 There was no route found for the given coordinates. Check + * for impossible routes (e.g. routes over oceans without ferry connections).
  • + *
  • NoSegment: 200 No road segment could be matched for coordinates. Check for + * coordinates too far away from a road.
  • + *
  • ProfileNotFound: 404 Use a valid profile as described above
  • + *
  • InvalidInput: 422
  • + *
+ * + * @return a string with one of the given values described in the list above + * @since 4.4.0 + */ + @NonNull + public abstract String code(); + + /** + * Optionally shows up in a response if an error or something unexpected occurred. + * + * @return a string containing the message + * @since 4.4.0 + */ + @Nullable + public abstract String message(); + + /** + * Barebones {@link DirectionsRoute} which only contains a list of + * {@link com.mapbox.api.directions.v5.models.RouteLeg}s, which only contain lists of the + * refreshed annotations. + * + * @return barebones route with annotation data + * @since 4.4.0 + */ + @Nullable + public abstract DirectionsRoute route(); + + /** + * Create a new instance of this class by using the {@link Builder} class. + * + * @return this classes {@link Builder} for creating a new instance + * @since 4.4.0 + */ + @NonNull + public static Builder builder() { + return new AutoValue_DirectionsRefreshResponse.Builder(); + } + + /** + * Gson type adapter for parsing Gson to this class. + * + * @param gson the built {@link Gson} object + * @return the type adapter for this class + * @since 4.4.0 + */ + public static TypeAdapter typeAdapter(Gson gson) { + return new AutoValue_DirectionsRefreshResponse.GsonTypeAdapter(gson); + } + + /** + * Create a new instance of this class by passing in a formatted valid JSON String. + * + * @param json a formatted valid JSON string defining a Directions Refresh response + * @return a new instance of this class defined by the values passed inside this static factory + * method + * @since 4.4.0 + */ + public static DirectionsRefreshResponse fromJson(String json) { + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.registerTypeAdapterFactory(DirectionsRefreshAdapterFactory.create()) + .registerTypeAdapterFactory(DirectionsAdapterFactory.create()); + return gsonBuilder.create().fromJson(json, DirectionsRefreshResponse.class); + } + + /** + * This builder can be used to set the values describing the {@link DirectionsRefreshResponse}. + * @since 4.4.0 + */ + @AutoValue.Builder + public abstract static class Builder { + + /** + * String indicating the state of the response. This is a separate code than the HTTP status + * code. On normal valid responses, the value will be Ok. For a full list of possible responses, + * see {@link DirectionsRefreshResponse#code()}. + * + * @param code a string with one of the given values described in the list above + * @return this builder + * @since 4.4.0 + */ + public abstract Builder code(String code); + + /** + * Optionally shows up in a response if an error or something unexpected occurred. + * + * @param message a string containing the message + * @return this builder + * @since 4.4.0 + */ + public abstract Builder message(String message); + + /** + * Barebones {@link DirectionsRoute} which only contains a list of + * {@link com.mapbox.api.directions.v5.models.RouteLeg}s, which only contain lists of the + * refreshed annotations. + * + * @param directionsRoute route containing annotation data + * @return this builder + * @since 4.4.0 + */ + public abstract Builder route(DirectionsRoute directionsRoute); + + /** + * Builds a new {@link DirectionsRefreshResponse} object. + * + * @return a new {@link DirectionsRefreshResponse} object + * @since 4.4.0 + */ + public abstract DirectionsRefreshResponse build(); + } +} diff --git a/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/models/package-info.java b/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/models/package-info.java new file mode 100644 index 000000000..1eaad7d11 --- /dev/null +++ b/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/models/package-info.java @@ -0,0 +1,6 @@ +/** + * Contains the model classes which represent the Directions Refresh API response. + * + * @since 4.4.0 + */ +package com.mapbox.api.directionsrefresh.v1.models; diff --git a/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/package-info.java b/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/package-info.java new file mode 100644 index 000000000..bcc296a60 --- /dev/null +++ b/services-directions-refresh/src/main/java/com/mapbox/api/directionsrefresh/v1/package-info.java @@ -0,0 +1,6 @@ +/** + * Contains classes for accessing the Mapbox Directions Refresh API. + * + * @since 4.4.0 + */ +package com.mapbox.api.directionsrefresh.v1; diff --git a/services-directions-refresh/src/test/java/com/mapbox/api/directionsrefresh/v1/MapboxDirectionsRefreshTest.java b/services-directions-refresh/src/test/java/com/mapbox/api/directionsrefresh/v1/MapboxDirectionsRefreshTest.java new file mode 100644 index 000000000..624547f39 --- /dev/null +++ b/services-directions-refresh/src/test/java/com/mapbox/api/directionsrefresh/v1/MapboxDirectionsRefreshTest.java @@ -0,0 +1,102 @@ +package com.mapbox.api.directionsrefresh.v1; + +import com.mapbox.api.directionsrefresh.v1.models.DirectionsRefreshResponse; +import com.mapbox.core.TestUtils; + +import org.hamcrest.junit.ExpectedException; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; + +import okhttp3.HttpUrl; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import retrofit2.Response; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class MapboxDirectionsRefreshTest extends TestUtils { + + private static final String DIRECTIONS_REFRESH_V1_FIXTURE = "directions_refresh_v1.json"; + + private MockWebServer server; + private HttpUrl mockUrl; + + @Before + public void setUp() throws IOException { + server = new MockWebServer(); + + server.setDispatcher(new okhttp3.mockwebserver.Dispatcher() { + @Override + public MockResponse dispatch(RecordedRequest request) throws InterruptedException { + + String resource = DIRECTIONS_REFRESH_V1_FIXTURE; + + try { + String body = loadJsonFixture(resource); + return new MockResponse().setBody(body); + } catch (IOException ioException) { + throw new RuntimeException(ioException); + } + } + }); + server.start(); + mockUrl = server.url(""); + } + + @After + public void tearDown() throws IOException { + server.shutdown(); + } + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void sanity() throws Exception { + MapboxDirectionsRefresh mapboxDirectionsRefresh = MapboxDirectionsRefresh.builder() + .accessToken(ACCESS_TOKEN) + .requestId("") + .build(); + + Assert.assertNotNull(mapboxDirectionsRefresh); + } + + @Test + public void testResponse() throws IOException { + MapboxDirectionsRefresh mapboxDirectionsRefresh = MapboxDirectionsRefresh.builder() + .accessToken(ACCESS_TOKEN) + .requestId("") + .baseUrl(mockUrl.toString()) + .build(); + + Response response = mapboxDirectionsRefresh.executeCall(); + DirectionsRefreshResponse directionsRefreshResponse = response.body(); + + assertEquals(200, response.code()); + assertEquals("Ok", directionsRefreshResponse.code()); + assertNotNull(directionsRefreshResponse.route().legs()); + assertNotNull(directionsRefreshResponse.route().legs().get(0).annotation()); + } + + @Test + public void testRoute() throws IOException { + MapboxDirectionsRefresh mapboxDirectionsRefresh = MapboxDirectionsRefresh.builder() + .accessToken(ACCESS_TOKEN) + .requestId("") + .baseUrl(mockUrl.toString()) + .build(); + + Response response = mapboxDirectionsRefresh.executeCall(); + DirectionsRefreshResponse directionsRefreshResponse = response.body(); + + assertNotNull(directionsRefreshResponse.route().legs()); + assertNotNull(directionsRefreshResponse.route().legs().get(0).annotation()); + } +} diff --git a/services-directions-refresh/src/test/resources/directions_refresh_v1.json b/services-directions-refresh/src/test/resources/directions_refresh_v1.json new file mode 100644 index 000000000..94b8e2942 --- /dev/null +++ b/services-directions-refresh/src/test/resources/directions_refresh_v1.json @@ -0,0 +1 @@ +{"code":"Ok","route":{"legs":[{"annotation":{"duration":[4.3,1.4,1.6,1.1,1.1,2,1.5,2.9,2.2,3.9,2.5,1.7,1.8,0.4,2.4,4.6,1.1,4.1,0.7,1.1,5.4,4.8,2.6,3.5,2.8,1.4,1.2,0.2,3.5,0.2,0.4,1.2,0.7,0.7,0.6,0.7,0.8,0.5,0.8,0.7,0.6,0.9,0.8,0.8,0.8,0.4,1.4,2,1.9,2.6,2.9,2.9,2.6,2.3,2.4,3.1,2.4,3,2.7,1.1,2.4,2,0.6,0.2,1.9,2.1,1.3,0.9,0.6,0.9,6.4,2.7,2.6,2.2,0.6,0.7,0.9,0.5,0.5,0.6,1.7,2.1,1.9,1.1,2,1.6,0.4,3.5,1,1.1,1.6,1.7,1.3,1,3.8,3.1,1.4,1.3,2.4,1.8,1.4,1.3,1.3,1.6,2.4,2.3,3.1,1.8,1.7,1.7,2.1,1.9,2.2,2.1,2.6,7.7,2.5,2.1,0.8,1,0.6,2.1,2,2.1,1.7,1.2,5.3,2.2,3.2,3.9,3.4,2.5,2,2,1.9,2.4,1.8,1.4,1.6,1.4,1.1,1.5,1.5,3.4,2.2,3.3,1,0.9,1.9,79.6,11,2.1,0.8,1.6,2.4,2.1,9.9,12.6,12.8,5.2,2.8,3.2,1.7,2.5,3.1,2.1,0.7,17.2,1.1,0.4,0.1,1,0.6,0.2,1.2,1,1.1,5.2,3.7,7.7,1.6,9.9,5,1,0.7,2.8,14.2,7.4,4.6,2.5,2.3,3.6,3.7,3.6,12.5,1.1,0.9,1.2,1.7,2.5,1.9,0.8,1.8,2,0.6,0.8,1.9,1.4,3.2,1.8,2.1,0.9,0.7,0.5,0.3,2.5,2,0.6,0.8,1,2.3,1,2,2.3,2.1,1.3,1.2,2.4,0.9,1.7,1.8,0.3,1.1,2.5,0.3,0.5,0.4,0.4,0.2,0.1,0.6,0.6,0.9,0.7,0.5,0.7,0.5,0.2,0.2,0.3,0.2,0.5,0.7,0.5,0.2,0.3,0.5,0.3,0.3,0.3,0.3,0.3,0.2,0.2,0.4,0.6,0.6,2.3,0.9,0.7,0.2,0.4,0.3,0.4,0.7,0.3,0.3,0.2,0.2,0.3,0.2,0.6,0.2,0.4,0.3,0.4,0.3,0.5,0.6,0.3,1.1,0.3,0.3,0.4,0.2,0.3,0.3,0.2,0.3,0.1,0.4,0.4,0.2,0.4,0.2,1,1,1.2,0.8,1.4,1.7,2.2,2.8,2.2,3.8,1.5,1.3,1.5,0.9,1.1,0.9,1.2,0.6,1.6,3.6,2.2,2.1,2.2,3,2.1,1.9,1.3,1.5,1,1.2,0.8,0.7,0.7,1.1,1.4,1,0.6,0.9,0.9,1.6,0.9,0.3,0.3,0.5,0.5,0.6,0.8,0.6,0.6,0.7,0.9,1.5,0.6,1.5,0.5,0.7,0.5,0.6,0.7,1.5,1.6,1,0.2,0.4,0.6,0.4,0.2,0.2,0.4,0.4,0.4,0.3,1.2,0.5,0.5,0.5,0.3,0.5,1.1,0.3,0.3,0.3,0.5,0.9,1.5,1.4,1.9,1.3,1.4,1.2,0.9,0.8,1.2,1.3,1.8,0.8,0.8,1.9,1.6,1.5,2.1,1.1,0.6,1.7,1.3,1.6,0.9,0.3,0.6,0.3,0.3,0.6,0.6,0.6,0.6,0.6,1.8,0.6,0.6,0.4,0.8,0.4,0.8,0.8,1.3,1,1,1,1,0.5,0.5,1.6,1.1,0.7,0.5,0.5,0.5,0.5,0.5,5.3,2.4,10.9,2.4,5.7,16.2,7.7,6.7],"congestion":["severe","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","heavy","heavy","heavy","heavy","heavy","heavy","heavy","heavy","heavy","heavy","heavy","heavy","heavy","heavy","heavy","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","low","low","low","heavy","heavy","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","heavy","heavy","heavy","low","low","heavy","heavy","heavy","heavy","heavy","heavy","heavy","heavy","low","low","low","low","low","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","moderate","unknown","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","moderate","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","moderate","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low","low"],"maxspeed":[{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"speed":30,"unit":"mph"},{"speed":30,"unit":"mph"},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"unknown":true},{"unknown":true},{"unknown":true},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"speed":35,"unit":"mph"},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"unknown":true},{"speed":30,"unit":"mph"}]}}]}} \ No newline at end of file diff --git a/services-directions/src/main/java/com/mapbox/api/directions/v5/DirectionsService.java b/services-directions/src/main/java/com/mapbox/api/directions/v5/DirectionsService.java index 811c1956d..718c451b6 100644 --- a/services-directions/src/main/java/com/mapbox/api/directions/v5/DirectionsService.java +++ b/services-directions/src/main/java/com/mapbox/api/directions/v5/DirectionsService.java @@ -51,6 +51,7 @@ public interface DirectionsService { * Note: coordinate indices not added here act as silent waypoints * @param waypointNames custom names for waypoints used for the arrival instruction * @param waypointTargets list of coordinate pairs for drop-off locations + * @param enableRefresh whether the routes should be refreshable * @return the {@link DirectionsResponse} in a Call wrapper * @since 1.0.0 */ @@ -78,6 +79,7 @@ Call getCall( @Query("approaches") String approaches, @Query("waypoints") String waypointIndices, @Query("waypoint_names") String waypointNames, - @Query("waypoint_targets") String waypointTargets + @Query("waypoint_targets") String waypointTargets, + @Query("enable_refresh") Boolean enableRefresh ); } diff --git a/services-directions/src/main/java/com/mapbox/api/directions/v5/MapboxDirections.java b/services-directions/src/main/java/com/mapbox/api/directions/v5/MapboxDirections.java index 67a4fffe7..568393e1b 100644 --- a/services-directions/src/main/java/com/mapbox/api/directions/v5/MapboxDirections.java +++ b/services-directions/src/main/java/com/mapbox/api/directions/v5/MapboxDirections.java @@ -86,7 +86,8 @@ protected Call initializeCall() { approaches(), waypointIndices(), waypointNames(), - waypointTargets()); + waypointTargets(), + enableRefresh()); } @Override @@ -266,6 +267,9 @@ private static String formatWaypointTargets(Point[] waypointTargets) { @Nullable abstract String waypointTargets(); + @Nullable + abstract Boolean enableRefresh(); + @Nullable abstract Interceptor interceptor(); @@ -745,6 +749,15 @@ public Builder addWaypointTargets(@Nullable Point... waypointTargets) { abstract Builder waypointTargets(@Nullable String waypointTargets); + /** + * Whether the routes should be refreshable via the directions refresh API. + * + * @param enableRefresh whether the routes should be refreshable + * @return this builder + * @since 4.4.0 + */ + public abstract Builder enableRefresh(Boolean enableRefresh); + abstract MapboxDirections autoBuild(); /** diff --git a/services-directions/src/main/java/com/mapbox/api/directions/v5/models/DirectionsResponse.java b/services-directions/src/main/java/com/mapbox/api/directions/v5/models/DirectionsResponse.java index 43038d7b3..f7b9f18ce 100644 --- a/services-directions/src/main/java/com/mapbox/api/directions/v5/models/DirectionsResponse.java +++ b/services-directions/src/main/java/com/mapbox/api/directions/v5/models/DirectionsResponse.java @@ -2,6 +2,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; + import com.google.auto.value.AutoValue; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -185,6 +186,8 @@ public abstract static class Builder { */ public abstract Builder routes(@NonNull List routes); + abstract List routes(); + /** * A universally unique identifier (UUID) for identifying and executing a similar specific route * in the future. @@ -195,12 +198,20 @@ public abstract static class Builder { */ public abstract Builder uuid(@Nullable String uuid); + abstract DirectionsResponse autoBuild(); + /** * Build a new {@link DirectionsResponse} object. * * @return a new {@link DirectionsResponse} using the provided values in this builder * @since 3.0.0 */ - public abstract DirectionsResponse build(); + public DirectionsResponse build() { + for (int i = 0; i < routes().size(); i++) { + routes().set(i, routes().get(i).toBuilder().routeIndex(String.valueOf(i)).build()); + } + + return autoBuild(); + } } } diff --git a/services-directions/src/main/java/com/mapbox/api/directions/v5/models/DirectionsRoute.java b/services-directions/src/main/java/com/mapbox/api/directions/v5/models/DirectionsRoute.java index f0fbdaace..7ed1886bb 100644 --- a/services-directions/src/main/java/com/mapbox/api/directions/v5/models/DirectionsRoute.java +++ b/services-directions/src/main/java/com/mapbox/api/directions/v5/models/DirectionsRoute.java @@ -32,6 +32,15 @@ public static Builder builder() { return new AutoValue_DirectionsRoute.Builder(); } + /** + * The index of this route in the original network response. + * + * @return string of an int value representing the index + * @since 4.4.0 + */ + @Nullable + public abstract String routeIndex(); + /** * The distance traveled from origin to destination. * @@ -234,6 +243,8 @@ public abstract static class Builder { */ public abstract Builder voiceLanguage(@Nullable String voiceLanguage); + abstract Builder routeIndex(String routeIndex); + /** * Build a new {@link DirectionsRoute} object. * diff --git a/services/build.gradle b/services/build.gradle index 829f2b89e..a8f860bbb 100644 --- a/services/build.gradle +++ b/services/build.gradle @@ -5,7 +5,7 @@ sourceSets { '../services-matching/src/main/java', '../services-matrix/src/main/java', '../services-optimization/src/main/java', '../services-staticmap/src/main/java', '../services-speech/src/main/java', '../services-tilequery/src/main/java', - '../services-route-tiles/src/main/java'] + '../services-route-tiles/src/main/java', './services-directions-refresh/src/main/java'] } dependencies { diff --git a/settings.gradle b/settings.gradle index 75cf04a36..d55f65f81 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,4 +13,5 @@ include ':services-matching' include ':services-staticmap' include ':services-tilequery' include ':services-route-tiles' +include ':services-directions-refresh' include 'samples'