package pl.charmas.android.reactivelocation.observables.geocode; import android.location.Address; import android.text.TextUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import rx.Observable; import rx.Subscriber; class FallbackReverseGeocodeObservable implements Observable.OnSubscribe<List<Address>> { private final Locale locale; private final double latitude; private final double longitude; private final int maxResults; FallbackReverseGeocodeObservable(Locale locale, double latitude, double longitude, int maxResults) { this.locale = locale; this.latitude = latitude; this.longitude = longitude; this.maxResults = maxResults; } @Override public void call(Subscriber<? super List<Address>> subscriber) { try { subscriber.onNext(alternativeReverseGeocodeQuery()); subscriber.onCompleted(); } catch (Exception ex) { subscriber.onError(ex); } } /** * This function fetches a list of addresses for the set latitude, longitude and maxResults properties from the * Google Geocode API (http://maps.googleapis.com/maps/api/geocode). * * @return List of addresses * @throws IOException In case of network problems * @throws JSONException In case of problems while parsing the json response from google geocode API servers */ private List<Address> alternativeReverseGeocodeQuery() throws IOException, JSONException { URL url = new URL(String.format(Locale.ENGLISH, "http://maps.googleapis.com/maps/api/geocode/json?" + "latlng=%1$f,%2$f&sensor=true&language=%3$s", latitude, longitude, locale.getLanguage() )); HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); StringBuilder stringBuilder = new StringBuilder(); List<Address> outResult = new ArrayList<>(); try { BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8")); String line; while ((line = reader.readLine()) != null) { stringBuilder.append(line); } // Root json response object JSONObject jsonRootObject = new JSONObject(stringBuilder.toString()); // No results status if ("ZERO_RESULTS".equalsIgnoreCase(jsonRootObject.getString("status"))) { return Collections.emptyList(); } // Other non-OK responses status if (!"OK".equalsIgnoreCase(jsonRootObject.getString("status"))) { throw new RuntimeException("Wrong API response"); } // Process results JSONArray results = jsonRootObject.getJSONArray("results"); for (int i = 0; i < results.length() && i < maxResults; i++) { Address address = new Address(Locale.getDefault()); String addressLineString = ""; JSONObject sourceResult = results.getJSONObject(i); JSONArray addressComponents = sourceResult.getJSONArray("address_components"); // Assemble address by various components for (int ac = 0; ac < addressComponents.length(); ac++) { String longNameVal = addressComponents.getJSONObject(ac).getString("long_name"); String shortNameVal = addressComponents.getJSONObject(ac).getString("short_name"); JSONArray acTypes = addressComponents.getJSONObject(ac).getJSONArray("types"); String acType = acTypes.getString(0); if (!TextUtils.isEmpty(longNameVal)) { if (acType.equalsIgnoreCase("street_number")) { if (TextUtils.isEmpty(addressLineString)) { addressLineString = longNameVal; } else { addressLineString += " " + longNameVal; } } else if (acType.equalsIgnoreCase("route")) { if (TextUtils.isEmpty(addressLineString)) { addressLineString = longNameVal; } else { addressLineString = longNameVal + " " + addressLineString; } } else if (acType.equalsIgnoreCase("sublocality")) { address.setSubLocality(longNameVal); } else if (acType.equalsIgnoreCase("locality")) { address.setLocality(longNameVal); } else if (acType.equalsIgnoreCase("administrative_area_level_2")) { address.setSubAdminArea(longNameVal); } else if (acType.equalsIgnoreCase("administrative_area_level_1")) { address.setAdminArea(longNameVal); } else if (acType.equalsIgnoreCase("country")) { address.setCountryName(longNameVal); address.setCountryCode(shortNameVal); } else if (acType.equalsIgnoreCase("postal_code")) { address.setPostalCode(longNameVal); } } } // Try to get the already formatted address String formattedAddress = sourceResult.getString("formatted_address"); if (!TextUtils.isEmpty(formattedAddress)) { String[] formattedAddressLines = formattedAddress.split(","); for (int ia = 0; ia < formattedAddressLines.length; ia++) { address.setAddressLine(ia, formattedAddressLines[ia].trim()); } } else if (!TextUtils.isEmpty(addressLineString)) { // If that fails use our manually assembled formatted address address.setAddressLine(0, addressLineString); } // Finally add address to resulting set outResult.add(address); } } finally { urlConnection.disconnect(); } return Collections.unmodifiableList(outResult); } }