package com.circlegate.liban.ws; import android.net.Uri; import android.os.SystemClock; import android.text.TextUtils; import com.circlegate.liban.R; import com.circlegate.liban.base.ApiBase.ApiCreator; import com.circlegate.liban.base.ApiDataIO.ApiDataInput; import com.circlegate.liban.base.ApiDataIO.ApiDataOutput; import com.circlegate.liban.base.CommonClasses.Couple; import com.circlegate.liban.location.LocPoint; import com.circlegate.liban.task.TaskErrors.BaseError; import com.circlegate.liban.task.TaskErrors.ITaskError; import com.circlegate.liban.task.TaskErrors.TaskError; import com.circlegate.liban.task.TaskErrors.TaskException; import com.circlegate.liban.task.TaskInterfaces.ITask; import com.circlegate.liban.task.TaskInterfaces.ITaskContext; import com.circlegate.liban.utils.EqualsUtils; import com.circlegate.liban.utils.LogUtils; import com.circlegate.liban.ws.WsBase.IWsParam; import com.circlegate.liban.ws.WsBase.IWsResult; import com.circlegate.liban.ws.WsBase.WsParam; import com.circlegate.liban.ws.WsBase.WsResult; import com.google.common.collect.ImmutableList; import org.joda.time.Duration; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import okhttp3.Request.Builder; import okhttp3.Response; public class WsGoogleApis { public static class GoogleUtils { public static Uri.Builder createUriBuilder(ITaskContext context, boolean https, String subPath, String optKey) { Uri.Builder uri = new Uri.Builder() .scheme(https ? "https" : "http") .authority("maps.googleapis.com") .path("maps/api/" + subPath + "/json") .appendQueryParameter("sensor", "true") .appendQueryParameter("language", context.getCurrentLangAbbrev()); if (!TextUtils.isEmpty(optKey)) uri.appendQueryParameter("key", optKey); return uri; } public static String encodeUriLocation(LocPoint l) { return l.getLatitudeString() + "," + l.getLongitudeString(); } public static LocPoint parseLocation(JSONObject location) throws JSONException { return new LocPoint(location.getDouble("lat"), location.getDouble("lng")); } public static ImmutableList<LocPoint> decodePolyline(String encoded) { ImmutableList.Builder<LocPoint> poly = ImmutableList.builder(); int index = 0, len = encoded.length(); int lat = 0, lng = 0; while (index < len) { int b, shift = 0, result = 0; do { b = encoded.charAt(index++) - 63; result |= (b & 0x1f) << shift; shift += 5; } while (b >= 0x20); int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); lat += dlat; shift = 0; result = 0; do { b = encoded.charAt(index++) - 63; result |= (b & 0x1f) << shift; shift += 5; } while (b >= 0x20); int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); lng += dlng; LocPoint p = new LocPoint((double)lat / 1E5, (double)lng / 1E5); poly.add(p); } return poly.build(); } public static String encodePolyline(Collection<? extends LocPoint> points) { StringBuilder str = new StringBuilder(); int lastLat = 0; int lastLng = 0; for (LocPoint point : points) { int lat = (point.getLatitudeE6() + 5) / 10; int lng = (point.getLongitudeE6() + 5) / 10; int diff = lat - lastLat; { int shifted = diff << 1; if (diff < 0) shifted = ~shifted; int rem = shifted; while (rem >= 0x20) { str.append((char) ((0x20 | (rem & 0x1f)) + 63)); rem >>= 5; } str.append((char) (rem + 63)); } diff = lng - lastLng; { // Stejny blok jako ten vyse - jenom kvuli tomu, aby cely kod byl // v jedine funkci int shifted = diff << 1; if (diff < 0) shifted = ~shifted; int rem = shifted; while (rem >= 0x20) { str.append((char) ((0x20 | (rem & 0x1f)) + 63)); rem >>= 5; } str.append((char) (rem + 63)); } lastLat = lat; lastLng = lng; } return str.toString(); } public static Couple<String, String> generateNameDesc(String formattedAddress) { String d = formattedAddress; int commaIndex = d.indexOf(','); String name = commaIndex < 0 ? d : d.substring(0, commaIndex); String desc = commaIndex < 0 ? "" : d.substring(commaIndex + 1).trim(); desc = desc.replace("Česká republika", "CZ"); desc = desc.replace("Slovensko", "SK"); desc = desc.replaceFirst("\\d\\d\\d \\d\\d (.*)", "$1"); return new Couple<String, String>(name, desc); } } public static class GoogleError extends TaskError { public static final GoogleError ZERO_RESULTS = new GoogleError("ZERO_RESULTS", R.string.err_google_zero_results, true) { @Override public boolean isOk() { return true; } }; public static final GoogleError OVER_QUERY_LIMIT = new GoogleError("OVER_QUERY_LIMIT", R.string.err_google_over_query_limit, false); public static final GoogleError REQUEST_DENIED = new GoogleError("REQUEST_DENIED", R.string.err_google_request_denied, false); public static final GoogleError INVALID_REQUEST = new GoogleError("INVALID_REQUEST", R.string.err_google_invalid_request, true); public static final GoogleError MAX_ELEMENTS_EXCEEDED = new GoogleError("MAX_ELEMENTS_EXCEEDED", R.string.err_google_max_elements_exceeded, true); public static final GoogleError NOT_FOUND = new GoogleError("NOT_FOUND", R.string.err_google_not_found, true); public static final GoogleError MAX_WAYPOINTS_EXCEEDED = new GoogleError("MAX_WAYPOINTS_EXCEEDED", R.string.err_google_max_waypoint_exceeded, true); private static volatile HashMap<String, GoogleError> map; private final String id; private final int msgRid; private final boolean cacheable; static { map = new HashMap<>(); } private GoogleError(String id, int msgRid, boolean cacheable) { this.id = id; this.msgRid = msgRid; this.cacheable = cacheable; synchronized (GoogleError.class) { if (map == null) map = new HashMap<>(); map.put(id, this); } } @Override public void save(ApiDataOutput d, int flags) { d.write(this.id); } public String getId() { return this.id; } public int getMsgRid() { return this.msgRid; } public boolean isCacheable() { return cacheable; } @Override public int hashCode() { int _hash = 17; _hash = _hash * 29 + EqualsUtils.hashCodeCheckNull(id); return _hash; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof GoogleError)) { return false; } GoogleError lhs = (GoogleError) o; return lhs != null && EqualsUtils.equalsCheckNull(id, lhs.id); } @Override public CharSequence getMsg(ITaskContext context) { return msgRid == 0 ? "" : context.getAndroidContext().getString(msgRid); } @Override public String getGoogleAnalyticsId() { return "GoogleError:" + id; } public static ITaskError getErrorById(String id) { if (id.equals("OK")) return BaseError.ERR_OK; else { GoogleError ret = getGoogleErrorById(id); if (ret != null) return ret; else return BaseError.ERR_UNKNOWN_ERROR; } } private static GoogleError getGoogleErrorById(String id) { synchronized (GoogleError.class) { if (map == null) return null; return map.get(id); } } public static final ApiCreator<GoogleError> CREATOR = new ApiCreator<GoogleError>() { public GoogleError create(ApiDataInput d) { return getGoogleErrorById(d.readString()); } public GoogleError[] newArray(int size) { return new GoogleError[size]; } }; } public interface IGoogleParam extends IWsParam { } public interface IGoogleResult extends IWsResult { } public static abstract class GoogleParam extends WsParam implements IGoogleParam { //private static final String TAG = GoogleParam.class.getSimpleName(); private static volatile long lastExecutionTime; @Override public String getSerialExecutionKey(ITaskContext context) { return "WS_GOOGLE"; } @Override public int getRetries(ITaskContext context, ITask task) { return 2; } @Override protected Builder createRequest(ITaskContext context, ITask task) { return WsUtils.createRequestAcceptingJsonResponse(getUri(context, task)); } @Override protected IWsResult createResult(ITaskContext context, ITask task, Response acceptableResponse) throws TaskException, IOException { try { JSONObject json = WsUtils.readResponseJson(acceptableResponse); return createResult(context, task, json); } catch (JSONException ex) { return createErrorResult(context, task, BaseError.ERR_CONNECTION_ERROR_UNEXPECTED_RES); } } @Override public IWsResult createResultUncached(ITaskContext context, ITask task) throws TaskException { final long minTimeBetween = 500; final long minDelayBefore = getMinDelayBefore(); final long currentTime = SystemClock.elapsedRealtime(); final long lastExecutionTime; synchronized (GoogleParam.class) { lastExecutionTime = GoogleParam.lastExecutionTime; } if (lastExecutionTime + minTimeBetween > currentTime || minDelayBefore > 0) { long sleepTime = Math.min(minTimeBetween, (lastExecutionTime + minTimeBetween) - currentTime); sleepTime = Math.max(sleepTime, minDelayBefore); log("Sleeping for: " + sleepTime + " ms"); try { Thread.sleep(sleepTime); } catch (InterruptedException e) { } } if (task.isCanceled()) { log("Task was canceled before execution"); return null; } log("Executing"); IWsResult ret = super.createResultUncached(context, task); if (ret == null) log("Finished, ret == null"); else if (ret.isValidResult()) log("Finished, valid result"); else log("Finished, " + ret.getError().getMsg(context)); synchronized (GoogleParam.class) { GoogleParam.lastExecutionTime = SystemClock.elapsedRealtime(); } return ret; } public long getMinDelayBefore() { return 0; } private void log(String msg) { LogUtils.d(getClass().getSimpleName(), this.toString() + " - " + msg); } public abstract String getUri(ITaskContext context, ITask task); public abstract IGoogleResult createResult(ITaskContext context, ITask task, JSONObject json) throws JSONException; } public static abstract class GoogleResult<TGoogleParam extends IGoogleParam> extends WsResult<TGoogleParam> implements IGoogleResult { public GoogleResult(TGoogleParam param, ITaskError error) { super(param, error); } public GoogleResult(TGoogleParam param, JSONObject json) throws JSONException { super(param, GoogleError.getErrorById(json.getString("status"))); } @Override public boolean isCacheableResult() { if (isValidResult()) return true; else return (getError() instanceof GoogleError) ? ((GoogleError)getError()).isCacheable() : super.isCacheableResult(); } } public static class GooglePlaceAutocompleteParam extends GoogleParam { public static final int TYPES_ANY = 0; public static final int TYPES_GEOCODE = 1; public static final int TYPES_ESTABLISHMENT = 2; public static final int TYPES_REGIONS = 3; public static final int TYPES_CITIES = 4; private final String input; private final String key; private final LocPoint location; // default LocPoint.INVALID private final int radius; // default -1 private final int types; // default TYPES_ANY private final String components; // optional public GooglePlaceAutocompleteParam(String input, String key, LocPoint location, int radius, int types, String components) { this.input = input; this.key = key; this.location = location; this.radius = radius; this.types = types; this.components = components; } public String getInput() { return this.input; } public String getKey() { return this.key; } public LocPoint getLocation() { return this.location; } public int getRadius() { return this.radius; } public int getTypes() { return this.types; } public String getComponents() { return this.components; } @Override public int hashCode() { int _hash = 17; _hash = _hash * 29 + EqualsUtils.hashCodeCheckNull(input); _hash = _hash * 29 + EqualsUtils.hashCodeCheckNull(key); _hash = _hash * 29 + EqualsUtils.hashCodeCheckNull(location); _hash = _hash * 29 + radius; _hash = _hash * 29 + types; _hash = _hash * 29 + EqualsUtils.hashCodeCheckNull(components); return _hash; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof GooglePlaceAutocompleteParam)) { return false; } GooglePlaceAutocompleteParam lhs = (GooglePlaceAutocompleteParam) o; return lhs != null && EqualsUtils.equalsCheckNull(input, lhs.input) && EqualsUtils.equalsCheckNull(key, lhs.key) && EqualsUtils.equalsCheckNull(location, lhs.location) && radius == lhs.radius && types == lhs.types && EqualsUtils.equalsCheckNull(components, lhs.components); } @Override public long getMinDelayBefore() { return 800; } @Override public String getUri(ITaskContext context, ITask task) { Uri.Builder uri = GoogleUtils.createUriBuilder(context, true, "place/autocomplete", key); uri.appendQueryParameter("input", input); if (!location.equals(LocPoint.INVALID)) uri.appendQueryParameter("location", GoogleUtils.encodeUriLocation(location)); if (radius >= 0) uri.appendQueryParameter("radius", Integer.toString(radius)); switch (types) { case TYPES_ANY: break; case TYPES_GEOCODE: uri.appendQueryParameter("types", "geocode"); break; case TYPES_ESTABLISHMENT: uri.appendQueryParameter("types", "establishment"); break; case TYPES_REGIONS: uri.appendQueryParameter("types", "(regions)"); break; case TYPES_CITIES: uri.appendQueryParameter("types", "(cities)"); break; default: throw new RuntimeException("Not implemented"); } if (!TextUtils.isEmpty(components)) uri.appendQueryParameter("components", components); return uri.build().toString(); } @Override public GooglePlaceAutocompleteResult createResult(ITaskContext context, ITask task, JSONObject json) throws JSONException { return new GooglePlaceAutocompleteResult(this, json); } @Override public GooglePlaceAutocompleteResult createErrorResult(ITaskContext context, ITask task, ITaskError error) { return new GooglePlaceAutocompleteResult(this, error, null); } @Override public String toString() { return getClass().getSimpleName() + ": " + input; } } public static class GooglePlaceAutocompleteResult extends GoogleResult<GooglePlaceAutocompleteParam> { private final ImmutableList<GooglePrediction> predictions; public GooglePlaceAutocompleteResult(GooglePlaceAutocompleteParam param, ITaskError error, ImmutableList<GooglePrediction> predictions) { super(param, error); this.predictions = predictions; } public GooglePlaceAutocompleteResult(GooglePlaceAutocompleteParam param, JSONObject json) throws JSONException { super(param, json); if (isValidResult()) { ImmutableList.Builder<GooglePrediction> predictions = new ImmutableList.Builder<GooglePrediction>(); JSONArray jPredictions = json.getJSONArray("predictions"); for (int i = 0; i < jPredictions.length(); i++) predictions.add(new GooglePrediction(jPredictions.getJSONObject(i))); this.predictions = predictions.build(); } else { this.predictions = null; } } public ImmutableList<GooglePrediction> getPredictions() { return this.predictions; } } public static class GooglePlaceDetailParam extends GoogleParam { private final String key; private final String placeId; public GooglePlaceDetailParam(String key, String placeId) { this.key = key; this.placeId = placeId; } public String getKey() { return this.key; } public String getPlaceId() { return this.placeId; } @Override public int hashCode() { int _hash = 17; _hash = _hash * 29 + EqualsUtils.hashCodeCheckNull(key); _hash = _hash * 29 + EqualsUtils.hashCodeCheckNull(placeId); return _hash; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof GooglePlaceDetailParam)) { return false; } GooglePlaceDetailParam lhs = (GooglePlaceDetailParam) o; return lhs != null && EqualsUtils.equalsCheckNull(key, lhs.key) && EqualsUtils.equalsCheckNull(placeId, lhs.placeId); } @Override public String getUri(ITaskContext context, ITask task) { Uri.Builder uri = GoogleUtils.createUriBuilder(context, true, "place/details", key) .appendQueryParameter("placeid", placeId); return uri.build().toString(); } @Override public GooglePlaceDetailResult createResult(ITaskContext context, ITask task, JSONObject json) throws JSONException { return new GooglePlaceDetailResult(this, json); } @Override public GooglePlaceDetailResult createErrorResult(ITaskContext context, ITask task, ITaskError error) { return new GooglePlaceDetailResult(this, error, null); } } public static class GooglePlaceDetailResult extends GoogleResult<GooglePlaceDetailParam> { private final LocPoint location; public GooglePlaceDetailResult(GooglePlaceDetailParam param, ITaskError error, LocPoint location) { super(param, error); this.location = location; } public GooglePlaceDetailResult(GooglePlaceDetailParam param, JSONObject json) throws JSONException { super(param, json); if (isValidResult()) { JSONObject result = json.getJSONObject("result"); this.location = GoogleUtils.parseLocation(result.getJSONObject("geometry").getJSONObject("location")); } else { this.location = null; } } public LocPoint getLocation() { return this.location; } } public static class GoogleDirectionsParam extends GoogleParam { public static final int MODE_DRIVING = 0; public static final int MODE_WALKING = 1; public static final int MODE_BICYCLING = 2; public static final int MODE_TRANSIT = 3; public static final int UNITS_METRIC = 0; public static final int UNITS_IMPERIAL = 1; private final GoogleLocationOrAddress origin; private final GoogleLocationOrAddress destination; private final int mode; // default MODE_DRIVING private final int units; // default UNITS_METRIC public GoogleDirectionsParam(GoogleLocationOrAddress origin, GoogleLocationOrAddress destination, int mode, int units) { this.origin = origin; this.destination = destination; this.mode = mode; this.units = units; } public GoogleLocationOrAddress getOrigin() { return this.origin; } public GoogleLocationOrAddress getDestination() { return this.destination; } public int getMode() { return this.mode; } public int getUnits() { return this.units; } @Override public int hashCode() { int _hash = 17; _hash = _hash * 29 + EqualsUtils.hashCodeCheckNull(origin); _hash = _hash * 29 + EqualsUtils.hashCodeCheckNull(destination); _hash = _hash * 29 + mode; _hash = _hash * 29 + units; return _hash; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof GoogleDirectionsParam)) { return false; } GoogleDirectionsParam lhs = (GoogleDirectionsParam) o; return lhs != null && EqualsUtils.equalsCheckNull(origin, lhs.origin) && EqualsUtils.equalsCheckNull(destination, lhs.destination) && mode == lhs.mode && units == lhs.units; } @Override public String getUri(ITaskContext context, ITask task) { Uri.Builder uri = GoogleUtils.createUriBuilder(context, false, "directions", null) .appendQueryParameter("origin", origin.getUriArgValue()) .appendQueryParameter("destination", destination.getUriArgValue()); switch (mode) { case MODE_DRIVING: break; case MODE_WALKING: uri.appendQueryParameter("mode", "walking"); break; case MODE_BICYCLING: uri.appendQueryParameter("mode", "bicycling"); break; case MODE_TRANSIT: uri.appendQueryParameter("mode", "transit"); break; default: throw new RuntimeException("Not implemented"); } switch (units) { case UNITS_METRIC: break; case UNITS_IMPERIAL: uri.appendQueryParameter("units", "imperial"); break; default: throw new RuntimeException("Not implemented"); } return uri.build().toString(); } @Override public GoogleDirectionsResult createResult(ITaskContext context, ITask task, JSONObject json) throws JSONException { return new GoogleDirectionsResult(this, json); } @Override public GoogleDirectionsResult createErrorResult(ITaskContext context, ITask task, ITaskError error) { return new GoogleDirectionsResult(this, error, null, null, null); } } public static class GoogleDirectionsResult extends GoogleResult<GoogleDirectionsParam> { private final GoogleDuration duration; private final GoogleDistance distance; private final ImmutableList<LocPoint> polyline; public GoogleDirectionsResult(GoogleDirectionsParam param, ITaskError error, GoogleDuration duration, GoogleDistance distance, ImmutableList<LocPoint> polyline) { super(param, error); this.duration = duration; this.distance = distance; this.polyline = polyline; } public GoogleDirectionsResult(GoogleDirectionsParam param, JSONObject json) throws JSONException { super(param, json); if (isValidResult()) { JSONObject route = json.getJSONArray("routes").getJSONObject(0); JSONObject leg = route.getJSONArray("legs").getJSONObject(0); this.duration = new GoogleDuration(leg.getJSONObject("duration")); this.distance = new GoogleDistance(leg.getJSONObject("distance")); this.polyline = GoogleUtils.decodePolyline(route.getJSONObject("overview_polyline").getString("points")); } else { this.duration = null; this.distance = null; this.polyline = null; } } public GoogleDuration getDuration() { return this.duration; } public GoogleDistance getDistance() { return this.distance; } public ImmutableList<LocPoint> getPolyline() { return this.polyline; } } public static class GoogleDistanceMatrixParam extends GoogleParam { public static final int MAX_RESULTS = 50; // 100 je moc dlouha url, navic problem se zadanym Odkud i Kam jako adresa... public static final int MODE_DRIVING = 0; public static final int MODE_WALKING = 1; public static final int MODE_BICYCLING = 2; public static final int UNITS_METRIC = 0; public static final int UNITS_IMPERIAL = 1; private final ImmutableList<GoogleLocationOrAddress> origins; private final ImmutableList<GoogleLocationOrAddress> destinations; private final int mode; // default MODE_DRIVING private final int units; // default UNITS_METRIC public GoogleDistanceMatrixParam(ImmutableList<GoogleLocationOrAddress> origins, ImmutableList<GoogleLocationOrAddress> destinations, int mode, int units) { this.origins = origins; this.destinations = destinations; this.mode = mode; this.units = units; } public ImmutableList<GoogleLocationOrAddress> getOrigins() { return this.origins; } public ImmutableList<GoogleLocationOrAddress> getDestinations() { return this.destinations; } public int getMode() { return this.mode; } public int getUnits() { return this.units; } private int _hash = EqualsUtils.HASHCODE_INVALID; @Override public int hashCode() { if (_hash == EqualsUtils.HASHCODE_INVALID) { int _hash = 17; _hash = _hash * 29 + EqualsUtils.itemsHashCode(origins); _hash = _hash * 29 + EqualsUtils.itemsHashCode(destinations); _hash = _hash * 29 + mode; _hash = _hash * 29 + units; this._hash = _hash == EqualsUtils.HASHCODE_INVALID ? EqualsUtils.HASHCODE_INVALID_REPLACEMENT : _hash; } return this._hash; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof GoogleDistanceMatrixParam)) { return false; } GoogleDistanceMatrixParam lhs = (GoogleDistanceMatrixParam) o; return lhs != null && EqualsUtils.itemsEqual(origins, lhs.origins) && EqualsUtils.itemsEqual(destinations, lhs.destinations) && mode == lhs.mode && units == lhs.units; } @Override public String getUri(ITaskContext context, ITask task) { Uri.Builder uri = GoogleUtils.createUriBuilder(context, false, "distancematrix", null); StringBuilder origins = new StringBuilder(); for (GoogleLocationOrAddress g : this.origins) { if (origins.length() > 0) origins.append('|'); origins.append(g.getUriArgValue()); } uri.appendQueryParameter("origins", origins.toString()); StringBuilder destinations = new StringBuilder(); for (GoogleLocationOrAddress g : this.destinations) { if (destinations.length() > 0) destinations.append('|'); destinations.append(g.getUriArgValue()); } uri.appendQueryParameter("destinations", destinations.toString()); switch (mode) { case MODE_DRIVING: break; case MODE_WALKING: uri.appendQueryParameter("mode", "walking"); break; case MODE_BICYCLING: uri.appendQueryParameter("mode", "bicycling"); break; default: throw new RuntimeException("Not implemented"); } switch (units) { case UNITS_METRIC: break; case UNITS_IMPERIAL: uri.appendQueryParameter("units", "imperial"); break; default: throw new RuntimeException("Not implemented"); } return uri.build().toString(); } @Override public GoogleDistanceMatrixResult createResult(ITaskContext context, ITask task, JSONObject json) throws JSONException { return new GoogleDistanceMatrixResult(this, json); } @Override public GoogleDistanceMatrixResult createErrorResult(ITaskContext context, ITask task, ITaskError error) { return new GoogleDistanceMatrixResult(this, error, null, null, null); } } public static class GoogleDistanceMatrixResult extends GoogleResult<GoogleDistanceMatrixParam> { private final ImmutableList<String> originAddresses; private final ImmutableList<String> destinationAddresses; private final ImmutableList<GoogleDistanceMatrixRow> rows; public GoogleDistanceMatrixResult(GoogleDistanceMatrixParam param, ITaskError error, ImmutableList<String> originAddresses, ImmutableList<String> destinationAddresses, ImmutableList<GoogleDistanceMatrixRow> rows) { super(param, error); this.originAddresses = originAddresses; this.destinationAddresses = destinationAddresses; this.rows = rows; } public GoogleDistanceMatrixResult(GoogleDistanceMatrixParam param, JSONObject json) throws JSONException { super(param, json); if (isValidResult()) { ImmutableList.Builder<String> originAddresses = new ImmutableList.Builder<String>(); JSONArray jOriginAddresses = json.getJSONArray("origin_addresses"); for (int i = 0; i < jOriginAddresses.length(); i++) originAddresses.add(jOriginAddresses.getString(i)); this.originAddresses = originAddresses.build(); ImmutableList.Builder<String> destinationAddresses = new ImmutableList.Builder<String>(); JSONArray jDestinationAddresses = json.getJSONArray("destination_addresses"); for (int i = 0; i < jDestinationAddresses.length(); i++) destinationAddresses.add(jDestinationAddresses.getString(i)); this.destinationAddresses = destinationAddresses.build(); ImmutableList.Builder<GoogleDistanceMatrixRow> rows = new ImmutableList.Builder<GoogleDistanceMatrixRow>(); JSONArray jRows = json.getJSONArray("rows"); for (int i = 0; i < jRows.length(); i++) rows.add(new GoogleDistanceMatrixRow(jRows.getJSONObject(i))); this.rows = rows.build(); } else { this.originAddresses = null; this.destinationAddresses = null; this.rows = null; } } public ImmutableList<String> getOriginAddresses() { return this.originAddresses; } public ImmutableList<String> getDestinationAddresses() { return this.destinationAddresses; } public ImmutableList<GoogleDistanceMatrixRow> getRows() { return this.rows; } } public static class GoogleGeocodeParam extends GoogleParam { private final GoogleLocationOrAddress place; public GoogleGeocodeParam(GoogleLocationOrAddress place) { this.place = place; } public GoogleLocationOrAddress getPlace() { return this.place; } @Override public int hashCode() { int _hash = 17; _hash = _hash * 29 + EqualsUtils.hashCodeCheckNull(place); return _hash; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof GoogleGeocodeParam)) { return false; } GoogleGeocodeParam lhs = (GoogleGeocodeParam) o; return lhs != null && EqualsUtils.equalsCheckNull(place, lhs.place); } @Override public String getUri(ITaskContext context, ITask task) { Uri.Builder uri = GoogleUtils.createUriBuilder(context, false, "geocode", null) .appendQueryParameter(place.getUriArgKey(), place.getUriArgValue()); return uri.build().toString(); } @Override public GoogleGeocodeResult createResult(ITaskContext context, ITask task, JSONObject json) throws JSONException { return new GoogleGeocodeResult(this, json); } @Override public GoogleGeocodeResult createErrorResult(ITaskContext context, ITask task, ITaskError error) { return new GoogleGeocodeResult(this, error, null, null); } } public static class GoogleGeocodeResult extends GoogleResult<GoogleGeocodeParam> { private final String formattedAddress; private final LocPoint location; public GoogleGeocodeResult(GoogleGeocodeParam param, ITaskError error, String formattedAddress, LocPoint location) { super(param, error); this.formattedAddress = formattedAddress; this.location = location; } public GoogleGeocodeResult(GoogleGeocodeParam param, JSONObject json) throws JSONException { super(param, json); if (isValidResult()) { JSONObject result = json.getJSONArray("results").getJSONObject(0); this.formattedAddress = result.getString("formatted_address"); this.location = GoogleUtils.parseLocation(result.getJSONObject("geometry").getJSONObject("location")); } else { this.formattedAddress = null; this.location = null; } } public String getFormattedAddress() { return this.formattedAddress; } public LocPoint getLocation() { return this.location; } } public static class GooglePrediction { private final String description; private final String id; private final String placeId; private final ImmutableList<GoogleTerm> terms; private final ImmutableList<GoogleMatchedSubstring> matchedSubstrings; public GooglePrediction(String description, String id, String placeId, ImmutableList<GoogleTerm> terms, ImmutableList<GoogleMatchedSubstring> matchedSubstrings) { this.description = description; this.id = id; this.placeId = placeId; this.terms = terms; this.matchedSubstrings = matchedSubstrings; } public GooglePrediction(JSONObject json) throws JSONException { this.description = json.getString("description"); this.id = json.getString("id"); this.placeId = json.getString("place_id"); ImmutableList.Builder<GoogleTerm> terms = new ImmutableList.Builder<GoogleTerm>(); JSONArray jTerms = json.getJSONArray("terms"); for (int i = 0; i < jTerms.length(); i++) terms.add(new GoogleTerm(jTerms.getJSONObject(i))); this.terms = terms.build(); ImmutableList.Builder<GoogleMatchedSubstring> matchedSubstrings = new ImmutableList.Builder<GoogleMatchedSubstring>(); JSONArray jMatchedSubstrings = json.getJSONArray("matched_substrings"); for (int i = 0; i < jMatchedSubstrings.length(); i++) matchedSubstrings.add(new GoogleMatchedSubstring(jMatchedSubstrings.getJSONObject(i))); this.matchedSubstrings = matchedSubstrings.build(); } public String getDescription() { return this.description; } public String getId() { return this.id; } public String getPlaceId() { return this.placeId; } public ImmutableList<GoogleTerm> getTerms() { return this.terms; } public ImmutableList<GoogleMatchedSubstring> getMatchedSubstrings() { return this.matchedSubstrings; } } public static class GoogleTerm { private final String value; private final int offset; public GoogleTerm(String value, int offset) { this.value = value; this.offset = offset; } public GoogleTerm(JSONObject json) throws JSONException { this.value = json.getString("value"); this.offset = json.getInt("offset"); } public String getValue() { return this.value; } public int getOffset() { return this.offset; } } public static class GoogleMatchedSubstring { private final int offset; private final int length; public GoogleMatchedSubstring(int offset, int length) { this.offset = offset; this.length = length; } public GoogleMatchedSubstring(JSONObject json) throws JSONException { this.offset = json.getInt("offset"); this.length = json.getInt("length"); } public int getOffset() { return this.offset; } public int getLength() { return this.length; } } public static class GoogleLocationOrAddress { // Vzdy musi byt zadana bud location nebo address private final LocPoint latlng; // default LocPoint.INVALID private final String address; // optional public GoogleLocationOrAddress(LocPoint latlng, String address) { this.latlng = latlng; this.address = address; } public LocPoint getLatlng() { return this.latlng; } public String getAddress() { return this.address; } @Override public int hashCode() { int _hash = 17; _hash = _hash * 29 + EqualsUtils.hashCodeCheckNull(latlng); _hash = _hash * 29 + EqualsUtils.hashCodeCheckNull(address); return _hash; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof GoogleLocationOrAddress)) { return false; } GoogleLocationOrAddress lhs = (GoogleLocationOrAddress) o; return lhs != null && EqualsUtils.equalsCheckNull(latlng, lhs.latlng) && EqualsUtils.equalsCheckNull(address, lhs.address); } public String getUriArgKey() { if (!latlng.equals(LocPoint.INVALID)) return "latlng"; else return "address"; } public String getUriArgValue() { if (!latlng.equals(LocPoint.INVALID)) return GoogleUtils.encodeUriLocation(latlng); else return address; } } public static class GoogleDistanceMatrixRow { private final ImmutableList<GoogleDistanceMatrixElement> elements; public GoogleDistanceMatrixRow(ImmutableList<GoogleDistanceMatrixElement> elements) { this.elements = elements; } public GoogleDistanceMatrixRow(JSONObject json) throws JSONException { ImmutableList.Builder<GoogleDistanceMatrixElement> elements = new ImmutableList.Builder<GoogleDistanceMatrixElement>(); JSONArray jElements = json.getJSONArray("elements"); for (int i = 0; i < jElements.length(); i++) elements.add(new GoogleDistanceMatrixElement(jElements.getJSONObject(i))); this.elements = elements.build(); } public ImmutableList<GoogleDistanceMatrixElement> getElements() { return this.elements; } } public static class GoogleDistanceMatrixElement { public static final int STATUS_OK = 0; public static final int STATUS_UNKNOWN_ERROR = 1; public static final int STATUS_NOT_FOUND = 2; public static final int STATUS_ZERO_RESULTS = 3; private final int status; private final GoogleDuration duration; // optional private final GoogleDistance distance; // optional public GoogleDistanceMatrixElement(int status, GoogleDuration duration, GoogleDistance distance) { this.status = status; this.duration = duration; this.distance = distance; } // UPRAVENO !!! public GoogleDistanceMatrixElement(JSONObject json) throws JSONException { String s = json.getString("status"); if (s.equals("OK")) this.status = STATUS_OK; else if (s.equals("NOT_FOUND")) this.status = STATUS_NOT_FOUND; else if (s.equals("ZERO_RESULTS")) this.status = STATUS_ZERO_RESULTS; else this.status = STATUS_UNKNOWN_ERROR; if (status == STATUS_OK) { this.duration = new GoogleDuration(json.getJSONObject("duration")); this.distance = new GoogleDistance(json.getJSONObject("distance")); } else { this.duration = null; this.distance = null; } } public int getStatus() { return this.status; } public GoogleDuration getDuration() { return this.duration; } public GoogleDistance getDistance() { return this.distance; } } public static class GoogleDuration { private final Duration value; private final String text; public GoogleDuration(Duration value, String text) { this.value = value; this.text = text; } public GoogleDuration(JSONObject json) throws JSONException { this.value = Duration.standardSeconds(json.getInt("value")); this.text = json.getString("text"); } public Duration getValue() { return this.value; } public String getText() { return this.text; } } public static class GoogleDistance { private final int value; private final String text; public GoogleDistance(int value, String text) { this.value = value; this.text = text; } public GoogleDistance(JSONObject json) throws JSONException { this.value = json.getInt("value"); this.text = json.getString("text"); } public int getValue() { return this.value; } public String getText() { return this.text; } } }