Skip to content

Commit

Permalink
Merge pull request #95 from sergiandreplace/feature/api_fallback
Browse files Browse the repository at this point in the history
Feature/api fallback
  • Loading branch information
ferranpons authored Jun 13, 2017
2 parents b97c6f5 + b58e0d8 commit c2e2355
Show file tree
Hide file tree
Showing 9 changed files with 382 additions and 4 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ You only need to use the Builder setters like:
```java
Intent intent = new LocationPickerActivity.Builder()
.withLocation(41.4036299, 2.1743558)
.withGeolocApiKey("<PUT API KEY HERE>")
.withSearchZone("es_ES")
.shouldReturnOkOnBackPressed()
.withStreetHidden()
Expand Down Expand Up @@ -305,6 +306,13 @@ Available tracking events are:
|RESULT_OK|Return location|
|CANCEL|Return without location|

#### Geocoding API Fallback

In few cases, the geocoding service from Android fails due to an issue with the NetworkLocator. The only way of fixing this is rebooting the device.

In order to cover these cases, you can instruct Leku to use the Geocoding API. To enable it, just use the method '''withGeolocApiKey''' when invoking the LocationPicker.

You should provide your Server Key as parameter. Keep in mind that the free tier only allows 2,500 requests per day. You can track how many times is it used in the Developer Console from Google.

#### Extra

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ protected void onCreate(Bundle savedInstanceState) {
public void onClick(View view) {
Intent locationPickerIntent = new LocationPickerActivity.Builder()
.withLocation(41.4036299, 2.1743558)
//.withGeolocApiKey("<PUT API KEY HERE>")
//.withSearchZone("es_ES")
//.shouldReturnOkOnBackPressed()
//.withStreetHidden()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.schibsted.leku.geocoder.api;

import android.location.Address;
import android.support.annotation.NonNull;
import com.schibstedspain.leku.geocoder.api.AddressBuilder;
import java.util.List;
import org.json.JSONException;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.junit.MockitoRule;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.mockito.junit.MockitoJUnit.rule;

public class AddressBuilderShould {

@Rule public MockitoRule mockitoRule = rule();

private AddressBuilder addressBuilder;

@Before
public void setUp() {
addressBuilder = new AddressBuilder();
}

@Test
public void returnExpectedAddressWhenJsonProvided() throws JSONException {
String json = getJson();

List<Address> addresses = addressBuilder.parseResult(json);

assertEquals("Barcelona", addresses.get(0).getLocality());
assertEquals("Carrer del Comte d'Urgell, 102", addresses.get(0).getAddressLine(0));
assertEquals("08011", addresses.get(0).getPostalCode());
assertTrue(Double.valueOf(41.3838035).equals(addresses.get(0).getLatitude()));
assertTrue(Double.valueOf(2.1568617).equals(addresses.get(0).getLongitude()));

}

@Test
public void returnExpectedAddressWhenJsonWithOnlyCityProvided() throws JSONException {
String json = getJsonForOnlyCity();

List<Address> addresses = addressBuilder.parseResult(json);

assertEquals("Barcelona", addresses.get(0).getLocality());
assertEquals("", addresses.get(0).getAddressLine(0));
assertEquals("", addresses.get(0).getPostalCode());
assertTrue(Double.valueOf(41.3850639).equals(addresses.get(0).getLatitude()));
assertTrue(Double.valueOf(2.1734035).equals(addresses.get(0).getLongitude()));

}

@NonNull
private String getJson() {
return "{\"results\": [{\"address_components\": [{\"long_name\": \"102\",\"short_name\": \"102\",\"types\": "
+ "[ \"street_number\"]},{\"long_name\": \"Carrer del Comte d'Urgell\",\"short_name\": \"Carrer del Comte d'Urgell\",\"types\": "
+ "[ \"route\"]},{\"long_name\": \"Barcelona\",\"short_name\": \"Barcelona\",\"types\": [ \"locality\", \"political\"]},"
+ "{\"long_name\": \"Barcelona\",\"short_name\": \"Barcelona\",\"types\": [ \"administrative_area_level_2\", \"political\"]},"
+ "{\"long_name\": \"Catalunya\",\"short_name\": \"CT\",\"types\": [ \"administrative_area_level_1\", \"political\"]},"
+ "{\"long_name\": \"Spain\",\"short_name\": \"ES\",\"types\": [ \"country\", \"political\"]},{\"long_name\": \"08011\","
+ "\"short_name\": \"08011\",\"types\": [ \"postal_code\"]}],\"formatted_address\""
+ ": \"Carrer del Comte d'Urgell, 102, 08011 Barcelona, Spain\",\"geometry\": {\"bounds\": {\"northeast\": "
+ "{ \"lat\": 41.3839416, \"lng\": 2.1570442},\"southwest\": { \"lat\": 41.3836653, \"lng\": 2.1566792}},\"location\": "
+ "{\"lat\": 41.3838035,\"lng\": 2.1568617},\"location_type\": \"ROOFTOP\",\"viewport\": {\"northeast\": "
+ "{ \"lat\": 41.3851524302915, \"lng\": 2.158210680291502},\"southwest\": { \"lat\": 41.3824544697085, "
+ "\"lng\": 2.155512719708498}}},\"partial_match\": true,\"place_id\": \"ChIJdehx-YiipBIR8hitzOckUuo\",\"types\": [\"premise\"] } "
+ "], \"status\": \"OK\"}";
}

@NonNull
private String getJsonForOnlyCity() {
return "{\"results\": [{\"address_components\": [{\"long_name\": \"Barcelona\",\"short_name\": \"Barcelona\",\"types\": "
+ "[\"locality\",\"political\"]},{\"long_name\": \"Barcelona\",\"short_name\": \"Barcelona\",\"types\": "
+ "[\"administrative_area_level_2\",\"political\"]},{\"long_name\": \"Catalonia\",\"short_name\": \"CT\",\"types\": "
+ "[\"administrative_area_level_1\",\"political\"]},{\"long_name\": \"Spain\",\"short_name\": \"ES\",\"types\": "
+ "[\"country\",\"political\"]}],\"formatted_address\": \"Barcelona, Spain\",\"geometry\": {\"bounds\": {\"northeast\": "
+ "{\"lat\": 41.4695761,\"lng\": 2.2280099},\"southwest\": {\"lat\": 41.320004,\"lng\": 2.0695258}},\"location\":"
+ " {\"lat\": 41.3850639,\"lng\": 2.1734035},\"location_type\": \"APPROXIMATE\",\"viewport\": {\"northeast\": "
+ "{\"lat\": 41.4695761,\"lng\": 2.2280099},\"southwest\": {\"lat\": 41.320004,\"lng\": 2.0695258}}},\"place_id\": "
+ "\"ChIJ5TCOcRaYpBIRCmZHTz37sEQ\",\"types\": [\"locality\",\"political\"]}],\"status\": \"OK\"}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.schibstedspain.leku.geocoder.GeocoderAPIInteractor;
import com.schibstedspain.leku.geocoder.GeocoderInteractor;
import com.schibstedspain.leku.geocoder.GeocoderPresenter;
import com.schibstedspain.leku.geocoder.GeocoderViewInterface;
import com.schibstedspain.leku.geocoder.api.AddressBuilder;
import com.schibstedspain.leku.geocoder.api.NetworkClient;
import com.schibstedspain.leku.permissions.PermissionUtils;
import com.schibstedspain.leku.tracker.TrackEvents;
import java.util.ArrayList;
Expand Down Expand Up @@ -77,6 +80,7 @@ public class LocationPickerActivity extends AppCompatActivity
public static final String ENABLE_LOCATION_PERMISSION_REQUEST = "enable_location_permission_request";
public static final String POIS_LIST = "pois_list";
public static final String LEKU_POI = "leku_poi";
private static final String GEOLOC_API_KEY = "geoloc_api_key";
private static final String LOCATION_KEY = "location_key";
private static final String LAST_LOCATION_QUERY = "last_location_query";
private static final String OPTIONS_HIDE_STREET = "street";
Expand Down Expand Up @@ -125,6 +129,7 @@ public class LocationPickerActivity extends AppCompatActivity
private Map<String, LekuPoi> lekuPoisMarkersMap;
private Marker currentMarker;
private TextWatcher textWatcher;
private GeocoderAPIInteractor apiInteractor;

@Override
protected void onCreate(Bundle savedInstanceState) {
Expand Down Expand Up @@ -154,8 +159,9 @@ protected void track(TrackEvents event) {

private void setUpMainVariables() {
Geocoder geocoder = new Geocoder(this, Locale.getDefault());
apiInteractor = new GeocoderAPIInteractor(new NetworkClient(), new AddressBuilder());
geocoderPresenter = new GeocoderPresenter(new ReactiveLocationProvider(getApplicationContext()),
new GeocoderInteractor(geocoder));
new GeocoderInteractor(geocoder), apiInteractor);
geocoderPresenter.setUI(this);
progressBar = (ProgressBar) findViewById(R.id.loading_progress_bar);
progressBar.setVisibility(View.GONE);
Expand Down Expand Up @@ -647,6 +653,9 @@ private void getSavedInstanceParams(Bundle savedInstanceState) {
if (savedInstanceState.keySet().contains(LAYOUTS_TO_HIDE)) {
setLayoutVisibilityFromBundle(savedInstanceState);
}
if (savedInstanceState.keySet().contains(GEOLOC_API_KEY)) {
apiInteractor.setApiKey(savedInstanceState.getString(GEOLOC_API_KEY));
}
if (savedInstanceState.keySet().contains(SEARCH_ZONE)) {
searchZone = savedInstanceState.getString(SEARCH_ZONE);
}
Expand Down Expand Up @@ -685,6 +694,9 @@ private void getTransitionBundleParams(Bundle transitionBundle) {
if (transitionBundle.keySet().contains(POIS_LIST)) {
poisList = transitionBundle.getParcelableArrayList(POIS_LIST);
}
if (transitionBundle.keySet().contains(GEOLOC_API_KEY)) {
apiInteractor.setApiKey(transitionBundle.getString(GEOLOC_API_KEY));
}
}

private void setLayoutVisibilityFromBundle(Bundle transitionBundle) {
Expand Down Expand Up @@ -1057,6 +1069,7 @@ public static class Builder {
private boolean enableSatelliteView = true;
private boolean shouldReturnOkOnBackPressed = false;
private List<LekuPoi> lekuPois;
private String geolocApiKey = null;

public Builder() {
}
Expand Down Expand Up @@ -1110,6 +1123,11 @@ public Builder withPois(List<LekuPoi> pois) {
return this;
}

public Builder withGeolocApiKey(String apiKey) {
this.geolocApiKey = apiKey;
return this;
}

public Intent build(Context context) {
Intent intent = new Intent(context, LocationPickerActivity.class);

Expand All @@ -1130,6 +1148,9 @@ public Intent build(Context context) {
if (lekuPois != null && !lekuPois.isEmpty()) {
intent.putExtra(POIS_LIST, new ArrayList<>(lekuPois));
}
if (geolocApiKey != null) {
intent.putExtra(GEOLOC_API_KEY, geolocApiKey);
}

return intent;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.schibstedspain.leku.geocoder;

import android.location.Address;
import com.google.android.gms.maps.model.LatLng;
import com.schibstedspain.leku.geocoder.api.AddressBuilder;
import com.schibstedspain.leku.geocoder.api.NetworkClient;
import java.util.List;
import java.util.Locale;
import org.json.JSONException;
import rx.Observable;

public class GeocoderAPIInteractor implements GeocoderInteractorInterface {

private static final String QUERY_REQUEST = "https://maps.googleapis.com/maps/api/geocode/json?address=%1$s&key=%2$s";
private static final String QUERY_REQUEST_WITH_RECTANGLE
= "https://maps.googleapis.com/maps/api/geocode/json?address=%1$s&key=%2$s&bounds=%3$f,%4$f|%5$f,%6$f";
private static final String QUERY_LAT_LONG = "https://maps.googleapis.com/maps/api/geocode/json?latlng=%1$f,%2$f&key=%3$s";
private String apiKey;
private final NetworkClient networkClient;
private final AddressBuilder addressBuilder;

public GeocoderAPIInteractor(NetworkClient networkClient, AddressBuilder addressBuilder) {
this.networkClient = networkClient;
this.addressBuilder = addressBuilder;
}

public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}

@Override
public Observable<List<Address>> getFromLocationName(String query) {
return Observable.create(subscriber -> {
if (apiKey == null) {
subscriber.onCompleted();
return;
}
try {
String result = networkClient.requestFromLocationName(String.format(Locale.ENGLISH,
QUERY_REQUEST, query.trim(), apiKey));
List<Address> addresses = addressBuilder.parseResult(result);
subscriber.onNext(addresses);
subscriber.onCompleted();
} catch (JSONException e) {
subscriber.onError(e);
}
});
}

@Override
public Observable<List<Address>> getFromLocationName(String query, LatLng lowerLeft,
LatLng upperRight) {
return Observable.create(subscriber -> {
if (apiKey == null) {
subscriber.onCompleted();
return;
}
try {
String result = networkClient.requestFromLocationName(String.format(Locale.ENGLISH,
QUERY_REQUEST_WITH_RECTANGLE, query.trim(), apiKey, lowerLeft.latitude,
lowerLeft.longitude, upperRight.latitude, upperRight.longitude));
List<Address> addresses = addressBuilder.parseResult(result);
subscriber.onNext(addresses);
subscriber.onCompleted();
} catch (JSONException e) {
subscriber.onError(e);
}
});
}

@Override
public Observable<List<Address>> getFromLocation(double latitude, double longitude) {
return Observable.create(subscriber -> {
if (apiKey == null) {
subscriber.onCompleted();
return;
}
try {
String result = networkClient.requestFromLocationName(String.format(Locale.ENGLISH,
QUERY_LAT_LONG, latitude, longitude, apiKey));
List<Address> addresses = addressBuilder.parseResult(result);
subscriber.onNext(addresses);
subscriber.onCompleted();
} catch (JSONException e) {
subscriber.onError(e);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,21 @@ public class GeocoderPresenter {
private static final int RETRY_COUNT = 3;

private final GeocoderInteractorInterface interactor;
private final GeocoderInteractorInterface apiInteractor;
private GeocoderViewInterface view;
private final GeocoderViewInterface nullView = new GeocoderViewInterface.NullView();
private CompositeSubscription compositeSubscription;
private final Scheduler scheduler;
private ReactiveLocationProvider locationProvider;

public GeocoderPresenter(ReactiveLocationProvider reactiveLocationProvider, GeocoderInteractorInterface interactor) {
this(reactiveLocationProvider, interactor, AndroidSchedulers.mainThread());
public GeocoderPresenter(ReactiveLocationProvider reactiveLocationProvider, GeocoderInteractorInterface interactor,
GeocoderInteractorInterface apiInteractor) {
this(reactiveLocationProvider, interactor, apiInteractor, AndroidSchedulers.mainThread());
}

public GeocoderPresenter(ReactiveLocationProvider reactiveLocationProvider, GeocoderInteractorInterface interactor,
Scheduler scheduler) {
GeocoderInteractorInterface apiInteractor, Scheduler scheduler) {
this.apiInteractor = apiInteractor;
this.view = nullView;
this.scheduler = scheduler;
this.locationProvider = reactiveLocationProvider;
Expand Down Expand Up @@ -56,6 +59,7 @@ public void getFromLocationName(String query) {
.subscribeOn(Schedulers.newThread())
.observeOn(scheduler)
.retry(RETRY_COUNT)
.onErrorResumeNext(apiInteractor.getFromLocationName(query))
.subscribe(view::showLocations, throwable -> view.showLoadLocationError(),
view::didLoadLocation);
compositeSubscription.add(locationNameSubscription);
Expand All @@ -67,6 +71,7 @@ public void getFromLocationName(String query, LatLng lowerLeft, LatLng upperRigh
.subscribeOn(Schedulers.newThread())
.observeOn(scheduler)
.retry(RETRY_COUNT)
.onErrorResumeNext(apiInteractor.getFromLocationName(query, lowerLeft, upperRight))
.subscribe(view::showLocations, throwable -> view.showLoadLocationError(),
view::didLoadLocation);
compositeSubscription.add(locationNameSubscription);
Expand All @@ -79,6 +84,7 @@ public void getDebouncedFromLocationName(String query, int debounceTime) {
.subscribeOn(Schedulers.newThread())
.observeOn(scheduler)
.retry(RETRY_COUNT)
.onErrorResumeNext(apiInteractor.getFromLocationName(query))
.subscribe(view::showDebouncedLocations, throwable -> view.showLoadLocationError(),
view::didLoadLocation);
compositeSubscription.add(locationNameDebounceSubscription);
Expand All @@ -91,6 +97,7 @@ public void getDebouncedFromLocationName(String query, LatLng lowerLeft, LatLng
.subscribeOn(Schedulers.newThread())
.observeOn(scheduler)
.retry(RETRY_COUNT)
.onErrorResumeNext(apiInteractor.getFromLocationName(query, lowerLeft, upperRight))
.subscribe(view::showDebouncedLocations, throwable -> view.showLoadLocationError(),
view::didLoadLocation);
compositeSubscription.add(locationNameDebounceSubscription);
Expand All @@ -102,6 +109,7 @@ public void getInfoFromLocation(LatLng latLng) {
.subscribeOn(Schedulers.newThread())
.observeOn(scheduler)
.retry(RETRY_COUNT)
.onErrorResumeNext(apiInteractor.getFromLocation(latLng.latitude, latLng.longitude))
.subscribe(view::showLocationInfo, throwable -> view.showGetLocationInfoError(),
view::didGetLocationInfo);
compositeSubscription.add(locationSubscription);
Expand Down
Loading

0 comments on commit c2e2355

Please sign in to comment.