diff options
Diffstat (limited to 'main/src/cgeo/geocaching/connector/gc')
8 files changed, 246 insertions, 261 deletions
diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java index 3b7c31e..4512979 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java @@ -344,6 +344,11 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override + public void logout() { + GCLogin.getInstance().logout(); + } + + @Override public String getUserName() { return GCLogin.getInstance().getActualUserName(); } diff --git a/main/src/cgeo/geocaching/connector/gc/GCConstants.java b/main/src/cgeo/geocaching/connector/gc/GCConstants.java index 3f16156..c2021bb 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConstants.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConstants.java @@ -45,7 +45,6 @@ public final class GCConstants { public final static Pattern PATTERN_OWNER_USERID = Pattern.compile("other caches <a href=\"/seek/nearest\\.aspx\\?u=(.*?)\">hidden</a> or"); public final static Pattern PATTERN_FOUND = Pattern.compile("ctl00_ContentBody_GeoNav_logText\">(Found It|Attended)"); public final static Pattern PATTERN_FOUND_ALTERNATIVE = Pattern.compile("<div class=\"StatusInformationWidget FavoriteWidget\""); - public final static Pattern PATTERN_FOUND_DATE = Pattern.compile(">Logged on: ([^<]+?)<"); public final static Pattern PATTERN_OWNER_DISPLAYNAME = Pattern.compile("<div id=\"ctl00_ContentBody_mcd1\">[^<]+<a href=\"[^\"]+\">([^<]+)</a>"); public final static Pattern PATTERN_TYPE = Pattern.compile("<a href=\"/seek/nearest.aspx\\?tx=([0-9a-f-]+)"); public final static Pattern PATTERN_HIDDEN = Pattern.compile("<div id=\"ctl00_ContentBody_mcd2\">\\W*Hidden[\\s:]*([^<]+?)</div>"); diff --git a/main/src/cgeo/geocaching/connector/gc/GCLogin.java b/main/src/cgeo/geocaching/connector/gc/GCLogin.java index 9f430c0..b2122d4 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCLogin.java +++ b/main/src/cgeo/geocaching/connector/gc/GCLogin.java @@ -175,6 +175,9 @@ public class GCLogin extends AbstractLogin { return StatusCode.NO_ERROR; } + private static String removeDotAndComma(final String str) { + return StringUtils.replaceChars(str, ".,", null); + } /** * Check if the user has been logged in when he retrieved the data. @@ -201,7 +204,7 @@ public class GCLogin extends AbstractLogin { setActualUserName(TextUtils.getMatch(page, GCConstants.PATTERN_LOGIN_NAME, true, "???")); int cachesCount = 0; try { - cachesCount = Integer.parseInt(TextUtils.getMatch(page, GCConstants.PATTERN_CACHES_FOUND, true, "0").replaceAll("[,.]", "")); + cachesCount = Integer.parseInt(removeDotAndComma(TextUtils.getMatch(page, GCConstants.PATTERN_CACHES_FOUND, true, "0"))); } catch (final NumberFormatException e) { Log.e("getLoginStatus: bad cache count", e); } @@ -266,7 +269,7 @@ public class GCLogin extends AbstractLogin { Settings.setGCMemberStatus(GCConstants.MEMBER_STATUS_PM); } - setActualCachesFound(Integer.parseInt(TextUtils.getMatch(profile, GCConstants.PATTERN_CACHES_FOUND, true, "-1").replaceAll("[,.]", ""))); + setActualCachesFound(Integer.parseInt(removeDotAndComma(TextUtils.getMatch(profile, GCConstants.PATTERN_CACHES_FOUND, true, "-1")))); final String avatarURL = TextUtils.getMatch(profile, GCConstants.PATTERN_AVATAR_IMAGE_PROFILE_PAGE, false, null); if (avatarURL != null) { @@ -309,14 +312,14 @@ public class GCLogin extends AbstractLogin { if (GC_CUSTOM_DATE_FORMATS.containsKey(format)) { try { return GC_CUSTOM_DATE_FORMATS.get(format).parse(trimmed); - } catch (final ParseException e) { + } catch (final ParseException ignored) { } } for (final SimpleDateFormat sdf : GC_CUSTOM_DATE_FORMATS.values()) { try { return sdf.parse(trimmed); - } catch (final ParseException e) { + } catch (final ParseException ignored) { } } diff --git a/main/src/cgeo/geocaching/connector/gc/GCMap.java b/main/src/cgeo/geocaching/connector/gc/GCMap.java index 27ce06e..63c42ff 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCMap.java +++ b/main/src/cgeo/geocaching/connector/gc/GCMap.java @@ -9,6 +9,7 @@ import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LiveMapStrategy.Strategy; import cgeo.geocaching.enumerations.LiveMapStrategy.StrategyFlag; import cgeo.geocaching.enumerations.StatusCode; +import cgeo.geocaching.files.ParserException; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter.Format; import cgeo.geocaching.geopoint.Units; @@ -16,20 +17,23 @@ import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Formatter; +import cgeo.geocaching.utils.JsonUtils; import cgeo.geocaching.utils.LeastRecentlyUsedMap; import cgeo.geocaching.utils.Log; +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.StringUtils; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; import rx.Observable; import rx.functions.Func2; import android.graphics.Bitmap; +import java.io.IOException; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; @@ -60,53 +64,41 @@ public class GCMap { // {"name":"HP: Hannover - Sahlkamp","gc":"GC2Q97X","g":"a09149ca-00e0-4aa2-b332-db2b4dfb18d2","available":true,"archived":false,"subrOnly":false,"li":false,"fp":"0","difficulty":{"text":1.0,"value":"1"},"terrain":{"text":1.5,"value":"1_5"},"hidden":"5/29/2011","container":{"text":"Small","value":"small.gif"},"type":{"text":"Traditional Cache","value":2},"owner":{"text":"GeoM@n","value":"1deaa69e-6bcc-421d-95a1-7d32b468cb82"}}] // } - final JSONObject json = new JSONObject(data); - final String status = json.getString("status"); + final ObjectNode json = (ObjectNode) JsonUtils.reader.readTree(data); + final String status = json.path("status").asText(); if (StringUtils.isBlank(status)) { - - throw new JSONException("No status inside JSON"); + throw new ParserException("No status inside JSON"); } if ("success".compareTo(status) != 0) { - throw new JSONException("Wrong status inside JSON"); + throw new ParserException("Wrong status inside JSON"); } - final JSONArray dataArray = json.getJSONArray("data"); + final ArrayNode dataArray = (ArrayNode) json.get("data"); if (dataArray == null) { - throw new JSONException("No data inside JSON"); + throw new ParserException("No data inside JSON"); } final ArrayList<Geocache> caches = new ArrayList<>(); - for (int j = 0; j < dataArray.length(); j++) { + for (final JsonNode dataObject: dataArray) { final Geocache cache = new Geocache(); - - JSONObject dataObject = dataArray.getJSONObject(j); - cache.setName(dataObject.getString("name")); - cache.setGeocode(dataObject.getString("gc")); - cache.setGuid(dataObject.getString("g")); // 34c2e609-5246-4f91-9029-d6c02b0f2a82" - cache.setDisabled(!dataObject.getBoolean("available")); - cache.setArchived(dataObject.getBoolean("archived")); - cache.setPremiumMembersOnly(dataObject.getBoolean("subrOnly")); + cache.setName(dataObject.path("name").asText()); + cache.setGeocode(dataObject.path("gc").asText()); + cache.setGuid(dataObject.path("g").asText()); // 34c2e609-5246-4f91-9029-d6c02b0f2a82" + cache.setDisabled(!dataObject.path("available").asBoolean()); + cache.setArchived(dataObject.path("archived").asBoolean()); + cache.setPremiumMembersOnly(dataObject.path("subrOnly").asBoolean()); // "li" seems to be "false" always - cache.setFavoritePoints(Integer.parseInt(dataObject.getString("fp"))); - JSONObject difficultyObj = dataObject.getJSONObject("difficulty"); - cache.setDifficulty(Float.parseFloat(difficultyObj.getString("text"))); // 3.5 - JSONObject terrainObj = dataObject.getJSONObject("terrain"); - cache.setTerrain(Float.parseFloat(terrainObj.getString("text"))); // 1.5 - cache.setHidden(GCLogin.parseGcCustomDate(dataObject.getString("hidden"), "MM/dd/yyyy")); // 7/23/2001 - JSONObject containerObj = dataObject.getJSONObject("container"); - cache.setSize(CacheSize.getById(containerObj.getString("text"))); // Regular - JSONObject typeObj = dataObject.getJSONObject("type"); - cache.setType(CacheType.getByPattern(typeObj.getString("text"))); // Traditional Cache - JSONObject ownerObj = dataObject.getJSONObject("owner"); - cache.setOwnerDisplayName(ownerObj.getString("text")); + cache.setFavoritePoints(Integer.parseInt(dataObject.path("fp").asText())); + cache.setDifficulty(Float.parseFloat(dataObject.path("difficulty").path("text").asText())); // 3.5 + cache.setTerrain(Float.parseFloat(dataObject.path("terrain").path("text").asText())); // 1.5 + cache.setHidden(GCLogin.parseGcCustomDate(dataObject.path("hidden").asText(), "MM/dd/yyyy")); // 7/23/2001 + cache.setSize(CacheSize.getById(dataObject.path("container").path("text").asText())); // Regular + cache.setType(CacheType.getByPattern(dataObject.path("type").path("text").asText())); // Traditional Cache + cache.setOwnerDisplayName(dataObject.path("owner").path("text").asText()); caches.add(cache); } result.addAndPutInCache(caches); - } catch (JSONException e) { - result.setError(StatusCode.UNKNOWN_ERROR); - } catch (ParseException e) { - result.setError(StatusCode.UNKNOWN_ERROR); - } catch (NumberFormatException e) { + } catch (ParserException | ParseException | IOException | NumberFormatException ignored) { result.setError(StatusCode.UNKNOWN_ERROR); } return result; @@ -125,7 +117,7 @@ public class GCMap { final LeastRecentlyUsedMap<String, String> nameCache = new LeastRecentlyUsedMap.LruCache<>(2000); // JSON id, cache name if (StringUtils.isEmpty(data)) { - throw new JSONException("No page given"); + throw new ParserException("No page given"); } // Example JSON information @@ -134,34 +126,33 @@ public class GCMap { // "data":{"55_55":[{"i":"gEaR","n":"Spiel & Sport"}],"55_54":[{"i":"gEaR","n":"Spiel & Sport"}],"17_25":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"55_53":[{"i":"gEaR","n":"Spiel & Sport"}],"17_27":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"17_26":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"57_53":[{"i":"gEaR","n":"Spiel & Sport"}],"57_55":[{"i":"gEaR","n":"Spiel & Sport"}],"3_62":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"3_61":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"57_54":[{"i":"gEaR","n":"Spiel & Sport"}],"3_60":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"15_27":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"15_26":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"15_25":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"4_60":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"4_61":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"4_62":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"16_25":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"16_26":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"16_27":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"2_62":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"2_60":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"2_61":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"56_53":[{"i":"gEaR","n":"Spiel & Sport"}],"56_54":[{"i":"gEaR","n":"Spiel & Sport"}],"56_55":[{"i":"gEaR","n":"Spiel & Sport"}]} // } - final JSONObject json = new JSONObject(data); + final ObjectNode json = (ObjectNode) JsonUtils.reader.readTree(data); - final JSONArray grid = json.getJSONArray("grid"); - if (grid == null || grid.length() != (UTFGrid.GRID_MAXY + 1)) { - throw new JSONException("No grid inside JSON"); + final ArrayNode grid = (ArrayNode) json.get("grid"); + if (grid == null || grid.size() != (UTFGrid.GRID_MAXY + 1)) { + throw new ParserException("No grid inside JSON"); } - final JSONArray keys = json.getJSONArray("keys"); + final ArrayNode keys = (ArrayNode) json.get("keys"); if (keys == null) { - throw new JSONException("No keys inside JSON"); + throw new ParserException("No keys inside JSON"); } - final JSONObject dataObject = json.getJSONObject("data"); + final ObjectNode dataObject = (ObjectNode) json.get("data"); if (dataObject == null) { - throw new JSONException("No data inside JSON"); + throw new ParserException("No data inside JSON"); } // iterate over the data and construct all caches in this tile Map<String, List<UTFGridPosition>> positions = new HashMap<>(); // JSON id as key Map<String, List<UTFGridPosition>> singlePositions = new HashMap<>(); // JSON id as key - for (int i = 1; i < keys.length(); i++) { // index 0 is empty - String key = keys.getString(i); - if (StringUtils.isNotBlank(key)) { + for (final JsonNode rawKey: keys) { + final String key = rawKey.asText(); + if (StringUtils.isNotBlank(key)) { // index 0 is empty UTFGridPosition pos = UTFGridPosition.fromString(key); - JSONArray dataForKey = dataObject.getJSONArray(key); - for (int j = 0; j < dataForKey.length(); j++) { - JSONObject cacheInfo = dataForKey.getJSONObject(j); - String id = cacheInfo.getString("i"); - nameCache.put(id, cacheInfo.getString("n")); + final ArrayNode dataForKey = (ArrayNode) dataObject.get(key); + for (final JsonNode cacheInfo: dataForKey) { + final String id = cacheInfo.get("i").asText(); + nameCache.put(id, cacheInfo.get("n").asText()); List<UTFGridPosition> listOfPositions = positions.get(id); List<UTFGridPosition> singleListOfPositions = singlePositions.get(id); @@ -174,7 +165,7 @@ public class GCMap { } listOfPositions.add(pos); - if (dataForKey.length() == 1) { + if (dataForKey.size() == 1) { singleListOfPositions.add(pos); } @@ -220,9 +211,7 @@ public class GCMap { searchResult.addAndPutInCache(caches); Log.d("Retrieved " + searchResult.getCount() + " caches for tile " + tile.toString()); - } catch (RuntimeException e) { - Log.e("GCMap.parseMapJSON", e); - } catch (JSONException e) { + } catch (RuntimeException | ParserException | IOException e) { Log.e("GCMap.parseMapJSON", e); } diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java index c771049..394df83 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCParser.java +++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java @@ -30,6 +30,8 @@ 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 +40,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; @@ -59,6 +62,7 @@ import android.net.Uri; import android.text.Html; import java.io.File; +import java.io.IOException; import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; @@ -75,6 +79,7 @@ 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 + private final static ImmutablePair<StatusCode, Geocache> UNKNOWN_PARSE_ERROR = ImmutablePair.of(StatusCode.UNKNOWN_ERROR, null); private static SearchResult parseSearch(final String url, final String pageContent, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { if (StringUtils.isBlank(pageContent)) { @@ -125,12 +130,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 +166,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 +178,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 +192,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 +209,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); } } @@ -266,7 +271,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 +285,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; @@ -371,7 +376,7 @@ public abstract class GCParser { 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.saveLogsWithoutTransaction(parsed.right.getGeocode(), getLogs(parseUserToken(page), Logs.ALL).toBlocking().toIterable()); } return result; } @@ -380,17 +385,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 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 +411,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 +454,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 +498,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,9 +555,9 @@ 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 { @@ -579,13 +587,13 @@ public abstract class GCParser { } } 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 +616,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 +650,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 +672,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 +688,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 +715,10 @@ 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"); + String[] wp = StringUtils.splitByWholeSeparator(wpItems[j], "<td"); // waypoint name // res is null during the unit tests @@ -730,13 +738,12 @@ 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"); + wp = StringUtils.splitByWholeSeparator(wpItems[j], "<td"); } // waypoint note @@ -751,7 +758,7 @@ public abstract class GCParser { // last check for necessary cache conditions if (StringUtils.isBlank(cache.getGeocode())) { - return ImmutablePair.of(StatusCode.UNKNOWN_ERROR, null); + return UNKNOWN_PARSE_ERROR; } cache.setDetailedUpdatedNow(); @@ -762,7 +769,7 @@ public abstract class GCParser { return StringUtils.replaceChars(numberWithPunctuation, ".,", ""); } - public static SearchResult searchByNextPage(final SearchResult search, boolean showCaptcha, RecaptchaReceiver recaptchaReceiver) { + public static SearchResult searchByNextPage(final SearchResult search, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { if (search == null) { return null; } @@ -845,7 +852,7 @@ public abstract class GCParser { * @return */ @Nullable - private static SearchResult searchByAny(final CacheType cacheType, final boolean my, final boolean showCaptcha, final Parameters params, RecaptchaReceiver recaptchaReceiver) { + private static SearchResult searchByAny(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"; @@ -871,12 +878,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, 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, final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { if (StringUtils.isBlank(keyword)) { Log.e("GCParser.searchByKeyword: No keyword given"); return null; @@ -894,7 +901,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, 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 +912,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, 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 +923,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, final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { if (StringUtils.isBlank(userName)) { Log.e("GCParser.searchByOwner: No user name given"); return null; @@ -926,32 +933,28 @@ 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) { + 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; } - 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); + + final ObjectNode response = Network.requestJSON("http://www.geocaching.com/api/geocode", new Parameters("q", address)); + if (response == null) { + return 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 @@ -1001,13 +1004,13 @@ public abstract class GCParser { return null; } - String subPage = StringUtils.substringAfter(page, "class=\"PocketQueryListTable"); + final String subPage = StringUtils.substringAfter(page, "class=\"PocketQueryListTable"); if (StringUtils.isEmpty(subPage)) { Log.e("GCParser.searchPocketQueryList: class \"PocketQueryListTable\" not found on page"); return Collections.emptyList(); } - List<PocketQueryList> list = new ArrayList<>(); + final List<PocketQueryList> list = new ArrayList<>(); final MatcherWrapper matcherPocket = new MatcherWrapper(GCConstants.PATTERN_LIST_PQ, subPage); @@ -1015,7 +1018,7 @@ public abstract class GCParser { int maxCaches; try { maxCaches = Integer.parseInt(matcherPocket.group(1)); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { maxCaches = 0; Log.e("GCParser.searchPocketQueryList: Unable to parse max caches", e); } @@ -1029,7 +1032,7 @@ public abstract class GCParser { Collections.sort(list, new Comparator<PocketQueryList>() { @Override - public int compare(PocketQueryList left, PocketQueryList right) { + public int compare(final PocketQueryList left, final PocketQueryList right) { return String.CASE_INSENSITIVE_ORDER.compare(left.getName(), right.getName()); } }); @@ -1417,7 +1420,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 +1489,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,7 +1520,7 @@ 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 @@ -1522,12 +1528,12 @@ public abstract class GCParser { if (releaseString != null) { try { trackable.setReleased(dateTbIn1.parse(releaseString)); - } catch (ParseException e) { + } 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); + } catch (final ParseException e) { + Log.e("Could not parse trackable release " + releaseString, e); } } } @@ -1538,14 +1544,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 +1564,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 +1591,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 +1636,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; } @@ -1662,17 +1668,15 @@ public abstract class GCParser { * 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,7 +1695,7 @@ public abstract class GCParser { Log.e("GCParser.loadLogsFromDetails: error " + statusCode + " when requesting log information"); return Observable.empty(); } - String rawResponse = Network.getResponseData(response); + final String rawResponse = Network.getResponseData(response); if (rawResponse == null) { Log.e("GCParser.loadLogsFromDetails: unable to read whole response"); return Observable.empty(); @@ -1712,56 +1716,51 @@ public abstract class GCParser { } 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(rawResponse); + 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; 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); } // 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 +1769,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(); } @@ -1865,16 +1864,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 @@ -1889,6 +1884,16 @@ public abstract class GCParser { DataStore.saveLogsWithoutTransaction(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); @@ -1923,75 +1928,61 @@ 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); + Log.i("Sending to " + uriPrefix + uriSuffix + " :" + jo.toString()); - } 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); + Log.i("Sending to " + uriPrefix + uriSuffix + " :" + jo.toString()); - } 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; } diff --git a/main/src/cgeo/geocaching/connector/gc/RecaptchaHandler.java b/main/src/cgeo/geocaching/connector/gc/RecaptchaHandler.java index 5bcfd17..c392521 100644 --- a/main/src/cgeo/geocaching/connector/gc/RecaptchaHandler.java +++ b/main/src/cgeo/geocaching/connector/gc/RecaptchaHandler.java @@ -42,9 +42,9 @@ public class RecaptchaHandler extends Handler { } private void loadChallenge(final ImageView imageView, final View reloadButton, final boolean needsFetch) { - final Observable<Bitmap> captcha = Observable.defer(new Func0<Observable<? extends Bitmap>>() { + final Observable<Bitmap> captcha = Observable.defer(new Func0<Observable<Bitmap>>() { @Override - public Observable<? extends Bitmap> call() { + public Observable<Bitmap> call() { if (needsFetch) { recaptchaReceiver.fetchChallenge(); } @@ -53,7 +53,7 @@ public class RecaptchaHandler extends Handler { if (is != null) { try { final Bitmap img = BitmapFactory.decodeStream(is); - return Observable.from(img); + return Observable.just(img); } catch (final Exception e) { Log.e("RecaptchaHandler.getCaptcha", e); return Observable.error(e); diff --git a/main/src/cgeo/geocaching/connector/gc/Tile.java b/main/src/cgeo/geocaching/connector/gc/Tile.java index 18fe65c..ff13fe3 100644 --- a/main/src/cgeo/geocaching/connector/gc/Tile.java +++ b/main/src/cgeo/geocaching/connector/gc/Tile.java @@ -56,11 +56,11 @@ public class Tile { private final int zoomLevel; private final Viewport viewPort; - public Tile(Geopoint origin, int zoomlevel) { + public Tile(final Geopoint origin, final int zoomlevel) { this(calcX(origin, clippedZoomlevel(zoomlevel)), calcY(origin, clippedZoomlevel(zoomlevel)), clippedZoomlevel(zoomlevel)); } - private Tile(int tileX, int tileY, int zoomlevel) { + private Tile(final int tileX, final int tileY, final int zoomlevel) { this.zoomLevel = clippedZoomlevel(zoomlevel); @@ -74,7 +74,7 @@ public class Tile { return zoomLevel; } - private static int clippedZoomlevel(int zoomlevel) { + private static int clippedZoomlevel(final int zoomlevel) { return Math.max(Math.min(zoomlevel, ZOOMLEVEL_MAX), ZOOMLEVEL_MIN); } @@ -95,7 +95,7 @@ public class Tile { */ private static int calcY(final Geopoint origin, final int zoomlevel) { // Optimization from Bing - double sinLatRad = Math.sin(Math.toRadians(origin.getLatitude())); + final double sinLatRad = Math.sin(Math.toRadians(origin.getLatitude())); // The cut of the fractional part instead of rounding to the nearest integer is intentional and part of the algorithm return (int) ((0.5 - Math.log((1 + sinLatRad) / (1 - sinLatRad)) / (4 * Math.PI)) * NUMBER_OF_TILES[zoomlevel]); } @@ -115,13 +115,13 @@ public class Tile { * href="http://developers.cloudmade.com/projects/tiles/examples/convert-coordinates-to-tile-numbers">Cloudmade</a> */ @NonNull - public Geopoint getCoord(UTFGridPosition pos) { + public Geopoint getCoord(final UTFGridPosition pos) { - double pixX = tileX * TILE_SIZE + pos.x * 4; - double pixY = tileY * TILE_SIZE + pos.y * 4; + final double pixX = tileX * TILE_SIZE + pos.x * 4; + final double pixY = tileY * TILE_SIZE + pos.y * 4; - double lonDeg = ((360.0 * pixX) / NUMBER_OF_PIXELS[this.zoomLevel]) - 180.0; - double latRad = Math.atan(Math.sinh(Math.PI * (1 - 2 * pixY / NUMBER_OF_PIXELS[this.zoomLevel]))); + final double lonDeg = ((360.0 * pixX) / NUMBER_OF_PIXELS[this.zoomLevel]) - 180.0; + final double latRad = Math.atan(Math.sinh(Math.PI * (1 - 2 * pixY / NUMBER_OF_PIXELS[this.zoomLevel]))); return new Geopoint(Math.toDegrees(latRad), lonDeg); } @@ -153,8 +153,8 @@ public class Tile { / Math.log(2) ); - Tile tileLeft = new Tile(left, zoom); - Tile tileRight = new Tile(right, zoom); + final Tile tileLeft = new Tile(left, zoom); + final Tile tileRight = new Tile(right, zoom); if (Math.abs(tileLeft.tileX - tileRight.tileX) < (numberOfTiles - 1)) { zoom += 1; @@ -190,8 +190,8 @@ public class Tile { ) / Math.log(2) ); - Tile tileBottom = new Tile(bottom, zoom); - Tile tileTop = new Tile(top, zoom); + final Tile tileBottom = new Tile(bottom, zoom); + final Tile tileTop = new Tile(top, zoom); if (Math.abs(tileBottom.tileY - tileTop.tileY) > (numberOfTiles - 1)) { zoom -= 1; @@ -200,7 +200,7 @@ public class Tile { return Math.min(zoom, ZOOMLEVEL_MAX); } - private static double tanGrad(double angleGrad) { + private static double tanGrad(final double angleGrad) { return Math.tan(angleGrad / 180.0 * Math.PI); } @@ -211,12 +211,12 @@ public class Tile { * @param x * @return */ - private static double asinh(double x) { + private static double asinh(final double x) { return Math.log(x + Math.sqrt(x * x + 1.0)); } @Override - public boolean equals(Object o) { + public boolean equals(final Object o) { if (this == o) { return true; } @@ -260,7 +260,7 @@ public class Tile { public Bitmap call() { try { return response != null ? BitmapFactory.decodeStream(response.getEntity().getContent()) : null; - } catch (IOException e) { + } catch (final IOException e) { Log.e("Tile.requestMapTile() ", e); return null; } @@ -298,20 +298,20 @@ public class Tile { * @return */ protected static Set<Tile> getTilesForViewport(final Viewport viewport, final int tilesOnAxis, final int minZoom) { - Set<Tile> tiles = new HashSet<>(); - int zoom = Math.max( + final Set<Tile> tiles = new HashSet<>(); + final int zoom = Math.max( Math.min(Tile.calcZoomLon(viewport.bottomLeft, viewport.topRight, tilesOnAxis), Tile.calcZoomLat(viewport.bottomLeft, viewport.topRight, tilesOnAxis)), minZoom); - Tile tileBottomLeft = new Tile(viewport.bottomLeft, zoom); - Tile tileTopRight = new Tile(viewport.topRight, zoom); + final Tile tileBottomLeft = new Tile(viewport.bottomLeft, zoom); + final Tile tileTopRight = new Tile(viewport.topRight, zoom); - int xLow = Math.min(tileBottomLeft.getX(), tileTopRight.getX()); - int xHigh = Math.max(tileBottomLeft.getX(), tileTopRight.getX()); + final int xLow = Math.min(tileBottomLeft.getX(), tileTopRight.getX()); + final int xHigh = Math.max(tileBottomLeft.getX(), tileTopRight.getX()); - int yLow = Math.min(tileBottomLeft.getY(), tileTopRight.getY()); - int yHigh = Math.max(tileBottomLeft.getY(), tileTopRight.getY()); + final int yLow = Math.min(tileBottomLeft.getY(), tileTopRight.getY()); + final int yHigh = Math.max(tileBottomLeft.getY(), tileTopRight.getY()); for (int xNum = xLow; xNum <= xHigh; xNum++) { for (int yNum = yLow; yNum <= yHigh; yNum++) { @@ -324,8 +324,6 @@ public class Tile { public static class TileCache extends LeastRecentlyUsedSet<Tile> { - private static final long serialVersionUID = -1942301031192719547L; - public TileCache() { super(64); } diff --git a/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java b/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java index eff193a..f9b8e1c 100644 --- a/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java +++ b/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java @@ -46,7 +46,7 @@ public final class UTFGridPosition { final int y = Integer.parseInt(matcher.group(2)); return new UTFGridPosition(x, y); } - } catch (NumberFormatException e) { + } catch (NumberFormatException ignored) { } return new UTFGridPosition(0, 0); } |
