aboutsummaryrefslogtreecommitdiffstats
path: root/main/src/cgeo/geocaching/connector/gc/GCParser.java
diff options
context:
space:
mode:
Diffstat (limited to 'main/src/cgeo/geocaching/connector/gc/GCParser.java')
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCParser.java578
1 files changed, 295 insertions, 283 deletions
diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java
index c771049..7fef8b4 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCParser.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java
@@ -22,14 +22,15 @@ import cgeo.geocaching.enumerations.WaypointType;
import cgeo.geocaching.files.LocParser;
import cgeo.geocaching.gcvote.GCVote;
import cgeo.geocaching.gcvote.GCVoteRating;
-import cgeo.geocaching.geopoint.DistanceParser;
-import cgeo.geocaching.geopoint.Geopoint;
import cgeo.geocaching.loaders.RecaptchaReceiver;
+import cgeo.geocaching.location.DistanceParser;
+import cgeo.geocaching.location.Geopoint;
import cgeo.geocaching.network.Network;
import cgeo.geocaching.network.Parameters;
import cgeo.geocaching.settings.Settings;
-import cgeo.geocaching.ui.DirectionImage;
import cgeo.geocaching.utils.CancellableHandler;
+import cgeo.geocaching.utils.HtmlUtils;
+import cgeo.geocaching.utils.JsonUtils;
import cgeo.geocaching.utils.Log;
import cgeo.geocaching.utils.MatcherWrapper;
import cgeo.geocaching.utils.RxUtils;
@@ -38,15 +39,16 @@ import cgeo.geocaching.utils.TextUtils;
import ch.boye.httpclientandroidlib.HttpResponse;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
import rx.Observable;
import rx.Observable.OnSubscribe;
@@ -54,11 +56,15 @@ import rx.Subscriber;
import rx.functions.Action1;
import rx.functions.Func0;
import rx.functions.Func2;
+import rx.schedulers.Schedulers;
import android.net.Uri;
import android.text.Html;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.Collator;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
@@ -66,16 +72,22 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.EnumSet;
-import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.regex.Pattern;
public abstract class GCParser {
- private final static SynchronizedDateFormat dateTbIn1 = new SynchronizedDateFormat("EEEEE, dd MMMMM yyyy", Locale.ENGLISH); // Saturday, 28 March 2009
- private final static SynchronizedDateFormat dateTbIn2 = new SynchronizedDateFormat("EEEEE, MMMMM dd, yyyy", Locale.ENGLISH); // Saturday, March 28, 2009
+ @NonNull
+ private final static SynchronizedDateFormat DATE_TB_IN_1 = new SynchronizedDateFormat("EEEEE, dd MMMMM yyyy", Locale.ENGLISH); // Saturday, 28 March 2009
+
+ @NonNull
+ private final static SynchronizedDateFormat DATE_TB_IN_2 = new SynchronizedDateFormat("EEEEE, MMMMM dd, yyyy", Locale.ENGLISH); // Saturday, March 28, 2009
+ @NonNull
+ private final static ImmutablePair<StatusCode, Geocache> UNKNOWN_PARSE_ERROR = ImmutablePair.of(StatusCode.UNKNOWN_ERROR, null);
+
+ @Nullable
private static SearchResult parseSearch(final String url, final String pageContent, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) {
if (StringUtils.isBlank(pageContent)) {
Log.e("GCParser.parseSearch: No page given");
@@ -125,12 +137,12 @@ public abstract class GCParser {
page = page.substring(startPos + 1, endPos - startPos + 1); // cut between <table> and </table>
- final String[] rows = page.split("<tr class=");
- final int rows_count = rows.length;
+ final String[] rows = StringUtils.splitByWholeSeparator(page, "<tr class=");
+ final int rowsCount = rows.length;
int excludedCaches = 0;
final ArrayList<Geocache> caches = new ArrayList<>();
- for (int z = 1; z < rows_count; z++) {
+ for (int z = 1; z < rowsCount; z++) {
final Geocache cache = new Geocache();
final String row = rows[z];
@@ -161,7 +173,7 @@ public abstract class GCParser {
}
} catch (final RuntimeException e) {
// failed to parse GUID and/or Disabled
- Log.w("GCParser.parseSearch: Failed to parse GUID and/or Disabled data");
+ Log.w("GCParser.parseSearch: Failed to parse GUID and/or Disabled data", e);
}
if (Settings.isExcludeDisabledCaches() && (cache.isDisabled() || cache.isArchived())) {
@@ -173,11 +185,11 @@ public abstract class GCParser {
cache.setGeocode(TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_GEOCODE, true, 1, cache.getGeocode(), true));
// cache type
- cache.setType(CacheType.getByPattern(TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_TYPE, true, 1, null, true)));
+ cache.setType(CacheType.getByPattern(TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_TYPE, null)));
// cache direction - image
if (Settings.getLoadDirImg()) {
- final String direction = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION_DISTANCE, false, 1, null, false);
+ final String direction = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION_DISTANCE, false, null);
if (direction != null) {
cache.setDirectionImg(direction);
}
@@ -187,7 +199,7 @@ public abstract class GCParser {
final String distance = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION_DISTANCE, false, 2, null, false);
if (distance != null) {
cache.setDistance(DistanceParser.parseDistance(distance,
- !Settings.isUseImperialUnits()));
+ !Settings.useImperialUnits()));
}
// difficulty/terrain
@@ -204,19 +216,19 @@ public abstract class GCParser {
}
// size
- final String container = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_CONTAINER, false, 1, null, false);
+ final String container = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_CONTAINER, false, null);
cache.setSize(CacheSize.getById(container));
// date hidden, makes sorting event caches easier
- final String dateHidden = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_HIDDEN_DATE, false, 1, null, false);
+ final String dateHidden = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_HIDDEN_DATE, false, null);
if (StringUtils.isNotBlank(dateHidden)) {
try {
- Date date = GCLogin.parseGcCustomDate(dateHidden);
+ final Date date = GCLogin.parseGcCustomDate(dateHidden);
if (date != null) {
cache.setHidden(date);
}
- } catch (ParseException e) {
- Log.e("Error parsing event date from search");
+ } catch (final ParseException e) {
+ Log.e("Error parsing event date from search", e);
}
}
@@ -235,6 +247,7 @@ public abstract class GCParser {
}
if (StringUtils.isNotBlank(inventoryPre)) {
+ assert inventoryPre != null;
final MatcherWrapper matcherTbsInside = new MatcherWrapper(GCConstants.PATTERN_SEARCH_TRACKABLESINSIDE, inventoryPre);
while (matcherTbsInside.find()) {
if (matcherTbsInside.groupCount() == 2 &&
@@ -266,7 +279,7 @@ public abstract class GCParser {
cache.setFavoritePoints(Integer.parseInt(result));
}
} catch (final NumberFormatException e) {
- Log.w("GCParser.parseSearch: Failed to parse favorite count");
+ Log.w("GCParser.parseSearch: Failed to parse favorite count", e);
}
caches.add(cache);
@@ -280,7 +293,7 @@ public abstract class GCParser {
searchResult.setTotalCountGC(Integer.parseInt(result) - excludedCaches);
}
} catch (final NumberFormatException e) {
- Log.w("GCParser.parseSearch: Failed to parse cache count");
+ Log.w("GCParser.parseSearch: Failed to parse cache count", e);
}
String recaptchaText = null;
@@ -291,6 +304,13 @@ public abstract class GCParser {
if (!cids.isEmpty() && (Settings.isGCPremiumMember() || showCaptcha) && ((recaptchaReceiver == null || StringUtils.isBlank(recaptchaReceiver.getChallenge())) || StringUtils.isNotBlank(recaptchaText))) {
Log.i("Trying to get .loc for " + cids.size() + " caches");
+ final Observable<Set<Geocache>> storedCaches = Observable.defer(new Func0<Observable<Set<Geocache>>>() {
+ @Override
+ public Observable<Set<Geocache>> call() {
+ return Observable.just(DataStore.loadCaches(Geocache.getGeocodes(caches), LoadFlags.LOAD_CACHE_OR_DB));
+ }
+ }).subscribeOn(Schedulers.io()).cache();
+ storedCaches.subscribe(); // Force asynchronous start of database loading
try {
// get coordinates for parsed caches
@@ -306,41 +326,41 @@ public abstract class GCParser {
params.put("recaptcha_challenge_field", recaptchaReceiver.getChallenge());
params.put("recaptcha_response_field", recaptchaText);
}
- params.put("ctl00$ContentBody$uxDownloadLoc", "Download Waypoints");
+ params.put("Download", "Download Waypoints");
- final String coordinates = Network.getResponseData(Network.postRequest("http://www.geocaching.com/seek/nearest.aspx", params), false);
+ // retrieve target url
+ final String queryUrl = TextUtils.getMatch(pageContent, GCConstants.PATTERN_SEARCH_POST_ACTION, "");
- if (StringUtils.contains(coordinates, "You have not agreed to the license agreement. The license agreement is required before you can start downloading GPX or LOC files from Geocaching.com")) {
- Log.i("User has not agreed to the license agreement. Can\'t download .loc file.");
- searchResult.setError(StatusCode.UNAPPROVED_LICENSE);
- return searchResult;
- }
+ if (StringUtils.isEmpty(queryUrl)) {
+ Log.w("Loc download url not found");
+ } else {
- LocParser.parseLoc(searchResult, coordinates);
+ final String coordinates = Network.getResponseData(Network.postRequest("http://www.geocaching.com/seek/" + queryUrl, params), false);
- } catch (final RuntimeException e) {
- Log.e("GCParser.parseSearch.CIDs", e);
- }
- }
+ if (StringUtils.contains(coordinates, "You have not agreed to the license agreement. The license agreement is required before you can start downloading GPX or LOC files from Geocaching.com")) {
+ Log.i("User has not agreed to the license agreement. Can\'t download .loc file.");
+ searchResult.setError(StatusCode.UNAPPROVED_LICENSE);
+ return searchResult;
+ }
- // get direction images
- if (Settings.getLoadDirImg()) {
- final Set<Geocache> cachesReloaded = searchResult.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB);
- for (final Geocache cache : cachesReloaded) {
- if (cache.getCoords() == null && StringUtils.isNotEmpty(cache.getDirectionImg())) {
- DirectionImage.getDrawable(cache.getDirectionImg());
+ LocParser.parseLoc(searchResult, coordinates, storedCaches.toBlocking().single());
}
+
+ } catch (final RuntimeException e) {
+ Log.e("GCParser.parseSearch.CIDs", e);
}
}
return searchResult;
}
+ @Nullable
private static Float parseStars(final String value) {
final float floatValue = Float.parseFloat(StringUtils.replaceChars(value, ',', '.'));
return floatValue >= 0.5 && floatValue <= 5.0 ? floatValue : null;
}
+ @Nullable
static SearchResult parseCache(final String page, final CancellableHandler handler) {
final ImmutablePair<StatusCode, Geocache> parsed = parseCacheFromText(page, handler);
// attention: parseCacheFromText already stores implicitly through searchResult.addCache
@@ -366,12 +386,13 @@ public abstract class GCParser {
return new SearchResult(cache);
}
+ @NonNull
static SearchResult parseAndSaveCacheFromText(final String page, @Nullable final CancellableHandler handler) {
final ImmutablePair<StatusCode, Geocache> parsed = parseCacheFromText(page, handler);
final SearchResult result = new SearchResult(parsed.left);
if (parsed.left == StatusCode.NO_ERROR) {
result.addAndPutInCache(Collections.singletonList(parsed.right));
- DataStore.saveLogsWithoutTransaction(parsed.right.getGeocode(), getLogs(page, Logs.ALL).toBlocking().toIterable());
+ DataStore.saveLogs(parsed.right.getGeocode(), getLogs(parseUserToken(page), Logs.ALL).toBlocking().toIterable());
}
return result;
}
@@ -380,17 +401,20 @@ public abstract class GCParser {
* Parse cache from text and return either an error code or a cache object in a pair. Note that inline logs are
* not parsed nor saved, while the cache itself is.
*
- * @param pageIn the page text to parse
- * @param handler the handler to send the progress notifications to
- * @return a pair, with a {@link StatusCode} on the left, and a non-nulll cache objet on the right
- * iff the status code is {@link StatusCode.NO_ERROR}.
+ * @param pageIn
+ * the page text to parse
+ * @param handler
+ * the handler to send the progress notifications to
+ * @return a pair, with a {@link StatusCode} on the left, and a non-null cache object on the right
+ * iff the status code is {@link cgeo.geocaching.enumerations.StatusCode#NO_ERROR}.
*/
+ @NonNull
static private ImmutablePair<StatusCode, Geocache> parseCacheFromText(final String pageIn, @Nullable final CancellableHandler handler) {
CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_details);
if (StringUtils.isBlank(pageIn)) {
Log.e("GCParser.parseCache: No page given");
- return null;
+ return UNKNOWN_PARSE_ERROR;
}
if (pageIn.contains(GCConstants.STRING_UNPUBLISHED_OTHER) || pageIn.contains(GCConstants.STRING_UNPUBLISHED_FROM_SEARCH)) {
@@ -403,12 +427,12 @@ public abstract class GCParser {
final String cacheName = Html.fromHtml(TextUtils.getMatch(pageIn, GCConstants.PATTERN_NAME, true, "")).toString();
if (GCConstants.STRING_UNKNOWN_ERROR.equalsIgnoreCase(cacheName)) {
- return ImmutablePair.of(StatusCode.UNKNOWN_ERROR, null);
+ return UNKNOWN_PARSE_ERROR;
}
// first handle the content with line breaks, then trim everything for easier matching and reduced memory consumption in parsed fields
String personalNoteWithLineBreaks = "";
- MatcherWrapper matcher = new MatcherWrapper(GCConstants.PATTERN_PERSONALNOTE, pageIn);
+ final MatcherWrapper matcher = new MatcherWrapper(GCConstants.PATTERN_PERSONALNOTE, pageIn);
if (matcher.find()) {
personalNoteWithLineBreaks = matcher.group(1).trim();
}
@@ -446,7 +470,7 @@ public abstract class GCParser {
final int pos = tableInside.indexOf(GCConstants.STRING_CACHEDETAILS);
if (pos == -1) {
Log.e("GCParser.parseCache: ID \"cacheDetails\" not found on page");
- return null;
+ return UNKNOWN_PARSE_ERROR;
}
tableInside = tableInside.substring(pos);
@@ -490,7 +514,7 @@ public abstract class GCParser {
}
} catch (final ParseException e) {
// failed to parse cache hidden date
- Log.w("GCParser.parseCache: Failed to parse cache hidden (event) date");
+ Log.w("GCParser.parseCache: Failed to parse cache hidden (event) date", e);
}
// favorite
@@ -547,17 +571,17 @@ public abstract class GCParser {
final String longDescription = TextUtils.getMatch(page, GCConstants.PATTERN_DESC, true, "");
String relatedWebPage = TextUtils.getMatch(page, GCConstants.PATTERN_RELATED_WEB_PAGE, true, "");
if (StringUtils.isNotEmpty(relatedWebPage)) {
- relatedWebPage = String.format("<a href=\"%s\"><b>%s</b></a><br/><br/>", relatedWebPage, relatedWebPage);
+ relatedWebPage = String.format("<br/><br/><a href=\"%s\"><b>%s</b></a>", relatedWebPage, relatedWebPage);
}
- cache.setDescription(relatedWebPage + longDescription);
+ cache.setDescription(longDescription + relatedWebPage);
// cache attributes
try {
+ final ArrayList<String> attributes = new ArrayList<>();
final String attributesPre = TextUtils.getMatch(page, GCConstants.PATTERN_ATTRIBUTES, true, null);
- if (null != attributesPre) {
+ if (attributesPre != null) {
final MatcherWrapper matcherAttributesInside = new MatcherWrapper(GCConstants.PATTERN_ATTRIBUTESINSIDE, attributesPre);
- final ArrayList<String> attributes = new ArrayList<>();
while (matcherAttributesInside.find()) {
if (matcherAttributesInside.groupCount() > 1 && !matcherAttributesInside.group(2).equalsIgnoreCase("blank")) {
// by default, use the tooltip of the attribute
@@ -575,17 +599,17 @@ public abstract class GCParser {
attributes.add(attribute);
}
}
- cache.setAttributes(attributes);
}
+ cache.setAttributes(attributes);
} catch (final RuntimeException e) {
// failed to parse cache attributes
- Log.w("GCParser.parseCache: Failed to parse cache attributes");
+ Log.w("GCParser.parseCache: Failed to parse cache attributes", e);
}
// cache spoilers
try {
if (CancellableHandler.isCancelled(handler)) {
- return null;
+ return UNKNOWN_PARSE_ERROR;
}
CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_spoilers);
@@ -608,7 +632,7 @@ public abstract class GCParser {
}
} catch (final RuntimeException e) {
// failed to parse cache spoilers
- Log.w("GCParser.parseCache: Failed to parse cache spoilers");
+ Log.w("GCParser.parseCache: Failed to parse cache spoilers", e);
}
// cache inventory
@@ -642,7 +666,7 @@ public abstract class GCParser {
}
} catch (final RuntimeException e) {
// failed to parse cache inventory
- Log.w("GCParser.parseCache: Failed to parse cache inventory (2)");
+ Log.w("GCParser.parseCache: Failed to parse cache inventory (2)", e);
}
// cache logs counts
@@ -664,7 +688,7 @@ public abstract class GCParser {
}
} catch (final NumberFormatException e) {
// failed to parse logs
- Log.w("GCParser.parseCache: Failed to parse cache log count");
+ Log.w("GCParser.parseCache: Failed to parse cache log count", e);
}
// waypoints - reset collection
@@ -680,13 +704,13 @@ public abstract class GCParser {
cache.addOrChangeWaypoint(waypoint, false);
cache.setUserModifiedCoords(true);
}
- } catch (final Geopoint.GeopointException e) {
+ } catch (final Geopoint.GeopointException ignored) {
}
int wpBegin = page.indexOf("<table class=\"Table\" id=\"ctl00_ContentBody_Waypoints\">");
if (wpBegin != -1) { // parse waypoints
if (CancellableHandler.isCancelled(handler)) {
- return null;
+ return UNKNOWN_PARSE_ERROR;
}
CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_waypoints);
@@ -707,10 +731,15 @@ public abstract class GCParser {
wpList = wpList.substring(wpBegin + 7, wpEnd);
}
- final String[] wpItems = wpList.split("<tr");
+ final String[] wpItems = StringUtils.splitByWholeSeparator(wpList, "<tr");
- for (int j = 1; j < wpItems.length; j++) {
- String[] wp = wpItems[j].split("<td");
+ for (int j = 1; j < wpItems.length; j += 2) {
+ final String[] wp = StringUtils.splitByWholeSeparator(wpItems[j], "<td");
+ assert wp != null;
+ if (wp.length < 8) {
+ Log.e("GCParser.cacheParseFromText: not enough waypoint columns in table");
+ continue;
+ }
// waypoint name
// res is null during the unit tests
@@ -730,39 +759,42 @@ public abstract class GCParser {
// waypoint latitude and longitude
latlon = Html.fromHtml(TextUtils.getMatch(wp[7], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, false, 2, "", false)).toString().trim();
if (!StringUtils.startsWith(latlon, "???")) {
- waypoint.setLatlon(latlon);
waypoint.setCoords(new Geopoint(latlon));
}
- j++;
- if (wpItems.length > j) {
- wp = wpItems[j].split("<td");
- }
+ if (wpItems.length >= j) {
+ final String[] wpNote = StringUtils.splitByWholeSeparator(wpItems[j + 1], "<td");
+ assert wpNote != null;
+ if (wpNote.length < 4) {
+ Log.d("GCParser.cacheParseFromText: not enough waypoint columns in table to extract note");
+ continue;
+ }
- // waypoint note
- waypoint.setNote(TextUtils.getMatch(wp[3], GCConstants.PATTERN_WPNOTE, waypoint.getNote()));
+ // waypoint note
+ waypoint.setNote(TextUtils.getMatch(wpNote[3], GCConstants.PATTERN_WPNOTE, waypoint.getNote()));
+ }
cache.addOrChangeWaypoint(waypoint, false);
}
}
}
- cache.parseWaypointsFromNote();
-
// last check for necessary cache conditions
if (StringUtils.isBlank(cache.getGeocode())) {
- return ImmutablePair.of(StatusCode.UNKNOWN_ERROR, null);
+ return UNKNOWN_PARSE_ERROR;
}
cache.setDetailedUpdatedNow();
return ImmutablePair.of(StatusCode.NO_ERROR, cache);
}
+ @Nullable
private static String getNumberString(final String numberWithPunctuation) {
return StringUtils.replaceChars(numberWithPunctuation, ".,", "");
}
- public static SearchResult searchByNextPage(final SearchResult search, boolean showCaptcha, RecaptchaReceiver recaptchaReceiver) {
+ @Nullable
+ public static SearchResult searchByNextPage(final SearchResult search, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) {
if (search == null) {
return null;
}
@@ -817,14 +849,12 @@ public abstract class GCParser {
/**
* Possibly hide caches found or hidden by user. This mutates its params argument when possible.
*
- * @param params
- * the parameters to mutate, or null to create a new Parameters if needed
- * @param my
- * @param addF
+ * @param params the parameters to mutate, or null to create a new Parameters if needed
+ * @param my {@code true} if the user's caches must be forcibly included regardless of their settings
* @return the original params if not null, maybe augmented with f=1, or a new Parameters with f=1 or null otherwise
*/
- private static Parameters addFToParams(final Parameters params, final boolean my, final boolean addF) {
- if (!my && Settings.isExcludeMyCaches() && addF) {
+ private static Parameters addFToParams(final Parameters params, final boolean my) {
+ if (!my && Settings.isExcludeMyCaches()) {
if (params == null) {
return new Parameters("f", "1");
}
@@ -835,22 +865,14 @@ public abstract class GCParser {
return params;
}
- /**
- * @param cacheType
- * @param listId
- * @param showCaptcha
- * @param params
- * the parameters to add to the request URI
- * @param recaptchaReceiver
- * @return
- */
@Nullable
- private static SearchResult searchByAny(final CacheType cacheType, final boolean my, final boolean showCaptcha, final Parameters params, RecaptchaReceiver recaptchaReceiver) {
+ private static SearchResult searchByAny(@NonNull final CacheType cacheType, final boolean my, final boolean showCaptcha, final Parameters params, final RecaptchaReceiver recaptchaReceiver) {
insertCacheType(params, cacheType);
final String uri = "http://www.geocaching.com/seek/nearest.aspx";
- final String fullUri = uri + "?" + addFToParams(params, my, true);
- final String page = GCLogin.getInstance().getRequestLogged(uri, addFToParams(params, my, true));
+ final Parameters paramsWithF = addFToParams(params, my);
+ final String fullUri = uri + "?" + paramsWithF;
+ final String page = GCLogin.getInstance().getRequestLogged(uri, paramsWithF);
if (StringUtils.isBlank(page)) {
Log.e("GCParser.searchByAny: No data from server");
@@ -871,12 +893,12 @@ public abstract class GCParser {
return search;
}
- public static SearchResult searchByCoords(final @NonNull Geopoint coords, final CacheType cacheType, final boolean showCaptcha, RecaptchaReceiver recaptchaReceiver) {
+ public static SearchResult searchByCoords(final @NonNull Geopoint coords, @NonNull final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) {
final Parameters params = new Parameters("lat", Double.toString(coords.getLatitude()), "lng", Double.toString(coords.getLongitude()));
return searchByAny(cacheType, false, showCaptcha, params, recaptchaReceiver);
}
- public static SearchResult searchByKeyword(final @NonNull String keyword, final CacheType cacheType, final boolean showCaptcha, RecaptchaReceiver recaptchaReceiver) {
+ public static SearchResult searchByKeyword(final @NonNull String keyword, @NonNull final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) {
if (StringUtils.isBlank(keyword)) {
Log.e("GCParser.searchByKeyword: No keyword given");
return null;
@@ -894,7 +916,7 @@ public abstract class GCParser {
return false;
}
- public static SearchResult searchByUsername(final String userName, final CacheType cacheType, final boolean showCaptcha, RecaptchaReceiver recaptchaReceiver) {
+ public static SearchResult searchByUsername(final String userName, @NonNull final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) {
if (StringUtils.isBlank(userName)) {
Log.e("GCParser.searchByUsername: No user name given");
return null;
@@ -905,7 +927,7 @@ public abstract class GCParser {
return searchByAny(cacheType, isSearchForMyCaches(userName), showCaptcha, params, recaptchaReceiver);
}
- public static SearchResult searchByPocketQuery(final String pocketGuid, final CacheType cacheType, final boolean showCaptcha, RecaptchaReceiver recaptchaReceiver) {
+ public static SearchResult searchByPocketQuery(final String pocketGuid, @NonNull final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) {
if (StringUtils.isBlank(pocketGuid)) {
Log.e("GCParser.searchByPocket: No guid name given");
return null;
@@ -916,7 +938,7 @@ public abstract class GCParser {
return searchByAny(cacheType, false, showCaptcha, params, recaptchaReceiver);
}
- public static SearchResult searchByOwner(final String userName, final CacheType cacheType, final boolean showCaptcha, RecaptchaReceiver recaptchaReceiver) {
+ public static SearchResult searchByOwner(final String userName, @NonNull final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) {
if (StringUtils.isBlank(userName)) {
Log.e("GCParser.searchByOwner: No user name given");
return null;
@@ -926,34 +948,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, RecaptchaReceiver recaptchaReceiver) {
- if (StringUtils.isBlank(address)) {
- Log.e("GCParser.searchByAddress: No address given");
- return null;
- }
- try {
- final JSONObject response = Network.requestJSON("http://www.geocaching.com/api/geocode", new Parameters("q", address));
- if (response == null) {
- return null;
- }
- if (!StringUtils.equalsIgnoreCase(response.getString("status"), "success")) {
- return null;
- }
- if (!response.has("data")) {
- return null;
- }
- final JSONObject data = response.getJSONObject("data");
- if (data == null) {
- return null;
- }
- return searchByCoords(new Geopoint(data.getDouble("lat"), data.getDouble("lng")), cacheType, showCaptcha, recaptchaReceiver);
- } catch (final JSONException e) {
- Log.w("GCParser.searchByAddress", e);
- }
-
- return null;
- }
-
@Nullable
public static Trackable searchTrackable(final String geocode, final String guid, final String id) {
if (StringUtils.isBlank(geocode) && StringUtils.isBlank(guid) && StringUtils.isBlank(id)) {
@@ -990,52 +984,59 @@ public abstract class GCParser {
return trackable;
}
- public static List<PocketQueryList> searchPocketQueryList() {
-
- final Parameters params = new Parameters();
+ /**
+ * Observable that fetches a list of pocket queries. Returns a single element (which may be an empty list).
+ * Executes on the network scheduler.
+ */
+ public static final Observable<List<PocketQueryList>> searchPocketQueryListObservable = Observable.defer(new Func0<Observable<List<PocketQueryList>>>() {
+ @Override
+ public Observable<List<PocketQueryList>> call() {
+ final Parameters params = new Parameters();
- final String page = GCLogin.getInstance().getRequestLogged("http://www.geocaching.com/pocket/default.aspx", params);
+ final String page = GCLogin.getInstance().getRequestLogged("http://www.geocaching.com/pocket/default.aspx", params);
- if (StringUtils.isBlank(page)) {
- Log.e("GCParser.searchPocketQueryList: No data from server");
- return null;
- }
+ if (StringUtils.isBlank(page)) {
+ Log.e("GCParser.searchPocketQueryList: No data from server");
+ return Observable.just(Collections.<PocketQueryList>emptyList());
+ }
- String subPage = StringUtils.substringAfter(page, "class=\"PocketQueryListTable");
- if (StringUtils.isEmpty(subPage)) {
- Log.e("GCParser.searchPocketQueryList: class \"PocketQueryListTable\" not found on page");
- return Collections.emptyList();
- }
+ final String subPage = StringUtils.substringAfter(page, "class=\"PocketQueryListTable");
+ if (StringUtils.isEmpty(subPage)) {
+ Log.e("GCParser.searchPocketQueryList: class \"PocketQueryListTable\" not found on page");
+ return Observable.just(Collections.<PocketQueryList>emptyList());
+ }
- List<PocketQueryList> list = new ArrayList<>();
+ final List<PocketQueryList> list = new ArrayList<>();
- final MatcherWrapper matcherPocket = new MatcherWrapper(GCConstants.PATTERN_LIST_PQ, subPage);
+ final MatcherWrapper matcherPocket = new MatcherWrapper(GCConstants.PATTERN_LIST_PQ, subPage);
- while (matcherPocket.find()) {
- int maxCaches;
- try {
- maxCaches = Integer.parseInt(matcherPocket.group(1));
- } catch (NumberFormatException e) {
- maxCaches = 0;
- Log.e("GCParser.searchPocketQueryList: Unable to parse max caches", e);
+ while (matcherPocket.find()) {
+ int maxCaches;
+ try {
+ maxCaches = Integer.parseInt(matcherPocket.group(1));
+ } catch (final NumberFormatException e) {
+ maxCaches = 0;
+ Log.e("GCParser.searchPocketQueryList: Unable to parse max caches", e);
+ }
+ final String guid = Html.fromHtml(matcherPocket.group(2)).toString();
+ final String name = Html.fromHtml(matcherPocket.group(3)).toString();
+ final PocketQueryList pqList = new PocketQueryList(guid, name, maxCaches);
+ list.add(pqList);
}
- final String guid = Html.fromHtml(matcherPocket.group(2)).toString();
- final String name = Html.fromHtml(matcherPocket.group(3)).toString();
- final PocketQueryList pqList = new PocketQueryList(guid, name, maxCaches);
- list.add(pqList);
- }
- // just in case, lets sort the resulting list
- Collections.sort(list, new Comparator<PocketQueryList>() {
+ // just in case, lets sort the resulting list
+ final Collator collator = TextUtils.getCollator();
+ Collections.sort(list, new Comparator<PocketQueryList>() {
- @Override
- public int compare(PocketQueryList left, PocketQueryList right) {
- return String.CASE_INSENSITIVE_ORDER.compare(left.getName(), right.getName());
- }
- });
+ @Override
+ public int compare(final PocketQueryList left, final PocketQueryList right) {
+ return collator.compare(left.getName(), right.getName());
+ }
+ });
- return list;
- }
+ return Observable.just(list);
+ }
+ }).subscribeOn(RxUtils.networkScheduler);
public static ImmutablePair<StatusCode, String> postLog(final String geocode, final String cacheid, final String[] viewstates,
final LogType logType, final int year, final int month, final int day,
@@ -1061,7 +1062,7 @@ public abstract class GCParser {
"__EVENTARGUMENT", "",
"__LASTFOCUS", "",
"ctl00$ContentBody$LogBookPanel1$ddLogType", Integer.toString(logType.id),
- "ctl00$ContentBody$LogBookPanel1$uxDateVisited", GCLogin.getCustomGcDateFormat().format(new GregorianCalendar(year, month - 1, day).getTime()),
+ "ctl00$ContentBody$LogBookPanel1$uxDateVisited", GCLogin.formatGcCustomDate(year, month, day),
"ctl00$ContentBody$LogBookPanel1$uxDateVisited$Month", Integer.toString(month),
"ctl00$ContentBody$LogBookPanel1$uxDateVisited$Day", Integer.toString(day),
"ctl00$ContentBody$LogBookPanel1$uxDateVisited$Year", Integer.toString(year),
@@ -1147,6 +1148,11 @@ public abstract class GCParser {
Log.e("GCParser.postLog.confim", e);
}
+ if (page == null) {
+ Log.e("GCParser.postLog: didn't get response");
+ return new ImmutablePair<>(StatusCode.LOG_POST_ERROR, "");
+ }
+
try {
final MatcherWrapper matcherOk = new MatcherWrapper(GCConstants.PATTERN_OK1, page);
@@ -1160,7 +1166,7 @@ public abstract class GCParser {
gcLogin.getLoginStatus(page);
// the log-successful-page contains still the old value
if (gcLogin.getActualCachesFound() >= 0) {
- gcLogin.setActualCachesFound(gcLogin.getActualCachesFound() + 1);
+ gcLogin.setActualCachesFound(gcLogin.getActualCachesFound() + (logType.isFoundLog() ? 1 : 0));
}
final String logID = TextUtils.getMatch(page, GCConstants.PATTERN_LOG_IMAGE_UPLOAD, "");
@@ -1211,6 +1217,11 @@ public abstract class GCParser {
final File image = new File(imageUri.getPath());
final String response = Network.getResponseData(Network.postRequest(uri, uploadParams, "ctl00$ContentBody$ImageUploadControl1$uxFileUpload", "image/jpeg", image));
+ if (response == null) {
+ Log.e("GCParser.uploadLogIMage: didn't get response for image upload");
+ return ImmutablePair.of(StatusCode.LOGIMAGE_POST_ERROR, null);
+ }
+
final MatcherWrapper matcherUrl = new MatcherWrapper(GCConstants.PATTERN_IMAGE_UPLOAD_URL, response);
if (matcherUrl.find()) {
@@ -1257,7 +1268,7 @@ public abstract class GCParser {
params.put("ctl00$ContentBody$LogBookPanel1$uxDateVisited", "");
} else {
params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged", Integer.toString(month) + "/" + Integer.toString(day) + "/" + Integer.toString(year));
- params.put("ctl00$ContentBody$LogBookPanel1$uxDateVisited", GCLogin.getCustomGcDateFormat().format(new GregorianCalendar(year, month - 1, day).getTime()));
+ params.put("ctl00$ContentBody$LogBookPanel1$uxDateVisited", GCLogin.formatGcCustomDate(year, month, day));
}
params.put(
"ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Day", Integer.toString(day),
@@ -1417,7 +1428,10 @@ public abstract class GCParser {
}
private static String getUserToken(final Geocache cache) {
- final String page = requestHtmlPage(cache.getGeocode(), null, "n");
+ return parseUserToken(requestHtmlPage(cache.getGeocode(), null, "n"));
+ }
+
+ private static String parseUserToken(final String page) {
return TextUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, "");
}
@@ -1483,7 +1497,7 @@ public abstract class GCParser {
}
} catch (final RuntimeException e) {
// failed to parse trackable owner name
- Log.w("GCParser.parseTrackable: Failed to parse trackable owner name");
+ Log.w("GCParser.parseTrackable: Failed to parse trackable owner name", e);
}
// trackable origin
@@ -1514,20 +1528,20 @@ public abstract class GCParser {
}
} catch (final RuntimeException e) {
// failed to parse trackable last known place
- Log.w("GCParser.parseTrackable: Failed to parse trackable last known place");
+ Log.w("GCParser.parseTrackable: Failed to parse trackable last known place", e);
}
// released date - can be missing on the page
final String releaseString = TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_RELEASES, false, null);
if (releaseString != null) {
try {
- trackable.setReleased(dateTbIn1.parse(releaseString));
- } catch (ParseException e) {
+ trackable.setReleased(DATE_TB_IN_1.parse(releaseString));
+ } catch (final ParseException ignored) {
if (trackable.getReleased() == null) {
try {
- trackable.setReleased(dateTbIn2.parse(releaseString));
- } catch (ParseException e1) {
- Log.e("Could not parse trackable release " + releaseString);
+ trackable.setReleased(DATE_TB_IN_2.parse(releaseString));
+ } catch (final ParseException e) {
+ Log.e("Could not parse trackable release " + releaseString, e);
}
}
}
@@ -1538,14 +1552,14 @@ public abstract class GCParser {
if (null != distance) {
try {
trackable.setDistance(DistanceParser.parseDistance(distance,
- !Settings.isUseImperialUnits()));
+ !Settings.useImperialUnits()));
} catch (final NumberFormatException e) {
Log.e("GCParser.parseTrackable: Failed to parse distance", e);
}
}
// trackable goal
- trackable.setGoal(convertLinks(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GOAL, true, trackable.getGoal())));
+ trackable.setGoal(HtmlUtils.removeExtraParagraph(convertLinks(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GOAL, true, trackable.getGoal()))));
// trackable details & image
try {
@@ -1558,12 +1572,12 @@ public abstract class GCParser {
trackable.setImage(StringUtils.replace(image, "/display/", "/large/"));
}
if (StringUtils.isNotEmpty(details) && !StringUtils.equals(details, "No additional details available.")) {
- trackable.setDetails(convertLinks(details));
+ trackable.setDetails(HtmlUtils.removeExtraParagraph(convertLinks(details)));
}
}
} catch (final RuntimeException e) {
// failed to parse trackable details & image
- Log.w("GCParser.parseTrackable: Failed to parse trackable details & image");
+ Log.w("GCParser.parseTrackable: Failed to parse trackable details & image", e);
}
if (StringUtils.isEmpty(trackable.getDetails()) && page.contains(GCConstants.ERROR_TB_NOT_ACTIVATED)) {
trackable.setDetails(CgeoApplication.getInstance().getString(R.string.trackable_not_activated));
@@ -1585,7 +1599,7 @@ public abstract class GCParser {
long date = 0;
try {
date = GCLogin.parseGcCustomDate(matcherLogs.group(2)).getTime();
- } catch (final ParseException e) {
+ } catch (final ParseException ignored) {
}
final LogEntry logDone = new LogEntry(
@@ -1630,7 +1644,7 @@ public abstract class GCParser {
return trackable;
}
- private static String convertLinks(String input) {
+ private static String convertLinks(final String input) {
if (input == null) {
return null;
}
@@ -1656,23 +1670,19 @@ public abstract class GCParser {
/**
* Extract special logs (friends, own) through seperate request.
*
- * @param page
- * The page to extrat userToken from
- * @param logType
- * The logType to request
+ * @param userToken the user token extracted from the web page
+ * @param logType the logType to request
* @return Observable<LogEntry> The logs
*/
- private static Observable<LogEntry> getLogs(final String page, final Logs logType) {
+ private static Observable<LogEntry> getLogs(final String userToken, final Logs logType) {
+ if (userToken.isEmpty()) {
+ Log.e("GCParser.loadLogsFromDetails: unable to extract userToken");
+ return Observable.empty();
+ }
+
return Observable.defer(new Func0<Observable<LogEntry>>() {
@Override
public Observable<LogEntry> call() {
- final MatcherWrapper userTokenMatcher = new MatcherWrapper(GCConstants.PATTERN_USERTOKEN, page);
- if (!userTokenMatcher.find()) {
- Log.e("GCParser.loadLogsFromDetails: unable to extract userToken");
- return Observable.empty();
- }
-
- final String userToken = userTokenMatcher.group(1);
final Parameters params = new Parameters(
"tkn", userToken,
"idx", "1",
@@ -1691,77 +1701,67 @@ public abstract class GCParser {
Log.e("GCParser.loadLogsFromDetails: error " + statusCode + " when requesting log information");
return Observable.empty();
}
- String rawResponse = Network.getResponseData(response);
- if (rawResponse == null) {
+ final InputStream responseStream = Network.getResponseStream(response);
+ if (responseStream == null) {
Log.e("GCParser.loadLogsFromDetails: unable to read whole response");
return Observable.empty();
}
- return parseLogs(logType != Logs.ALL, rawResponse);
+ return parseLogs(logType != Logs.ALL, responseStream);
}
}).subscribeOn(RxUtils.networkScheduler);
}
- private static Observable<LogEntry> parseLogs(final boolean markAsFriendsLog, final String rawResponse) {
+ private static Observable<LogEntry> parseLogs(final boolean markAsFriendsLog, final InputStream responseStream) {
return Observable.create(new OnSubscribe<LogEntry>() {
@Override
public void call(final Subscriber<? super LogEntry> subscriber) {
- // for non logged in users the log book is not shown
- if (StringUtils.isBlank(rawResponse)) {
- subscriber.onCompleted();
- return;
- }
-
try {
- final JSONObject resp = new JSONObject(rawResponse);
- if (!resp.getString("status").equals("success")) {
- Log.e("GCParser.loadLogsFromDetails: status is " + resp.getString("status"));
+ final ObjectNode resp = (ObjectNode) JsonUtils.reader.readTree(responseStream);
+ if (!resp.path("status").asText().equals("success")) {
+ Log.e("GCParser.loadLogsFromDetails: status is " + resp.path("status").asText("[absent]"));
subscriber.onCompleted();
return;
}
- final JSONArray data = resp.getJSONArray("data");
-
- for (int index = 0; index < data.length(); index++) {
- final JSONObject entry = data.getJSONObject(index);
-
+ final ArrayNode data = (ArrayNode) resp.get("data");
+ for (final JsonNode entry: data) {
// FIXME: use the "LogType" field instead of the "LogTypeImage" one.
- final String logIconNameExt = entry.optString("LogTypeImage", ".gif");
+ final String logIconNameExt = entry.path("LogTypeImage").asText(".gif");
final String logIconName = logIconNameExt.substring(0, logIconNameExt.length() - 4);
- long date = 0;
+ final long date;
try {
- date = GCLogin.parseGcCustomDate(entry.getString("Visited")).getTime();
- } catch (final ParseException e) {
- Log.e("GCParser.loadLogsFromDetails: failed to parse log date.");
+ date = GCLogin.parseGcCustomDate(entry.get("Visited").asText()).getTime();
+ } catch (ParseException | NullPointerException e) {
+ Log.e("GCParser.loadLogsFromDetails: failed to parse log date", e);
+ continue;
}
// TODO: we should update our log data structure to be able to record
// proper coordinates, and make them clickable. In the meantime, it is
// better to integrate those coordinates into the text rather than not
// display them at all.
- final String latLon = entry.getString("LatLonString");
- final String logText = (StringUtils.isEmpty(latLon) ? "" : (latLon + "<br/><br/>")) + TextUtils.removeControlCharacters(entry.getString("LogText"));
+ final String latLon = entry.path("LatLonString").asText();
+ final String logText = (StringUtils.isEmpty(latLon) ? "" : (latLon + "<br/><br/>")) + TextUtils.removeControlCharacters(entry.path("LogText").asText());
final LogEntry logDone = new LogEntry(
- TextUtils.removeControlCharacters(entry.getString("UserName")),
+ TextUtils.removeControlCharacters(entry.path("UserName").asText()),
date,
LogType.getByIconName(logIconName),
logText);
- logDone.found = entry.getInt("GeocacheFindCount");
+ logDone.found = entry.path("GeocacheFindCount").asInt();
logDone.friend = markAsFriendsLog;
- final JSONArray images = entry.getJSONArray("Images");
- for (int i = 0; i < images.length(); i++) {
- final JSONObject image = images.getJSONObject(i);
- final String url = "http://imgcdn.geocaching.com/cache/log/large/" + image.getString("FileName");
- final String title = TextUtils.removeControlCharacters(image.getString("Name"));
+ final ArrayNode images = (ArrayNode) entry.get("Images");
+ for (final JsonNode image: images) {
+ final String url = "http://imgcdn.geocaching.com/cache/log/large/" + image.path("FileName").asText();
+ final String title = TextUtils.removeControlCharacters(image.path("Name").asText());
final Image logImage = new Image(url, title);
logDone.addLogImage(logImage);
}
subscriber.onNext(logDone);
}
- } catch (final JSONException e) {
- // failed to parse logs
+ } catch (final IOException e) {
Log.w("GCParser.loadLogsFromDetails: Failed to parse cache logs", e);
}
subscriber.onCompleted();
@@ -1770,7 +1770,7 @@ public abstract class GCParser {
}
@NonNull
- public static List<LogType> parseTypes(String page) {
+ public static List<LogType> parseTypes(final String page) {
if (StringUtils.isEmpty(page)) {
return Collections.emptyList();
}
@@ -1801,9 +1801,10 @@ public abstract class GCParser {
return types;
}
+ @NonNull
public static List<TrackableLog> parseTrackableLog(final String page) {
if (StringUtils.isEmpty(page)) {
- return null;
+ return Collections.emptyList();
}
String table = StringUtils.substringBetween(page, "<table id=\"tblTravelBugs\"", "</table>");
@@ -1816,7 +1817,7 @@ public abstract class GCParser {
table = StringUtils.substringBetween(table, "<tbody>", "</tbody>");
if (StringUtils.isBlank(table)) {
Log.e("GCParser.parseTrackableLog: tbody not found on page");
- return null;
+ return Collections.emptyList();
}
final List<TrackableLog> trackableLogs = new ArrayList<>();
@@ -1865,16 +1866,12 @@ public abstract class GCParser {
return;
}
- final Observable<LogEntry> logs = getLogs(page, Logs.ALL);
- Observable<LogEntry> specialLogs;
- if (Settings.isFriendLogsWanted()) {
- CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_logs);
- specialLogs = Observable.merge(getLogs(page, Logs.FRIENDS),
- getLogs(page, Logs.OWN));
- } else {
- CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_logs);
- specialLogs = Observable.empty();
- }
+ CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_logs);
+ final String userToken = parseUserToken(page);
+ final Observable<LogEntry> logs = getLogs(userToken, Logs.ALL);
+ final Observable<LogEntry> ownLogs = getLogs(userToken, Logs.OWN).cache();
+ final Observable<LogEntry> specialLogs = Settings.isFriendLogsWanted() ?
+ Observable.merge(getLogs(userToken, Logs.FRIENDS), ownLogs) : Observable.<LogEntry>empty();
final Observable<List<LogEntry>> mergedLogs = Observable.zip(logs.toList(), specialLogs.toList(),
new Func2<List<LogEntry>, List<LogEntry>, List<LogEntry>>() {
@Override
@@ -1886,9 +1883,19 @@ public abstract class GCParser {
mergedLogs.subscribe(new Action1<List<LogEntry>>() {
@Override
public void call(final List<LogEntry> logEntries) {
- DataStore.saveLogsWithoutTransaction(cache.getGeocode(), logEntries);
+ DataStore.saveLogs(cache.getGeocode(), logEntries);
}
});
+ if (cache.isFound() && cache.getVisitedDate() == 0) {
+ ownLogs.subscribe(new Action1<LogEntry>() {
+ @Override
+ public void call(final LogEntry logEntry) {
+ if (logEntry.type == LogType.FOUND_IT) {
+ cache.setVisitedDate(logEntry.date);
+ }
+ }
+ });
+ }
if (Settings.isRatingWanted() && !CancellableHandler.isCancelled(handler)) {
CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_gcvote);
@@ -1901,7 +1908,7 @@ public abstract class GCParser {
}
// Wait for completion of logs parsing, retrieving and merging
- mergedLogs.toBlocking().last();
+ RxUtils.waitForCompletion(mergedLogs);
}
/**
@@ -1923,77 +1930,82 @@ public abstract class GCParser {
}
}
- public static boolean uploadModifiedCoordinates(Geocache cache, Geopoint wpt) {
+ public static boolean uploadModifiedCoordinates(final Geocache cache, final Geopoint wpt) {
return editModifiedCoordinates(cache, wpt);
}
- public static boolean deleteModifiedCoordinates(Geocache cache) {
+ public static boolean deleteModifiedCoordinates(final Geocache cache) {
return editModifiedCoordinates(cache, null);
}
- public static boolean editModifiedCoordinates(Geocache cache, Geopoint wpt) {
+ public static boolean editModifiedCoordinates(final Geocache cache, final Geopoint wpt) {
final String userToken = getUserToken(cache);
if (StringUtils.isEmpty(userToken)) {
return false;
}
- try {
- JSONObject jo;
- if (wpt != null) {
- jo = new JSONObject().put("dto", (new JSONObject().put("ut", userToken)
- .put("data", new JSONObject()
- .put("lat", wpt.getLatitudeE6() / 1E6)
- .put("lng", wpt.getLongitudeE6() / 1E6))));
- } else {
- jo = new JSONObject().put("dto", (new JSONObject().put("ut", userToken)));
- }
-
- final String uriSuffix = wpt != null ? "SetUserCoordinate" : "ResetUserCoordinate";
+ final ObjectNode jo = new ObjectNode(JsonUtils.factory);
+ final ObjectNode dto = jo.putObject("dto").put("ut", userToken);
+ if (wpt != null) {
+ dto.putObject("data").put("lat", wpt.getLatitudeE6() / 1E6).put("lng", wpt.getLongitudeE6() / 1E6);
+ }
- final String uriPrefix = "http://www.geocaching.com/seek/cache_details.aspx/";
- final HttpResponse response = Network.postJsonRequest(uriPrefix + uriSuffix, jo);
- Log.i("Sending to " + uriPrefix + uriSuffix + " :" + jo.toString());
+ final String uriSuffix = wpt != null ? "SetUserCoordinate" : "ResetUserCoordinate";
- if (response != null && response.getStatusLine().getStatusCode() == 200) {
- Log.i("GCParser.editModifiedCoordinates - edited on GC.com");
- return true;
- }
+ final String uriPrefix = "http://www.geocaching.com/seek/cache_details.aspx/";
+ final HttpResponse response = Network.postJsonRequest(uriPrefix + uriSuffix, jo);
- } catch (final JSONException e) {
- Log.e("Unknown exception with json wrap code", e);
+ if (response != null && response.getStatusLine().getStatusCode() == 200) {
+ Log.i("GCParser.editModifiedCoordinates - edited on GC.com");
+ return true;
}
+
Log.e("GCParser.deleteModifiedCoordinates - cannot delete modified coords");
return false;
}
- public static boolean uploadPersonalNote(Geocache cache) {
+ public static boolean uploadPersonalNote(final Geocache cache) {
final String userToken = getUserToken(cache);
if (StringUtils.isEmpty(userToken)) {
return false;
}
- try {
- final JSONObject jo = new JSONObject()
- .put("dto", (new JSONObject()
- .put("et", StringUtils.defaultString(cache.getPersonalNote()))
- .put("ut", userToken)));
-
- final String uriSuffix = "SetUserCacheNote";
+ final ObjectNode jo = new ObjectNode(JsonUtils.factory);
+ jo.putObject("dto").put("et", StringUtils.defaultString(cache.getPersonalNote())).put("ut", userToken);
- final String uriPrefix = "http://www.geocaching.com/seek/cache_details.aspx/";
- final HttpResponse response = Network.postJsonRequest(uriPrefix + uriSuffix, jo);
- Log.i("Sending to " + uriPrefix + uriSuffix + " :" + jo.toString());
+ final String uriSuffix = "SetUserCacheNote";
- if (response != null && response.getStatusLine().getStatusCode() == 200) {
- Log.i("GCParser.uploadPersonalNote - uploaded to GC.com");
- return true;
- }
+ final String uriPrefix = "http://www.geocaching.com/seek/cache_details.aspx/";
+ final HttpResponse response = Network.postJsonRequest(uriPrefix + uriSuffix, jo);
- } catch (final JSONException e) {
- Log.e("Unknown exception with json wrap code", e);
+ if (response != null && response.getStatusLine().getStatusCode() == 200) {
+ Log.i("GCParser.uploadPersonalNote - uploaded to GC.com");
+ return true;
}
+
Log.e("GCParser.uploadPersonalNote - cannot upload personal note");
return false;
}
+ public static boolean ignoreCache(@NonNull final Geocache cache) {
+ final String uri = "http://www.geocaching.com/bookmarks/ignore.aspx?guid=" + cache.getGuid() + "&WptTypeID=" + cache.getType().wptTypeId;
+ final String page = GCLogin.getInstance().postRequestLogged(uri, null);
+
+ if (StringUtils.isBlank(page)) {
+ Log.e("GCParser.ignoreCache: No data from server");
+ return false;
+ }
+
+ final String[] viewstates = GCLogin.getViewstates(page);
+
+ final Parameters params = new Parameters(
+ "__EVENTTARGET", "",
+ "__EVENTARGUMENT", "",
+ "ctl00$ContentBody$btnYes", "Yes. Ignore it.");
+
+ GCLogin.putViewstates(params, viewstates);
+ final String response = Network.getResponseData(Network.postRequest(uri, params));
+
+ return StringUtils.contains(response, "<p class=\"Success\">");
+ }
}