6 package com.google.appinventor.components.runtime;
11 import static java.util.Arrays.asList;
13 import android.util.Log;
33 import java.io.BufferedOutputStream;
34 import java.io.IOException;
35 import java.io.InputStreamReader;
36 import java.net.HttpURLConnection;
38 import java.util.List;
39 import org.json.JSONException;
40 import org.osmdroid.util.GeoPoint;
47 @SuppressWarnings(
"TryFinallyCanBeTryWithResources")
48 @DesignerComponent(version = YaVersion.NAVIGATION_COMPONENT_VERSION,
49 category = ComponentCategory.MAPS,
50 description =
"Navigation",
52 iconName =
"images/navigation.png")
53 @UsesPermissions(permissionNames =
"android.permission.INTERNET")
54 @UsesLibraries({
"osmdroid.jar"})
58 private static final String TAG =
"Navigation";
60 public static final String OPEN_ROUTE_SERVICE_URL =
61 "https://api.openrouteservice.org/v2/directions/";
62 private String apiKey;
63 private GeoPoint startLocation;
64 private GeoPoint endLocation;
65 private TransportMethod method;
66 private String serviceUrl = OPEN_ROUTE_SERVICE_URL;
67 private String language =
"en";
70 enum TransportMethod {
71 DEFAULT (
"foot-walking"),
72 DRIVING (
"driving-car"),
73 CYCLING (
"cycling-regular"),
74 WALKING (
"foot-walking"),
75 WHEELCHAIR (
"wheelchair");
77 private final String method;
79 TransportMethod(String method) {
83 private String method() {
94 super(container.
$form());
96 startLocation =
new GeoPoint(0.0, 0.0);
97 endLocation =
new GeoPoint(0.0, 0.0);
98 method = TransportMethod.DEFAULT;
108 @
SimpleFunction(description =
"Request directions from the routing service.")
109 public
void RequestDirections() {
110 if (apiKey.equals(
"")) {
114 final GeoPoint startLocation = this.startLocation;
115 final GeoPoint endLocation = this.endLocation;
116 final TransportMethod method = this.method;
118 @SuppressWarnings(
"TryWithIdenticalCatches")
122 performRequest(startLocation, endLocation, method);
123 }
catch (IOException e) {
124 form.dispatchErrorOccurredEvent(
Navigation.this,
"RequestDirections",
126 }
catch (JSONException je) {
127 form.dispatchErrorOccurredEvent(
Navigation.this,
"RequestDirections",
138 public
void ServiceURL(String url) {
139 this.serviceUrl = url;
150 public
void ApiKey(String key) {
155 defaultValue =
"0.0")
158 if (isValidLatitude(latitude)) {
159 startLocation.setLatitude(latitude);
161 getDispatchDelegate().dispatchErrorOccurredEvent(
this,
"StartLatitude",
167 description =
"The latitude of the start location.")
168 public
double StartLatitude() {
169 return startLocation.getLatitude();
173 defaultValue =
"0.0")
176 if (isValidLongitude(longitude)) {
177 startLocation.setLongitude(longitude);
179 getDispatchDelegate().dispatchErrorOccurredEvent(
this,
"StartLongitude",
185 description =
"The longitude of the start location.")
186 public
double StartLongitude() {
187 return startLocation.getLongitude();
192 GeoPoint point = feature.getCentroid();
193 double latitude = point.getLatitude();
194 double longitude = point.getLongitude();
195 if (!isValidLatitude(latitude)) {
196 getDispatchDelegate().dispatchErrorOccurredEvent(
this,
"SetStartLocation",
198 }
else if (!isValidLongitude(longitude)) {
199 getDispatchDelegate().dispatchErrorOccurredEvent(
this,
"SetStartLocation",
202 startLocation.setCoords(latitude, longitude);
207 defaultValue =
"0.0")
210 if (isValidLatitude(latitude)) {
211 endLocation.setLatitude(latitude);
213 getDispatchDelegate().dispatchErrorOccurredEvent(
this,
"EndLatitude",
219 description =
"The latitude of the end location.")
220 public
double EndLatitude() {
221 return endLocation.getLatitude();
225 defaultValue =
"0.0")
228 if (isValidLongitude(longitude)) {
229 endLocation.setLongitude(longitude);
231 getDispatchDelegate().dispatchErrorOccurredEvent(
this,
"EndLongitude",
237 description =
"The longitude of the end location.")
238 public
double EndLongitude() {
239 return endLocation.getLongitude();
243 public String TransportationMethod() {
244 return method.method();
258 defaultValue =
"foot-walking")
259 @
SimpleProperty(description =
"The transportation method used for determining the route.")
260 public
void TransportationMethod(String method) {
261 for (TransportMethod t : TransportMethod.values()) {
262 if (method.equals(t.method())) {
270 GeoPoint point = feature.getCentroid();
271 double latitude = point.getLatitude();
272 double longitude = point.getLongitude();
273 if (!isValidLatitude(latitude)) {
274 getDispatchDelegate().dispatchErrorOccurredEvent(
this,
"SetEndLocation",
276 }
else if (!isValidLongitude(longitude)) {
277 getDispatchDelegate().dispatchErrorOccurredEvent(
this,
"SetEndLocation",
280 endLocation.setCoords(latitude, longitude);
289 @
SimpleProperty(description =
"The language to use for textual directions.")
291 public
void Language(String language) {
292 this.language = language;
306 @
SimpleProperty(description =
"Content of the last response as a dictionary.")
326 @
SimpleEvent(description =
"Event triggered when the Openrouteservice returns the directions.")
329 Log.d(TAG,
"GotDirections");
335 private void performRequest(GeoPoint start, GeoPoint end, TransportMethod method)
336 throws IOException, JSONException {
337 final String finalURL = serviceUrl + method.method() +
"/geojson/";
338 URL url =
new URL(finalURL);
339 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
340 connection.setDoInput(
true);
341 connection.setDoOutput(
true);
342 connection.setRequestProperty(
"Content-Type",
"application/json; charset=UTF-8");
343 connection.setRequestMethod(
"POST");
344 connection.setRequestProperty(
"Authorization", apiKey);
346 String coords =
"{\"coordinates\": "
349 byte[] postData = coords.getBytes(
"UTF-8");
350 connection.setFixedLengthStreamingMode(postData.length);
351 BufferedOutputStream out =
new BufferedOutputStream(connection.getOutputStream());
353 out.write(postData, 0, postData.length);
359 if (connection.getResponseCode() != 200) {
360 form.dispatchErrorOccurredEvent(
this,
"RequestDirections",
361 ErrorMessages.ERROR_ROUTING_SERVICE_ERROR, connection.getResponseCode(),
362 connection.getResponseMessage());
365 final String geoJson = getResponseContent(connection);
367 final YailDictionary response = (YailDictionary) JsonUtil.getObjectFromJson(geoJson,
true);
368 YailList features = (YailList) response.get(
"features");
369 if (features.size() > 0) {
370 YailDictionary feature = (YailDictionary) features.getObject(0);
371 YailDictionary summary =
372 (YailDictionary) feature.getObjectAtKeyPath(asList(
"properties",
"summary"));
373 final double distance = (Double) summary.get(
"distance");
374 final double duration = (Double) summary.get(
"duration");
375 final YailList directions = YailList.makeList(getDirections(feature));
376 final YailList coordinates = getLineStringCoords(feature);
378 form.runOnUiThread(
new Runnable() {
381 lastResponse = response;
382 GotDirections(directions, coordinates, distance, duration);
387 form.dispatchErrorOccurredEvent(
this,
"RequestDirections",
388 ErrorMessages.ERROR_NO_ROUTE_FOUND);
390 }
catch (Exception e) {
391 form.dispatchErrorOccurredEvent(
this,
"RequestDirections",
392 ErrorMessages.ERROR_UNABLE_TO_REQUEST_DIRECTIONS, e.getMessage());
395 connection.disconnect();
399 private static String getResponseContent(HttpURLConnection connection)
throws IOException {
400 String encoding = connection.getContentEncoding();
401 if (encoding ==
null) {
404 Log.d(TAG, Integer.toString(connection.getResponseCode()));
405 InputStreamReader reader =
new InputStreamReader(connection.getInputStream(), encoding);
407 int contentLength = connection.getContentLength();
408 StringBuilder sb = (contentLength != -1)
409 ?
new StringBuilder(contentLength)
410 :
new StringBuilder();
411 char[] buf =
new char[1024];
413 while ((read = reader.read(buf)) != -1) {
414 sb.append(buf, 0, read);
416 return sb.toString();
422 private Double[][] getCoordinates(GeoPoint startLocation, GeoPoint endLocation) {
423 Double[][] coords =
new Double[2][2];
424 coords[0][0] = startLocation.getLongitude();
425 coords[0][1] = startLocation.getLatitude();
426 coords[1][0] = endLocation.getLongitude();
427 coords[1][1] = endLocation.getLatitude();
431 private YailList getLineStringCoords(YailDictionary feature) {
433 (YailList) feature.getObjectAtKeyPath(asList(
"geometry",
"coordinates"));
434 return GeoJSONUtil.swapCoordinates(coords);
437 private List<?> getDirections(YailDictionary feature) {
438 return YailDictionary.walkKeyPath(feature,
439 asList(
"properties",
"segments", ALL,
"steps", ALL,
"instruction"));