aboutsummaryrefslogtreecommitdiffstats
path: root/main/src
diff options
context:
space:
mode:
Diffstat (limited to 'main/src')
-rw-r--r--main/src/cgeo/geocaching/AddressListActivity.java51
-rw-r--r--main/src/cgeo/geocaching/CacheDetailActivity.java26
-rw-r--r--main/src/cgeo/geocaching/CacheListActivity.java8
-rw-r--r--main/src/cgeo/geocaching/DataStore.java4
-rw-r--r--main/src/cgeo/geocaching/Geocache.java5
-rw-r--r--main/src/cgeo/geocaching/SearchActivity.java4
-rw-r--r--main/src/cgeo/geocaching/TrackableActivity.java50
-rw-r--r--main/src/cgeo/geocaching/Waypoint.java11
-rw-r--r--main/src/cgeo/geocaching/activity/ActivityMixin.java11
-rw-r--r--main/src/cgeo/geocaching/apps/cache/navi/SygicNavigationApp.java14
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCParser.java26
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OkapiClient.java1
-rw-r--r--main/src/cgeo/geocaching/loaders/AddressGeocacheListLoader.java23
-rw-r--r--main/src/cgeo/geocaching/location/AndroidGeocoder.java59
-rw-r--r--main/src/cgeo/geocaching/location/GCGeocoder.java65
-rw-r--r--main/src/cgeo/geocaching/location/Geocoder.java61
-rw-r--r--main/src/cgeo/geocaching/location/GeopointParser.java2
-rw-r--r--main/src/cgeo/geocaching/location/MapQuestGeocoder.java117
-rw-r--r--main/src/cgeo/geocaching/ui/AddressListAdapter.java5
-rw-r--r--main/src/cgeo/geocaching/ui/CacheDetailsCreator.java40
20 files changed, 357 insertions, 226 deletions
diff --git a/main/src/cgeo/geocaching/AddressListActivity.java b/main/src/cgeo/geocaching/AddressListActivity.java
index 4f71ab6..4f81f8b 100644
--- a/main/src/cgeo/geocaching/AddressListActivity.java
+++ b/main/src/cgeo/geocaching/AddressListActivity.java
@@ -1,14 +1,17 @@
package cgeo.geocaching;
import cgeo.geocaching.activity.AbstractListActivity;
-import cgeo.geocaching.location.Geocoder;
+import cgeo.geocaching.location.AndroidGeocoder;
+import cgeo.geocaching.location.GCGeocoder;
+import cgeo.geocaching.location.MapQuestGeocoder;
import cgeo.geocaching.ui.AddressListAdapter;
-import org.apache.commons.collections4.CollectionUtils;
+import rx.Observable;
+import rx.android.observables.AndroidObservable;
+import rx.functions.Action1;
import android.app.ProgressDialog;
import android.location.Address;
-import android.os.AsyncTask;
import android.os.Bundle;
import java.util.List;
@@ -19,47 +22,35 @@ public class AddressListActivity extends AbstractListActivity {
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState, R.layout.addresslist_activity);
- // get parameters
- final String keyword = getIntent().getStringExtra(Intents.EXTRA_KEYWORD);
-
- if (keyword == null) {
- showToast(res.getString(R.string.err_search_address_forgot));
- finish();
- return;
- }
final AddressListAdapter adapter = new AddressListAdapter(this);
setListAdapter(adapter);
+ final String keyword = getIntent().getStringExtra(Intents.EXTRA_KEYWORD);
final ProgressDialog waitDialog =
ProgressDialog.show(this, res.getString(R.string.search_address_started), keyword, true);
waitDialog.setCancelable(true);
-
lookupAddressInBackground(keyword, adapter, waitDialog);
}
private void lookupAddressInBackground(final String keyword, final AddressListAdapter adapter, final ProgressDialog waitDialog) {
- new AsyncTask<Void, Void, List<Address>>() {
-
+ final Observable<Address> geocoderObservable = new AndroidGeocoder(this).getFromLocationName(keyword)
+ .onErrorResumeNext(MapQuestGeocoder.getFromLocationName(keyword))
+ .onErrorResumeNext(GCGeocoder.getFromLocationName(keyword));
+ AndroidObservable.bindActivity(this, geocoderObservable.toList()).subscribe(new Action1<List<Address>>() {
@Override
- protected List<Address> doInBackground(final Void... params) {
- final Geocoder geocoder = new Geocoder(AddressListActivity.this);
- return geocoder.getFromLocationName(keyword);
- }
-
- @Override
- protected void onPostExecute(final List<Address> addresses) {
+ public void call(final List<Address> addresses) {
waitDialog.dismiss();
- if (CollectionUtils.isNotEmpty(addresses)) {
- for (final Address address : addresses) {
- adapter.add(address); // don't use addAll, it's only available with API >= 11
- }
- } else {
- finish();
- CacheListActivity.startActivityAddress(AddressListActivity.this, null, keyword);
+ for (final Address address : addresses) {
+ adapter.add(address); // don't use addAll, it's only available with API >= 11
}
}
-
- }.execute();
+ }, new Action1<Throwable>() {
+ @Override
+ public void call(final Throwable throwable) {
+ finish();
+ showToast(res.getString(R.string.err_unknown_address));
+ }
+ });
}
} \ No newline at end of file
diff --git a/main/src/cgeo/geocaching/CacheDetailActivity.java b/main/src/cgeo/geocaching/CacheDetailActivity.java
index 169238b..85597e7 100644
--- a/main/src/cgeo/geocaching/CacheDetailActivity.java
+++ b/main/src/cgeo/geocaching/CacheDetailActivity.java
@@ -115,6 +115,7 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
+import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.TextView.BufferType;
@@ -952,10 +953,9 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
* Creator for details-view.
*/
private class DetailsViewCreator extends AbstractCachingPageViewCreator<ScrollView> {
- /**
- * Reference to the details list, so that the helper-method can access it without an additional argument
- */
+ // Reference to the details list and favorite line, so that the helper-method can access them without an additional argument
private LinearLayout detailsList;
+ private ImmutablePair<RelativeLayout, TextView> favoriteLine;
@Override
public ScrollView getDispatchedView(final ViewGroup parentView) {
@@ -992,10 +992,10 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
span.setSpan(new ForegroundColorSpan(res.getColor(R.color.archived_cache_color)), 0, span.toString().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
- addContextMenu(details.add(R.string.cache_name, span));
+ addContextMenu(details.add(R.string.cache_name, span).right);
details.add(R.string.cache_type, cache.getType().getL10n());
details.addSize(cache);
- addContextMenu(details.add(R.string.cache_geocode, cache.getGeocode()));
+ addContextMenu(details.add(R.string.cache_geocode, cache.getGeocode()).right);
details.addCacheState(cache);
details.addDistance(cache, cacheDistanceView);
@@ -1006,9 +1006,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
details.addRating(cache);
// favorite count
- if (cache.getFavoritePoints() > 0) {
- details.add(R.string.cache_favorite, cache.getFavoritePoints() + "×");
- }
+ favoriteLine = details.add(R.string.cache_favorite, "");
// own rating
if (cache.getMyVote() > 0) {
@@ -1017,7 +1015,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
// cache author
if (StringUtils.isNotBlank(cache.getOwnerDisplayName()) || StringUtils.isNotBlank(cache.getOwnerUserId())) {
- final TextView ownerView = details.add(R.string.cache_owner, "");
+ final TextView ownerView = details.add(R.string.cache_owner, "").right;
if (StringUtils.isNotBlank(cache.getOwnerDisplayName())) {
ownerView.setText(cache.getOwnerDisplayName(), TextView.BufferType.SPANNABLE);
} else { // OwnerReal guaranteed to be not blank based on above
@@ -1039,7 +1037,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
// cache coordinates
if (cache.getCoords() != null) {
- final TextView valueView = details.add(R.string.cache_coordinates, cache.getCoords().toString());
+ final TextView valueView = details.add(R.string.cache_coordinates, cache.getCoords().toString()).right;
valueView.setOnClickListener(new CoordinatesFormatSwitcher(cache.getCoords()));
addContextMenu(valueView);
}
@@ -1340,6 +1338,14 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
buttonRemove.setEnabled(false);
buttonRemove.setVisibility(View.GONE);
}
+
+ // Favorite counts
+ if (cache.getFavoritePoints() > 0) {
+ favoriteLine.left.setVisibility(View.VISIBLE);
+ favoriteLine.right.setText(cache.getFavoritePoints() + "×");
+ } else {
+ favoriteLine.left.setVisibility(View.GONE);
+ }
}
/**
diff --git a/main/src/cgeo/geocaching/CacheListActivity.java b/main/src/cgeo/geocaching/CacheListActivity.java
index c8f1101..1b6a268 100644
--- a/main/src/cgeo/geocaching/CacheListActivity.java
+++ b/main/src/cgeo/geocaching/CacheListActivity.java
@@ -27,7 +27,6 @@ import cgeo.geocaching.list.PseudoList;
import cgeo.geocaching.list.StoredList;
import cgeo.geocaching.loaders.AbstractSearchLoader;
import cgeo.geocaching.loaders.AbstractSearchLoader.CacheListLoaderType;
-import cgeo.geocaching.loaders.AddressGeocacheListLoader;
import cgeo.geocaching.loaders.CoordsGeocacheListLoader;
import cgeo.geocaching.loaders.FinderGeocacheListLoader;
import cgeo.geocaching.loaders.HistoryGeocacheListLoader;
@@ -1549,12 +1548,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA
} else {
title = coords.toString();
}
- if (coords != null) {
- loader = new CoordsGeocacheListLoader(app, coords);
- }
- else {
- loader = new AddressGeocacheListLoader(app, address);
- }
+ loader = new CoordsGeocacheListLoader(app, coords);
break;
case FINDER:
final String username = extras.getString(Intents.EXTRA_USERNAME);
diff --git a/main/src/cgeo/geocaching/DataStore.java b/main/src/cgeo/geocaching/DataStore.java
index fae0d38..1081828 100644
--- a/main/src/cgeo/geocaching/DataStore.java
+++ b/main/src/cgeo/geocaching/DataStore.java
@@ -1103,7 +1103,9 @@ public class DataStore {
for (final Geocache cache : caches) {
final String geocode = cache.getGeocode();
final Geocache existingCache = existingCaches.get(geocode);
- final boolean dbUpdateRequired = !cache.gatherMissingFrom(existingCache) || cacheCache.getCacheFromCache(geocode) != null;
+ boolean dbUpdateRequired = !cache.gatherMissingFrom(existingCache) || cacheCache.getCacheFromCache(geocode) != null;
+ // parse the note AFTER merging the local information in
+ dbUpdateRequired |= cache.parseWaypointsFromNote();
cache.addStorageLocation(StorageLocation.CACHE);
cacheCache.putCacheInCache(cache);
diff --git a/main/src/cgeo/geocaching/Geocache.java b/main/src/cgeo/geocaching/Geocache.java
index 69babc4..d396b93 100644
--- a/main/src/cgeo/geocaching/Geocache.java
+++ b/main/src/cgeo/geocaching/Geocache.java
@@ -1368,12 +1368,15 @@ public class Geocache implements IWaypoint {
/**
* Detect coordinates in the personal note and convert them to user defined waypoints. Works by rule of thumb.
*/
- public void parseWaypointsFromNote() {
+ public boolean parseWaypointsFromNote() {
+ boolean changed = false;
for (final Waypoint waypoint : Waypoint.parseWaypointsFromNote(StringUtils.defaultString(getPersonalNote()))) {
if (!hasIdenticalWaypoint(waypoint.getCoords())) {
addOrChangeWaypoint(waypoint, false);
+ changed = true;
}
}
+ return changed;
}
private boolean hasIdenticalWaypoint(final Geopoint point) {
diff --git a/main/src/cgeo/geocaching/SearchActivity.java b/main/src/cgeo/geocaching/SearchActivity.java
index 9de279d..1606350 100644
--- a/main/src/cgeo/geocaching/SearchActivity.java
+++ b/main/src/cgeo/geocaching/SearchActivity.java
@@ -373,7 +373,7 @@ public class SearchActivity extends AbstractActionBarActivity implements Coordin
}
private void findByGeocodeFn() {
- final String geocodeText = StringUtils.trim(geocodeEditText.getText().toString());
+ final String geocodeText = StringUtils.trimToEmpty(geocodeEditText.getText().toString());
if (StringUtils.isBlank(geocodeText) || geocodeText.equalsIgnoreCase("GC")) {
Dialogs.message(this, R.string.warn_search_help_title, R.string.warn_search_help_gccode);
@@ -384,7 +384,7 @@ public class SearchActivity extends AbstractActionBarActivity implements Coordin
}
private void findTrackableFn() {
- final String trackableText = StringUtils.trim(trackableEditText.getText().toString());
+ final String trackableText = StringUtils.trimToEmpty(trackableEditText.getText().toString());
if (StringUtils.isBlank(trackableText) || trackableText.equalsIgnoreCase("TB")) {
Dialogs.message(this, R.string.warn_search_help_title, R.string.warn_search_help_tb);
diff --git a/main/src/cgeo/geocaching/TrackableActivity.java b/main/src/cgeo/geocaching/TrackableActivity.java
index fec1157..a9f508c 100644
--- a/main/src/cgeo/geocaching/TrackableActivity.java
+++ b/main/src/cgeo/geocaching/TrackableActivity.java
@@ -160,7 +160,7 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi
return;
}
- String message;
+ final String message;
if (StringUtils.isNotBlank(name)) {
message = Html.fromHtml(name).toString();
} else if (StringUtils.isNotBlank(geocode)) {
@@ -394,10 +394,10 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi
}
// trackable name
- addContextMenu(details.add(R.string.trackable_name, StringUtils.isNotBlank(trackable.getName()) ? Html.fromHtml(trackable.getName()).toString() : res.getString(R.string.trackable_unknown)));
+ addContextMenu(details.add(R.string.trackable_name, StringUtils.isNotBlank(trackable.getName()) ? Html.fromHtml(trackable.getName()).toString() : res.getString(R.string.trackable_unknown)).right);
// trackable type
- String tbType;
+ final String tbType;
if (StringUtils.isNotBlank(trackable.getType())) {
tbType = Html.fromHtml(trackable.getType()).toString();
} else {
@@ -406,10 +406,10 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi
details.add(R.string.trackable_type, tbType);
// trackable geocode
- addContextMenu(details.add(R.string.trackable_code, trackable.getGeocode()));
+ addContextMenu(details.add(R.string.trackable_code, trackable.getGeocode()).right);
// trackable owner
- final TextView owner = details.add(R.string.trackable_owner, res.getString(R.string.trackable_unknown));
+ final TextView owner = details.add(R.string.trackable_owner, res.getString(R.string.trackable_unknown)).right;
if (StringUtils.isNotBlank(trackable.getOwner())) {
owner.setText(Html.fromHtml(trackable.getOwner()), TextView.BufferType.SPANNABLE);
owner.setOnClickListener(new UserActionsClickListener(trackable));
@@ -419,20 +419,26 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi
if (StringUtils.isNotBlank(trackable.getSpottedName()) ||
trackable.getSpottedType() == Trackable.SPOTTED_UNKNOWN ||
trackable.getSpottedType() == Trackable.SPOTTED_OWNER) {
+
+ final StringBuilder text;
boolean showTimeSpan = true;
- StringBuilder text;
-
- if (trackable.getSpottedType() == Trackable.SPOTTED_CACHE) {
- text = new StringBuilder(res.getString(R.string.trackable_spotted_in_cache) + ' ' + Html.fromHtml(trackable.getSpottedName()).toString());
- } else if (trackable.getSpottedType() == Trackable.SPOTTED_USER) {
- text = new StringBuilder(res.getString(R.string.trackable_spotted_at_user) + ' ' + Html.fromHtml(trackable.getSpottedName()).toString());
- } else if (trackable.getSpottedType() == Trackable.SPOTTED_UNKNOWN) {
- text = new StringBuilder(res.getString(R.string.trackable_spotted_unknown_location));
- } else if (trackable.getSpottedType() == Trackable.SPOTTED_OWNER) {
- text = new StringBuilder(res.getString(R.string.trackable_spotted_owner));
- } else {
- text = new StringBuilder("N/A");
- showTimeSpan = false;
+ switch (trackable.getSpottedType()) {
+ case Trackable.SPOTTED_CACHE:
+ text = new StringBuilder(res.getString(R.string.trackable_spotted_in_cache) + ' ' + Html.fromHtml(trackable.getSpottedName()).toString());
+ break;
+ case Trackable.SPOTTED_USER:
+ text = new StringBuilder(res.getString(R.string.trackable_spotted_at_user) + ' ' + Html.fromHtml(trackable.getSpottedName()).toString());
+ break;
+ case Trackable.SPOTTED_UNKNOWN:
+ text = new StringBuilder(res.getString(R.string.trackable_spotted_unknown_location));
+ break;
+ case Trackable.SPOTTED_OWNER:
+ text = new StringBuilder(res.getString(R.string.trackable_spotted_owner));
+ break;
+ default:
+ text = new StringBuilder("N/A");
+ showTimeSpan = false;
+ break;
}
// days since last spotting
@@ -445,7 +451,7 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi
}
}
- final TextView spotted = details.add(R.string.trackable_spotted, text.toString());
+ final TextView spotted = details.add(R.string.trackable_spotted, text.toString()).right;
spotted.setClickable(true);
if (Trackable.SPOTTED_CACHE == trackable.getSpottedType()) {
spotted.setOnClickListener(new View.OnClickListener() {
@@ -472,19 +478,19 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi
// trackable origin
if (StringUtils.isNotBlank(trackable.getOrigin())) {
- final TextView origin = details.add(R.string.trackable_origin, "");
+ final TextView origin = details.add(R.string.trackable_origin, "").right;
origin.setText(Html.fromHtml(trackable.getOrigin()), TextView.BufferType.SPANNABLE);
addContextMenu(origin);
}
// trackable released
if (trackable.getReleased() != null) {
- addContextMenu(details.add(R.string.trackable_released, Formatter.formatDate(trackable.getReleased().getTime())));
+ addContextMenu(details.add(R.string.trackable_released, Formatter.formatDate(trackable.getReleased().getTime())).right);
}
// trackable distance
if (trackable.getDistance() >= 0) {
- addContextMenu(details.add(R.string.trackable_distance, Units.getDistanceFromKilometers(trackable.getDistance())));
+ addContextMenu(details.add(R.string.trackable_distance, Units.getDistanceFromKilometers(trackable.getDistance())).right);
}
// trackable goal
diff --git a/main/src/cgeo/geocaching/Waypoint.java b/main/src/cgeo/geocaching/Waypoint.java
index 5cfeb29..b4979e4 100644
--- a/main/src/cgeo/geocaching/Waypoint.java
+++ b/main/src/cgeo/geocaching/Waypoint.java
@@ -97,18 +97,11 @@ public class Waypoint implements IWaypoint {
if (newPrefixes.containsKey(prefix)) {
newPrefixes.get(prefix).merge(oldWaypoint);
} else if (oldWaypoint.isUserDefined() || forceMerge) {
- // personal note waypoints should always be taken from the new list only
- if (!isPersonalNoteWaypoint(oldWaypoint)) {
- newPoints.add(oldWaypoint);
- }
+ newPoints.add(oldWaypoint);
}
}
}
- private static boolean isPersonalNoteWaypoint(final @NonNull Waypoint waypoint) {
- return StringUtils.startsWith(waypoint.getName(), CgeoApplication.getInstance().getString(R.string.cache_personal_note) + " ");
- }
-
public boolean isUserDefined() {
return own || WaypointType.OWN == waypointType;
}
@@ -304,7 +297,7 @@ public class Waypoint implements IWaypoint {
((point.getLatitudeE6() % 1000) != 0 || (point.getLongitudeE6() % 1000) != 0)) {
final String name = CgeoApplication.getInstance().getString(R.string.cache_personal_note) + " " + count;
final String potentialWaypointType = note.substring(Math.max(0, matcher.start() - 15));
- final Waypoint waypoint = new Waypoint(name, parseWaypointType(potentialWaypointType), false);
+ final Waypoint waypoint = new Waypoint(name, parseWaypointType(potentialWaypointType), true);
waypoint.setCoords(point);
waypoints.add(waypoint);
count++;
diff --git a/main/src/cgeo/geocaching/activity/ActivityMixin.java b/main/src/cgeo/geocaching/activity/ActivityMixin.java
index b135358..14a2fbf 100644
--- a/main/src/cgeo/geocaching/activity/ActivityMixin.java
+++ b/main/src/cgeo/geocaching/activity/ActivityMixin.java
@@ -157,6 +157,15 @@ public final class ActivityMixin {
editText.setSelection(newCursor);
}
+ /**
+ * This is the exact code from Google to implement Up navigation, with one exception: activity.isTaskRoot() was
+ * added as {@link NavUtils#shouldUpRecreateTask(Activity, Intent)} seems not to handle the case, that this activity
+ * was created from an intent by another app, and our own app is not yet running. The bug seems to be fixed in
+ * Android 4.4.something, however.
+ *
+ * @param activity
+ * @return
+ */
public static boolean navigateUp(@NonNull final Activity activity) {
// see http://developer.android.com/training/implementing-navigation/ancestral.html
final Intent upIntent = NavUtils.getParentActivityIntent(activity);
@@ -164,7 +173,7 @@ public final class ActivityMixin {
activity.finish();
return true;
}
- if (NavUtils.shouldUpRecreateTask(activity, upIntent)) {
+ if (NavUtils.shouldUpRecreateTask(activity, upIntent) || activity.isTaskRoot()) {
// This activity is NOT part of this app's task, so create a new task
// when navigating up, with a synthesized back stack.
TaskStackBuilder.create(activity)
diff --git a/main/src/cgeo/geocaching/apps/cache/navi/SygicNavigationApp.java b/main/src/cgeo/geocaching/apps/cache/navi/SygicNavigationApp.java
index 5d667c3..391fa90 100644
--- a/main/src/cgeo/geocaching/apps/cache/navi/SygicNavigationApp.java
+++ b/main/src/cgeo/geocaching/apps/cache/navi/SygicNavigationApp.java
@@ -2,6 +2,7 @@ package cgeo.geocaching.apps.cache.navi;
import cgeo.geocaching.R;
import cgeo.geocaching.location.Geopoint;
+import cgeo.geocaching.utils.ProcessUtils;
import android.app.Activity;
import android.content.Intent;
@@ -14,10 +15,19 @@ import android.net.Uri;
*/
class SygicNavigationApp extends AbstractPointNavigationApp {
- private static final String PACKAGE = "com.sygic.aura";
+ private static final String PACKAGE_NORMAL = "com.sygic.aura";
+ /**
+ * there is a secondary edition of this app
+ */
+ private static final String PACKAGE_VOUCHER = "com.sygic.aura_voucher";
SygicNavigationApp() {
- super(getString(R.string.cache_menu_sygic), R.id.cache_app_sygic, null, PACKAGE);
+ super(getString(R.string.cache_menu_sygic), R.id.cache_app_sygic, null, PACKAGE_NORMAL);
+ }
+
+ @Override
+ public boolean isInstalled() {
+ return ProcessUtils.isLaunchable(PACKAGE_NORMAL) || ProcessUtils.isLaunchable(PACKAGE_VOUCHER);
}
@Override
diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java
index 33447c7..a0784a8 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCParser.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java
@@ -771,8 +771,6 @@ public abstract class GCParser {
}
}
- cache.parseWaypointsFromNote();
-
// last check for necessary cache conditions
if (StringUtils.isBlank(cache.getGeocode())) {
return UNKNOWN_PARSE_ERROR;
@@ -940,30 +938,6 @@ public abstract class GCParser {
return searchByAny(cacheType, isSearchForMyCaches(userName), showCaptcha, params, recaptchaReceiver);
}
- public static SearchResult searchByAddress(final String address, final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) {
- if (StringUtils.isBlank(address)) {
- Log.e("GCParser.searchByAddress: No address given");
- return null;
- }
-
- final ObjectNode response = Network.requestJSON("http://www.geocaching.com/api/geocode", new Parameters("q", address));
- if (response == null) {
- return null;
- }
-
- if (!StringUtils.equalsIgnoreCase(response.path("status").asText(), "success")) {
- return null;
- }
-
- final JsonNode data = response.path("data");
- final JsonNode latNode = data.get("lat");
- final JsonNode lngNode = data.get("lng");
- if (latNode == null || lngNode == null) {
- return null;
- }
- return searchByCoords(new Geopoint(latNode.asDouble(), lngNode.asDouble()), cacheType, showCaptcha, recaptchaReceiver);
- }
-
@Nullable
public static Trackable searchTrackable(final String geocode, final String guid, final String id) {
if (StringUtils.isBlank(geocode) && StringUtils.isBlank(guid) && StringUtils.isBlank(id)) {
diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
index 26e0f78..a1f8029 100644
--- a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
+++ b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
@@ -397,7 +397,6 @@ final class OkapiClient {
}
if (response.hasNonNull(CACHE_MY_NOTES)) {
cache.setPersonalNote(response.get(CACHE_MY_NOTES).asText());
- cache.parseWaypointsFromNote();
}
cache.setLogPasswordRequired(response.get(CACHE_REQ_PASSWORD).asBoolean());
diff --git a/main/src/cgeo/geocaching/loaders/AddressGeocacheListLoader.java b/main/src/cgeo/geocaching/loaders/AddressGeocacheListLoader.java
deleted file mode 100644
index e1573c9..0000000
--- a/main/src/cgeo/geocaching/loaders/AddressGeocacheListLoader.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package cgeo.geocaching.loaders;
-
-import cgeo.geocaching.SearchResult;
-import cgeo.geocaching.connector.gc.GCParser;
-import cgeo.geocaching.settings.Settings;
-
-import android.content.Context;
-
-public class AddressGeocacheListLoader extends AbstractSearchLoader {
-
- private final String address;
-
- public AddressGeocacheListLoader(Context context, String address) {
- super(context);
- this.address = address;
- }
-
- @Override
- public SearchResult runSearch() {
- return GCParser.searchByAddress(address, Settings.getCacheType(), Settings.isShowCaptcha(), this);
- }
-
-}
diff --git a/main/src/cgeo/geocaching/location/AndroidGeocoder.java b/main/src/cgeo/geocaching/location/AndroidGeocoder.java
new file mode 100644
index 0000000..98ea285
--- /dev/null
+++ b/main/src/cgeo/geocaching/location/AndroidGeocoder.java
@@ -0,0 +1,59 @@
+package cgeo.geocaching.location;
+
+import cgeo.geocaching.utils.Log;
+import cgeo.geocaching.utils.RxUtils;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.eclipse.jdt.annotation.NonNull;
+
+import rx.Observable;
+import rx.functions.Func0;
+
+import android.content.Context;
+import android.location.Address;
+import android.location.Geocoder;
+
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Encapsulation of the Android {@link Geocoder} with default error handling. All methods of this class
+ * are blocking and will do network lookups.
+ *
+ */
+public class AndroidGeocoder {
+ private final Geocoder geocoder;
+
+ public AndroidGeocoder(final Context context) {
+ geocoder = new Geocoder(context, Locale.getDefault());
+ }
+
+ /**
+ * Retrieve addresses from a textual location using Android geocoding API. The work happens on the network
+ * scheduler.
+ *
+ * @param keyword
+ * the location
+ * @return an observable containing zero or more locations
+ *
+ * @see Geocoder#getFromLocationName(String, int)
+ */
+ public Observable<Address> getFromLocationName(@NonNull final String keyword) {
+ return Observable.defer(new Func0<Observable<Address>>() {
+ @Override
+ public Observable<Address> call() {
+ try {
+ final List<Address> addresses = geocoder.getFromLocationName(keyword, 20);
+ if (CollectionUtils.isEmpty(addresses)) {
+ return Observable.error(new RuntimeException("no result from Android geocoder"));
+ }
+ return Observable.from(addresses);
+ } catch (final Exception e) {
+ Log.i("Unable to use Android geocoder: " + e.getMessage());
+ return Observable.error(e);
+ }
+ }
+ }).subscribeOn(RxUtils.networkScheduler);
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/location/GCGeocoder.java b/main/src/cgeo/geocaching/location/GCGeocoder.java
new file mode 100644
index 0000000..549044f
--- /dev/null
+++ b/main/src/cgeo/geocaching/location/GCGeocoder.java
@@ -0,0 +1,65 @@
+package cgeo.geocaching.location;
+
+import cgeo.geocaching.network.Network;
+import cgeo.geocaching.network.Parameters;
+import cgeo.geocaching.settings.Settings;
+import cgeo.geocaching.utils.Log;
+import cgeo.geocaching.utils.RxUtils;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jdt.annotation.NonNull;
+
+import rx.Observable;
+import rx.functions.Func0;
+
+import android.location.Address;
+
+import java.util.Locale;
+
+public class GCGeocoder {
+
+ private GCGeocoder() {
+ // Do not instantiate
+ }
+
+ /**
+ * Retrieve addresses from a textual location using geocaching.com geocoding API. The work happens on the network
+ * scheduler.
+ *
+ * @param address
+ * the location
+ * @return an observable containing zero or more locations
+ *
+ * @see android.location.Geocoder#getFromLocationName(String, int)
+ */
+ public static Observable<Address> getFromLocationName(@NonNull final String address) {
+ return Observable.defer(new Func0<Observable<Address>>() {
+ @Override
+ public Observable<Address> call() {
+ if (!Settings.isGCConnectorActive()) {
+ return Observable.error(new RuntimeException("geocaching.com connector is not active"));
+ }
+ final ObjectNode response = Network.requestJSON("https://www.geocaching.com/api/geocode", new Parameters("q", address));
+ if (response == null || !StringUtils.equalsIgnoreCase(response.path("status").asText(), "success")) {
+ return Observable.error(new RuntimeException("unable to use geocaching.com geocoder"));
+ }
+
+ final JsonNode data = response.path("data");
+ final Address geocodedAddress = new Address(Locale.getDefault());
+ try {
+ geocodedAddress.setLatitude(data.get("lat").asDouble());
+ geocodedAddress.setLongitude(data.get("lng").asDouble());
+ geocodedAddress.setAddressLine(0, address);
+ return Observable.just(geocodedAddress);
+ } catch (final Exception e) {
+ Log.e("unable to decode answer from geocaching.com geocoder", e);
+ return Observable.error(e);
+ }
+ }
+ }).subscribeOn(RxUtils.networkScheduler);
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/location/Geocoder.java b/main/src/cgeo/geocaching/location/Geocoder.java
deleted file mode 100644
index 1582daa..0000000
--- a/main/src/cgeo/geocaching/location/Geocoder.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package cgeo.geocaching.location;
-
-import cgeo.geocaching.utils.Log;
-
-import org.apache.commons.lang3.StringUtils;
-import org.eclipse.jdt.annotation.NonNull;
-
-import android.content.Context;
-import android.location.Address;
-
-import java.io.IOException;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Encapsulation of the Android {@link android.location.Geocoder} with default error handling. All methods of this class
- * are blocking and will do network lookups.
- *
- */
-public class Geocoder {
- private final android.location.Geocoder geocoder;
-
- public Geocoder(final Context context) {
- geocoder = new android.location.Geocoder(context, Locale.getDefault());
- }
-
- /**
- * @param keyword
- * @return
- *
- * @see android.location.Geocoder#getFromLocationName(String, int)
- */
- public @NonNull List<Address> getFromLocationName(final String keyword) {
- try {
- return geocoder.getFromLocationName(keyword, 20);
- } catch (final Exception e) {
- handleException(e);
- return Collections.emptyList();
- }
- }
-
- public @NonNull List<Address> getFromLocation(final Geopoint coords) {
- try {
- return geocoder.getFromLocation(coords.getLatitude(), coords.getLongitude(), 20);
- } catch (final IOException e) {
- handleException(e);
- return Collections.emptyList();
- }
- }
-
- private static void handleException(final Exception e) {
- // non Google devices come without the geocoder
- if (StringUtils.containsIgnoreCase(e.getMessage(), "Service not Available")) {
- Log.i("No geocoder available");
- }
- else {
- Log.e("Geocoder", e);
- }
- }
-}
diff --git a/main/src/cgeo/geocaching/location/GeopointParser.java b/main/src/cgeo/geocaching/location/GeopointParser.java
index e73e787..a6b8e45 100644
--- a/main/src/cgeo/geocaching/location/GeopointParser.java
+++ b/main/src/cgeo/geocaching/location/GeopointParser.java
@@ -122,7 +122,7 @@ class GeopointParser {
// Nothing found with "N 52...", try to match string as decimal degree parts (i.e. multiple doubles)
try {
- final String[] items = StringUtils.split(text.trim());
+ final String[] items = StringUtils.split(StringUtils.trimToEmpty(text));
if (items.length > 0 && items.length <= 2) {
final int index = (latlon == LatLon.LON ? items.length - 1 : 0);
final String textPart = items[index];
diff --git a/main/src/cgeo/geocaching/location/MapQuestGeocoder.java b/main/src/cgeo/geocaching/location/MapQuestGeocoder.java
new file mode 100644
index 0000000..537ae40
--- /dev/null
+++ b/main/src/cgeo/geocaching/location/MapQuestGeocoder.java
@@ -0,0 +1,117 @@
+package cgeo.geocaching.location;
+
+import cgeo.geocaching.network.Network;
+import cgeo.geocaching.network.Parameters;
+import cgeo.geocaching.utils.Log;
+import cgeo.geocaching.utils.RxUtils;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jdt.annotation.NonNull;
+
+import rx.Observable;
+import rx.Observable.OnSubscribe;
+import rx.Subscriber;
+import rx.functions.Func0;
+
+import android.location.Address;
+
+import java.util.Locale;
+
+public class MapQuestGeocoder {
+
+ private static final String MAPQUEST_KEY = "Fmjtd|luurn1u2n9,bs=o5-9wynua";
+
+ private MapQuestGeocoder() {
+ // Do not instantiate
+ }
+
+ /**
+ * Retrieve addresses from a textual location using MapQuest geocoding API. The work happens on the network
+ * scheduler.
+ *
+ * @param address
+ * the location
+ * @return an observable containing zero or more locations
+ *
+ * @see android.location.Geocoder#getFromLocationName(String, int)
+ */
+ public static Observable<Address> getFromLocationName(@NonNull final String address) {
+ return Observable.defer(new Func0<Observable<Address>>() {
+ @Override
+ public Observable<Address> call() {
+ final ObjectNode response = Network.requestJSON("https://www.mapquestapi.com/geocoding/v1/address",
+ new Parameters("key", MAPQUEST_KEY, "location", address, "maxResults", "20", "thumbMaps", "false"));
+ if (response == null) {
+ Log.w("MapQuest decoder error: no response");
+ return Observable.error(new RuntimeException("no answer from MapQuest geocoder"));
+ }
+ final int statusCode = response.path("info").path("statuscode").asInt(-1);
+ if (statusCode != 0) {
+ Log.w("MapQuest decoder error: statuscode is not 0");
+ return Observable.error(new RuntimeException("no correct answer from MapQuest geocoder"));
+ }
+ return Observable.create(new OnSubscribe<Address>() {
+ @Override
+ public void call(final Subscriber<? super Address> subscriber) {
+ try {
+ for (final JsonNode address: response.get("results").get(0).get("locations")) {
+ subscriber.onNext(mapquestToAddress(address));
+ }
+ subscriber.onCompleted();
+ } catch (final Exception e) {
+ Log.e("Error decoding MapQuest address", e);
+ subscriber.onError(e);
+ }
+ }
+ });
+ }
+ }).subscribeOn(RxUtils.networkScheduler);
+ }
+
+ private static Address mapquestToAddress(final JsonNode mapquestAddress) {
+ final Address address = new Address(Locale.getDefault());
+ for (int i = 1; i <= 6; i++) {
+ final String adminAreaName = "adminArea" + i;
+ setComponent(address, mapquestAddress, adminAreaName, mapquestAddress.path(adminAreaName + "Type").asText());
+ }
+ setComponent(address, mapquestAddress, "postalCode", "PostalCode");
+ int index = 0;
+ for (final String addressComponent: new String[]{ mapquestAddress.path("street").asText(), address.getSubLocality(), address.getLocality(),
+ address.getPostalCode(), address.getSubAdminArea(), address.getAdminArea(), address.getCountryCode() }) {
+ if (StringUtils.isNotBlank(addressComponent)) {
+ address.setAddressLine(index++, addressComponent);
+ }
+ }
+ address.setLatitude(mapquestAddress.get("latLng").get("lat").asDouble());
+ address.setLongitude(mapquestAddress.get("latLng").get("lng").asDouble());
+ return address;
+ }
+
+ private static void setComponent(final Address address, final JsonNode mapquestAddress, final String adminArea, final String adminAreaType) {
+ final String content = StringUtils.trimToNull(mapquestAddress.path(adminArea).asText());
+ switch (adminAreaType) {
+ case "City":
+ address.setLocality(content);
+ break;
+ case "Neighborhood":
+ address.setSubLocality(content);
+ break;
+ case "PostalCode":
+ address.setPostalCode(content);
+ break;
+ case "State":
+ address.setAdminArea(content);
+ break;
+ case "County":
+ address.setSubAdminArea(content);
+ break;
+ case "Country":
+ address.setCountryCode(content);
+ break;
+ }
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/ui/AddressListAdapter.java b/main/src/cgeo/geocaching/ui/AddressListAdapter.java
index 691c8d2..455954a 100644
--- a/main/src/cgeo/geocaching/ui/AddressListAdapter.java
+++ b/main/src/cgeo/geocaching/ui/AddressListAdapter.java
@@ -9,6 +9,7 @@ import cgeo.geocaching.location.Geopoint;
import cgeo.geocaching.location.Units;
import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jdt.annotation.NonNull;
import android.app.Activity;
import android.location.Address;
@@ -23,7 +24,7 @@ import java.util.ArrayList;
public class AddressListAdapter extends ArrayAdapter<Address> {
final private LayoutInflater inflater;
- final private Geopoint location;
+ @NonNull final private Geopoint location;
protected static final class ViewHolder extends AbstractViewHolder {
@InjectView(R.id.label) protected TextView label;
@@ -72,7 +73,7 @@ public class AddressListAdapter extends ArrayAdapter<Address> {
}
private CharSequence getDistanceText(final Address address) {
- if (location != null && address.hasLatitude() && address.hasLongitude()) {
+ if (address.hasLatitude() && address.hasLongitude()) {
return Units.getDistanceFromKilometers(location.distanceTo(new Geopoint(address.getLatitude(), address.getLongitude())));
}
diff --git a/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java b/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java
index 55e9cfc..7dd3847 100644
--- a/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java
+++ b/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java
@@ -12,16 +12,15 @@ import cgeo.geocaching.location.Units;
import cgeo.geocaching.utils.Formatter;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
import org.eclipse.jdt.annotation.NonNull;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.res.Resources;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
+import android.widget.RatingBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -45,18 +44,20 @@ public final class CacheDetailsCreator {
}
/**
- * @param nameId
- * @param value
- * @return the view containing the displayed string (i.e. the right side one from the pair of "label": "value")
+ * Create a "name: value" line.
+ *
+ * @param nameId the resource of the name field
+ * @param value the initial value
+ * @return a pair made of the whole "name: value" line (to be able to hide it for example) and of the value (to update it)
*/
- public TextView add(final int nameId, final CharSequence value) {
+ public ImmutablePair<RelativeLayout, TextView> add(final int nameId, final CharSequence value) {
final RelativeLayout layout = (RelativeLayout) activity.getLayoutInflater().inflate(R.layout.cache_information_item, null, false);
final TextView nameView = ButterKnife.findById(layout, R.id.name);
nameView.setText(res.getString(nameId));
lastValueView = ButterKnife.findById(layout, R.id.value);
lastValueView.setText(value);
parentView.addView(layout);
- return lastValueView;
+ return ImmutablePair.of(layout, lastValueView);
}
public TextView getValueView() {
@@ -71,33 +72,18 @@ public final class CacheDetailsCreator {
final RelativeLayout layout = (RelativeLayout) activity.getLayoutInflater().inflate(R.layout.cache_information_item, null, false);
final TextView nameView = ButterKnife.findById(layout, R.id.name);
lastValueView = ButterKnife.findById(layout, R.id.value);
- final LinearLayout layoutStars = ButterKnife.findById(layout, R.id.stars);
nameView.setText(activity.getResources().getString(nameId));
lastValueView.setText(String.format("%.1f", value) + ' ' + activity.getResources().getString(R.string.cache_rating_of) + " " + String.format("%d", max));
- createStarImages(layoutStars, value, max);
+
+ final RatingBar layoutStars = ButterKnife.findById(layout, R.id.stars);
+ layoutStars.setRating(value);
layoutStars.setVisibility(View.VISIBLE);
parentView.addView(layout);
return layout;
}
- private void createStarImages(final ViewGroup starsContainer, final float value, final int max) {
- final LayoutInflater inflater = LayoutInflater.from(activity);
-
- for (int i = 0; i < max; i++) {
- final ImageView star = (ImageView) inflater.inflate(R.layout.star_image, starsContainer, false);
- if (value - i >= 0.75) {
- star.setImageResource(R.drawable.star_on);
- } else if (value - i >= 0.25) {
- star.setImageResource(R.drawable.star_half);
- } else {
- star.setImageResource(R.drawable.star_off);
- }
- starsContainer.addView(star);
- }
- }
-
public void addCacheState(final Geocache cache) {
if (cache.isLogOffline() || cache.isArchived() || cache.isDisabled() || cache.isPremiumMembersOnly() || cache.isFound()) {
final List<String> states = new ArrayList<>(5);
@@ -209,7 +195,7 @@ public final class CacheDetailsCreator {
if (StringUtils.isEmpty(dateString)) {
return null;
}
- final TextView view = add(cache.isEventCache() ? R.string.cache_event : R.string.cache_hidden, dateString);
+ final TextView view = add(cache.isEventCache() ? R.string.cache_event : R.string.cache_hidden, dateString).right;
view.setId(R.id.date);
return view;
}