aboutsummaryrefslogtreecommitdiffstats
path: root/main/src/cgeo/geocaching/connector/gc
diff options
context:
space:
mode:
Diffstat (limited to 'main/src/cgeo/geocaching/connector/gc')
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCBase.java202
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCConnector.java17
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCParser.java1618
-rw-r--r--main/src/cgeo/geocaching/connector/gc/Login.java434
-rw-r--r--main/src/cgeo/geocaching/connector/gc/Tile.java39
5 files changed, 2161 insertions, 149 deletions
diff --git a/main/src/cgeo/geocaching/connector/gc/GCBase.java b/main/src/cgeo/geocaching/connector/gc/GCBase.java
index 871e75a..2f22c52 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCBase.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCBase.java
@@ -2,7 +2,6 @@ package cgeo.geocaching.connector.gc;
import cgeo.geocaching.SearchResult;
import cgeo.geocaching.Settings;
-import cgeo.geocaching.cgBase;
import cgeo.geocaching.cgCache;
import cgeo.geocaching.cgeoapplication;
import cgeo.geocaching.enumerations.CacheSize;
@@ -13,11 +12,11 @@ import cgeo.geocaching.enumerations.StatusCode;
import cgeo.geocaching.geopoint.Geopoint;
import cgeo.geocaching.geopoint.IConversion;
import cgeo.geocaching.geopoint.Viewport;
-import cgeo.geocaching.network.Login;
import cgeo.geocaching.network.Network;
+import cgeo.geocaching.network.Parameters;
import cgeo.geocaching.ui.Formatter;
import cgeo.geocaching.utils.BaseUtils;
-import cgeo.geocaching.utils.LeastRecentlyUsedCache;
+import cgeo.geocaching.utils.LeastRecentlyUsedMap;
import cgeo.geocaching.utils.Log;
import org.apache.commons.collections.CollectionUtils;
@@ -31,6 +30,7 @@ import android.graphics.Bitmap;
import java.text.ParseException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -52,6 +52,9 @@ public class GCBase {
protected final static long GC_BASE31 = 31;
protected final static long GC_BASE16 = 16;
+ private final static LeastRecentlyUsedMap<Integer, Tile> tileCache = new LeastRecentlyUsedMap.LruCache<Integer, Tile>(64);
+ private static Viewport lastSearchViewport = null;
+
/**
* Searches the view port on the live map with Strategy.AUTO
*
@@ -84,6 +87,17 @@ public class GCBase {
}
}
+ public static void removeFromTileCache(Geopoint coords) {
+ if (coords != null) {
+ Collection<Tile> tiles = new ArrayList<Tile>(tileCache.values());
+ for (Tile tile : tiles) {
+ if (tile.containsPoint(coords)) {
+ tileCache.remove(tile.hashCode());
+ }
+ }
+ }
+ }
+
/**
* Searches the view port on the live map for caches.
* The strategy dictates if only live map information is used or if an additional
@@ -98,7 +112,7 @@ public class GCBase {
* @return
*/
private static SearchResult searchByViewport(final Viewport viewport, final String[] tokens, Strategy strategy) {
- Log.d(Settings.tag, "GCBase.searchByViewport" + viewport.toString());
+ Log.d("GCBase.searchByViewport" + viewport.toString());
String referer = GCConstants.URL_LIVE_MAP;
@@ -110,58 +124,76 @@ public class GCBase {
for (Tile tile : tiles) {
- StringBuilder url = new StringBuilder();
- url.append("?x=").append(tile.getX()) // x tile
- .append("&y=").append(tile.getY()) // y tile
- .append("&z=").append(tile.getZoomlevel()); // zoom level
- if (tokens != null) {
- url.append("&k=").append(tokens[0]); // user session
- url.append("&st=").append(tokens[1]); // session token
- }
- url.append("&ep=1");
- if (Settings.isExcludeMyCaches()) {
- url.append("&hf=1").append("&hh=1"); // hide found, hide hidden
- }
- if (Settings.getCacheType() == CacheType.TRADITIONAL) {
- url.append("&ect=9,5,3,6,453,13,1304,137,11,4,8,1858"); // 2 = tradi 3 = multi 8 = mystery
- }
- if (Settings.getCacheType() == CacheType.MULTI) {
- url.append("&ect=9,5,2,6,453,13,1304,137,11,4,8,1858");
- }
- if (Settings.getCacheType() == CacheType.MYSTERY) {
- url.append("&ect=9,5,3,6,453,13,1304,137,11,4,2,1858");
- }
- if (tile.getZoomlevel() != 14) {
- url.append("&_=").append(String.valueOf(System.currentTimeMillis()));
- }
- // other types t.b.d
- final String urlString = url.toString();
+ if (!tileCache.containsKey(tile.hashCode())) {
+ final Parameters params = new Parameters(
+ "x", String.valueOf(tile.getX()),
+ "y", String.valueOf(tile.getY()),
+ "z", String.valueOf(tile.getZoomlevel()),
+ "ep", "1");
+ if (tokens != null) {
+ params.put("k", tokens[0], "st", tokens[1]);
+ }
+ if (Settings.isExcludeMyCaches()) {
+ params.put("hf", "1", "hh", "1"); // hide found, hide hidden
+ }
+ if (Settings.getCacheType() == CacheType.TRADITIONAL) {
+ params.put("ect", "9,5,3,6,453,13,1304,137,11,4,8,1858"); // 2 = tradi 3 = multi 8 = mystery
+ } else if (Settings.getCacheType() == CacheType.MULTI) {
+ params.put("ect", "9,5,2,6,453,13,1304,137,11,4,8,1858");
+ } else if (Settings.getCacheType() == CacheType.MYSTERY) {
+ params.put("ect", "9,5,3,6,453,13,1304,137,11,4,2,1858");
+ }
+ if (tile.getZoomlevel() != 14) {
+ params.put("_", String.valueOf(System.currentTimeMillis()));
+ }
+ // TODO: other types t.b.d
- // The PNG must be request before ! Else the following request would return with 204 - No Content
- Bitmap bitmap = Tile.requestMapTile(GCConstants.URL_MAP_TILE + urlString, referer);
+ // The PNG must be requested first, otherwise the following request would always return with 204 - No Content
+ Bitmap bitmap = Tile.requestMapTile(GCConstants.URL_MAP_TILE, params, referer);
- assert bitmap.getWidth() == Tile.TILE_SIZE : "Bitmap has wrong width";
- assert bitmap.getHeight() == Tile.TILE_SIZE : "Bitmap has wrong height";
+ // Check bitmap size
+ if (bitmap != null && (bitmap.getWidth() != Tile.TILE_SIZE ||
+ bitmap.getHeight() != Tile.TILE_SIZE)) {
+ bitmap.recycle();
+ bitmap = null;
+ }
- String data = Tile.requestMapInfo(GCConstants.URL_MAP_INFO + urlString, referer);
- if (StringUtils.isEmpty(data)) {
- Log.e(Settings.tag, "GCBase.searchByViewport: No data from server for tile (" + tile.getX() + "/" + tile.getY() + ")");
- } else {
- final SearchResult search = parseMapJSON(data, tile, bitmap, strategy);
- if (search == null || CollectionUtils.isEmpty(search.getGeocodes())) {
- Log.e(Settings.tag, "GCBase.searchByViewport: No cache parsed for viewport " + viewport);
+ String data = Tile.requestMapInfo(GCConstants.URL_MAP_INFO, params, referer);
+ if (StringUtils.isEmpty(data)) {
+ Log.e("GCBase.searchByViewport: No data from server for tile (" + tile.getX() + "/" + tile.getY() + ")");
+ } else {
+ final SearchResult search = parseMapJSON(data, tile, bitmap, strategy);
+ if (search == null || CollectionUtils.isEmpty(search.getGeocodes())) {
+ Log.e("GCBase.searchByViewport: No cache parsed for viewport " + viewport);
+ }
+ else {
+ searchResult.addGeocodes(search.getGeocodes());
+ }
+ tileCache.put(tile.hashCode(), tile);
}
- else {
- searchResult.addGeocodes(search.getGeocodes());
+
+ // release native bitmap memory
+ if (bitmap != null) {
+ bitmap.recycle();
}
+
}
}
}
if (strategy.flags.contains(StrategyFlag.SEARCH_NEARBY)) {
- SearchResult search = cgBase.searchByCoords(null, viewport.getCenter(), Settings.getCacheType(), false);
- if (search != null) {
- searchResult.addGeocodes(search.getGeocodes());
+ Geopoint center = viewport.getCenter();
+ if ((lastSearchViewport == null) || !lastSearchViewport.contains(center)) {
+ SearchResult search = GCParser.searchByCoords(null, center, Settings.getCacheType(), false);
+ if (search != null && !search.isEmpty()) {
+ final Set<String> geocodes = search.getGeocodes();
+ if (Settings.isPremiumMember()) {
+ lastSearchViewport = cgeoapplication.getInstance().getBounds(geocodes);
+ } else {
+ lastSearchViewport = new Viewport(center, 0.01, 0.01);
+ }
+ searchResult.addGeocodes(geocodes);
+ }
}
}
@@ -180,7 +212,7 @@ public class GCBase {
try {
- final LeastRecentlyUsedCache<String, String> nameCache = new LeastRecentlyUsedCache<String, String>(2000); // JSON id, cache name
+ final LeastRecentlyUsedMap<String, String> nameCache = new LeastRecentlyUsedMap.LruCache<String, String>(2000); // JSON id, cache name
if (StringUtils.isEmpty(data)) {
throw new JSONException("No page given");
@@ -265,7 +297,7 @@ public class GCBase {
cache.setName(nameCache.get(id));
cache.setZoomlevel(tile.getZoomlevel());
cache.setCoords(tile.getCoord(xy));
- if (strategy.flags.contains(StrategyFlag.PARSE_TILES) && positions.size() < 64) {
+ if (strategy.flags.contains(StrategyFlag.PARSE_TILES) && positions.size() < 64 && bitmap != null) {
// don't parse if there are too many caches. The decoding would return too much wrong results
IconDecoder.parseMapPNG(cache, bitmap, xy, tile.getZoomlevel());
} else {
@@ -273,10 +305,10 @@ public class GCBase {
}
searchResult.addCache(cache);
}
- Log.d(Settings.tag, "Retrieved " + searchResult.getCount() + " caches for tile " + tile.toString());
+ Log.d("Retrieved " + searchResult.getCount() + " caches for tile " + tile.toString());
} catch (Exception e) {
- Log.e(Settings.tag, "GCBase.parseMapJSON", e);
+ Log.e("GCBase.parseMapJSON", e);
}
return searchResult;
@@ -325,65 +357,9 @@ public class GCBase {
return gcid;
}
- private static String modulo(final long value, final long base, final String sequence) {
- String result = "";
- long rest = 0;
- long divResult = value;
- do
- {
- rest = divResult % base;
- divResult = (int) Math.floor(divResult / base);
- result = sequence.charAt((int) rest) + result;
- } while (divResult != 0);
- return result;
- }
-
- /**
- * Convert (old) GCIds to GCCode (geocode)
- *
- * Based on http://www.geoclub.de/viewtopic.php?f=111&t=54859&start=40
- */
- public static String gcidToGCCode(final long gcid) {
- String gccode = modulo(gcid + 411120, GC_BASE31, SEQUENCE_GCID);
- if ((gccode.length() < 4) || (gccode.length() == 4 && SEQUENCE_GCID.indexOf(gccode.charAt(0)) < 16)) {
- gccode = modulo(gcid, GC_BASE16, SEQUENCE_GCID);
- }
- return "GC" + gccode;
- }
-
- /**
- * Convert ids from the live map to (old) GCIds
- *
- * Based on http://www.geoclub.de/viewtopic.php?f=111&t=54859&start=40
- */
- public static long newidToGCId(final String newid) {
- long gcid = 0;
- for (int p = 0; p < newid.length(); p++) {
- gcid = GC_BASE57 * gcid + SEQUENCE_NEWID.indexOf(newid.charAt(p));
- }
- return gcid;
- }
-
- /**
- * Convert (old) GCIds to ids used in the live map
- *
- * Based on http://www.geoclub.de/viewtopic.php?f=111&t=54859&start=40
- */
- public static String gcidToNewId(final long gcid) {
- return modulo(gcid, GC_BASE57, SEQUENCE_NEWID);
- }
-
- /**
- * Convert ids from the live map into GCCode (geocode)
- */
- public static String newidToGeocode(final String newid) {
- long gcid = GCBase.newidToGCId(newid);
- return GCBase.gcidToGCCode(gcid);
- }
-
/** Get user session & session token from the Live Map. Needed for following requests */
public static String[] getTokens() {
- final HttpResponse response = Network.request(GCConstants.URL_LIVE_MAP, null, false);
+ final HttpResponse response = Network.getRequest(GCConstants.URL_LIVE_MAP);
final String data = Network.getResponseData(response);
String userSession = BaseUtils.getMatch(data, GCConstants.PATTERN_USERSESSION, "");
String sessionToken = BaseUtils.getMatch(data, GCConstants.PATTERN_SESSIONTOKEN, "");
@@ -392,18 +368,14 @@ public class GCBase {
public static SearchResult searchByGeocodes(final Set<String> geocodes) {
- SearchResult result = new SearchResult();
+ final SearchResult result = new SearchResult();
final String geocodeList = StringUtils.join(geocodes.toArray(), "|");
-
- String referer = GCConstants.URL_LIVE_MAP_DETAILS;
-
- StringBuilder url = new StringBuilder();
- url.append("?i=").append(geocodeList).append("&_=").append(String.valueOf(System.currentTimeMillis()));
- final String urlString = url.toString();
+ final String referer = GCConstants.URL_LIVE_MAP_DETAILS;
try {
- String data = Tile.requestMapInfo(referer + urlString, referer);
+ final Parameters params = new Parameters("i", geocodeList, "_", String.valueOf(System.currentTimeMillis()));
+ final String data = Tile.requestMapInfo(referer, params, referer);
// Example JSON information
// {"status":"success",
diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java
index d955418..a431359 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java
@@ -3,13 +3,11 @@ package cgeo.geocaching.connector.gc;
import cgeo.geocaching.R;
import cgeo.geocaching.SearchResult;
import cgeo.geocaching.Settings;
-import cgeo.geocaching.cgBase;
import cgeo.geocaching.cgCache;
import cgeo.geocaching.cgeoapplication;
import cgeo.geocaching.connector.AbstractConnector;
import cgeo.geocaching.enumerations.StatusCode;
import cgeo.geocaching.geopoint.Viewport;
-import cgeo.geocaching.network.Network;
import cgeo.geocaching.network.Parameters;
import cgeo.geocaching.utils.CancellableHandler;
import cgeo.geocaching.utils.Log;
@@ -17,7 +15,6 @@ import cgeo.geocaching.utils.Log;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
-
import java.util.Set;
import java.util.regex.Pattern;
@@ -90,7 +87,7 @@ public class GCConnector extends AbstractConnector {
public SearchResult searchByGeocode(final String geocode, final String guid, final cgeoapplication app, final CancellableHandler handler) {
if (app == null) {
- Log.e(Settings.tag, "cgeoBase.searchByGeocode: No application found");
+ Log.e("cgeoBase.searchByGeocode: No application found");
return null;
}
@@ -103,15 +100,15 @@ public class GCConnector extends AbstractConnector {
params.put("log", "y");
params.put("numlogs", String.valueOf(GCConstants.NUMBER_OF_LOGS));
- cgBase.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_loadpage);
+ CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_loadpage);
- final String page = Network.requestLogged("http://www.geocaching.com/seek/cache_details.aspx", params, false, false, false);
+ final String page = Login.getRequestLogged("http://www.geocaching.com/seek/cache_details.aspx", params);
if (StringUtils.isEmpty(page)) {
final SearchResult search = new SearchResult();
if (app.isThere(geocode, guid, true, false)) {
if (StringUtils.isBlank(geocode) && StringUtils.isNotBlank(guid)) {
- Log.i(Settings.tag, "Loading old cache from cache.");
+ Log.i("Loading old cache from cache.");
search.addGeocode(app.getGeocode(guid));
} else {
@@ -121,15 +118,15 @@ public class GCConnector extends AbstractConnector {
return search;
}
- Log.e(Settings.tag, "cgeoBase.searchByGeocode: No data from server");
+ Log.e("cgeoBase.searchByGeocode: No data from server");
search.setError(StatusCode.COMMUNICATION_ERROR);
return search;
}
- final SearchResult searchResult = cgBase.parseCache(page, handler);
+ final SearchResult searchResult = GCParser.parseCache(page, handler);
if (searchResult == null || CollectionUtils.isEmpty(searchResult.getGeocodes())) {
- Log.e(Settings.tag, "cgeoBase.searchByGeocode: No cache parsed");
+ Log.e("cgeoBase.searchByGeocode: No cache parsed");
return searchResult;
}
diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java
new file mode 100644
index 0000000..cf7c762
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java
@@ -0,0 +1,1618 @@
+package cgeo.geocaching.connector.gc;
+
+import cgeo.geocaching.LogEntry;
+import cgeo.geocaching.R;
+import cgeo.geocaching.SearchResult;
+import cgeo.geocaching.Settings;
+import cgeo.geocaching.TrackableLog;
+import cgeo.geocaching.cgCache;
+import cgeo.geocaching.cgImage;
+import cgeo.geocaching.cgSearchThread;
+import cgeo.geocaching.cgTrackable;
+import cgeo.geocaching.cgWaypoint;
+import cgeo.geocaching.cgeoapplication;
+import cgeo.geocaching.enumerations.CacheSize;
+import cgeo.geocaching.enumerations.CacheType;
+import cgeo.geocaching.enumerations.LoadFlags;
+import cgeo.geocaching.enumerations.LoadFlags.SaveFlag;
+import cgeo.geocaching.enumerations.LogType;
+import cgeo.geocaching.enumerations.LogTypeTrackable;
+import cgeo.geocaching.enumerations.StatusCode;
+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.network.Network;
+import cgeo.geocaching.network.Parameters;
+import cgeo.geocaching.ui.DirectionImage;
+import cgeo.geocaching.utils.BaseUtils;
+import cgeo.geocaching.utils.CancellableHandler;
+import cgeo.geocaching.utils.Log;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpResponse;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.net.Uri;
+import android.text.Html;
+import android.text.Spannable;
+import android.text.Spanned;
+import android.text.style.StrikethroughSpan;
+
+import java.net.URLDecoder;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.regex.Matcher;
+
+public abstract class GCParser {
+ private final static SimpleDateFormat dateTbIn1 = new SimpleDateFormat("EEEEE, dd MMMMM yyyy", Locale.ENGLISH); // Saturday, 28 March 2009
+ private final static SimpleDateFormat dateTbIn2 = new SimpleDateFormat("EEEEE, MMMMM dd, yyyy", Locale.ENGLISH); // Saturday, March 28, 2009
+
+ private static SearchResult parseSearch(final cgSearchThread thread, final String url, final String pageContent, final boolean showCaptcha) {
+ if (StringUtils.isBlank(pageContent)) {
+ Log.e("cgeoBase.parseSearch: No page given");
+ return null;
+ }
+
+ final List<String> cids = new ArrayList<String>();
+ final List<String> guids = new ArrayList<String>();
+ String recaptchaChallenge = null;
+ String recaptchaText = null;
+ String page = pageContent;
+
+ final SearchResult searchResult = new SearchResult();
+ searchResult.setUrl(url);
+ searchResult.viewstates = Login.getViewstates(page);
+
+ // recaptcha
+ if (showCaptcha) {
+ String recaptchaJsParam = BaseUtils.getMatch(page, GCConstants.PATTERN_SEARCH_RECAPTCHA, false, null);
+
+ if (recaptchaJsParam != null) {
+ final Parameters params = new Parameters("k", recaptchaJsParam.trim());
+ final String recaptchaJs = Network.getResponseData(Network.getRequest("http://www.google.com/recaptcha/api/challenge", params));
+
+ if (StringUtils.isNotBlank(recaptchaJs)) {
+ recaptchaChallenge = BaseUtils.getMatch(recaptchaJs, GCConstants.PATTERN_SEARCH_RECAPTCHACHALLENGE, true, 1, null, true);
+ }
+ }
+ if (thread != null && StringUtils.isNotBlank(recaptchaChallenge)) {
+ thread.setChallenge(recaptchaChallenge);
+ thread.notifyNeed();
+ }
+ }
+
+ if (!page.contains("SearchResultsTable")) {
+ // there are no results. aborting here avoids a wrong error log in the next parsing step
+ return searchResult;
+ }
+
+ int startPos = page.indexOf("<div id=\"ctl00_ContentBody_ResultsPanel\"");
+ if (startPos == -1) {
+ Log.e("cgeoBase.parseSearch: ID \"ctl00_ContentBody_dlResults\" not found on page");
+ return null;
+ }
+
+ page = page.substring(startPos); // cut on <table
+
+ startPos = page.indexOf('>');
+ int endPos = page.indexOf("ctl00_ContentBody_UnitTxt");
+ if (startPos == -1 || endPos == -1) {
+ Log.e("cgeoBase.parseSearch: ID \"ctl00_ContentBody_UnitTxt\" not found on page");
+ return null;
+ }
+
+ 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;
+
+ for (int z = 1; z < rows_count; z++) {
+ final cgCache cache = new cgCache();
+ String row = rows[z];
+
+ // check for cache type presence
+ if (!row.contains("images/wpttypes")) {
+ continue;
+ }
+
+ try {
+ final Matcher matcherGuidAndDisabled = GCConstants.PATTERN_SEARCH_GUIDANDDISABLED.matcher(row);
+
+ while (matcherGuidAndDisabled.find()) {
+ if (matcherGuidAndDisabled.groupCount() > 0) {
+ guids.add(matcherGuidAndDisabled.group(1));
+
+ cache.setGuid(matcherGuidAndDisabled.group(1));
+ if (matcherGuidAndDisabled.group(4) != null) {
+ cache.setName(Html.fromHtml(matcherGuidAndDisabled.group(4).trim()).toString());
+ }
+ if (matcherGuidAndDisabled.group(6) != null) {
+ cache.setLocation(Html.fromHtml(matcherGuidAndDisabled.group(6).trim()).toString());
+ }
+
+ final String attr = matcherGuidAndDisabled.group(2);
+ if (attr != null) {
+ cache.setDisabled(attr.contains("Strike"));
+
+ cache.setArchived(attr.contains("OldWarning"));
+ }
+ }
+ }
+ } catch (Exception e) {
+ // failed to parse GUID and/or Disabled
+ Log.w("cgeoBase.parseSearch: Failed to parse GUID and/or Disabled data");
+ }
+
+ if (Settings.isExcludeDisabledCaches() && (cache.isDisabled() || cache.isArchived())) {
+ // skip disabled and archived caches
+ continue;
+ }
+
+ String inventoryPre = null;
+
+ cache.setGeocode(BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_GEOCODE, true, 1, cache.getGeocode(), true).toUpperCase());
+
+ // cache type
+ cache.setType(CacheType.getByPattern(BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_TYPE, true, 1, null, true)));
+
+ // cache direction - image
+ if (Settings.getLoadDirImg()) {
+ cache.setDirectionImg(URLDecoder.decode(BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION, true, 1, cache.getDirectionImg(), true)));
+ }
+
+ // cache inventory
+ final Matcher matcherTbs = GCConstants.PATTERN_SEARCH_TRACKABLES.matcher(row);
+ while (matcherTbs.find()) {
+ if (matcherTbs.groupCount() > 0) {
+ cache.setInventoryItems(Integer.parseInt(matcherTbs.group(1)));
+ inventoryPre = matcherTbs.group(2);
+ }
+ }
+
+ if (StringUtils.isNotBlank(inventoryPre)) {
+ final Matcher matcherTbsInside = GCConstants.PATTERN_SEARCH_TRACKABLESINSIDE.matcher(inventoryPre);
+ while (matcherTbsInside.find()) {
+ if (matcherTbsInside.groupCount() == 2 && matcherTbsInside.group(2) != null) {
+ final String inventoryItem = matcherTbsInside.group(2).toLowerCase();
+ if (inventoryItem.equals("premium member only cache")) {
+ continue;
+ } else {
+ if (cache.getInventoryItems() <= 0) {
+ cache.setInventoryItems(1);
+ }
+ }
+ }
+ }
+ }
+
+ // premium cache
+ cache.setPremiumMembersOnly(row.contains("/images/small_profile.gif"));
+
+ // found it
+ cache.setFound(row.contains("/images/icons/icon_smile"));
+
+ // own it
+ cache.setOwn(row.contains("/images/silk/star.png"));
+
+ // id
+ String result = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_ID, null);
+ if (null != result) {
+ cache.setCacheId(result);
+ cids.add(cache.getCacheId());
+ }
+
+ // favorite count
+ try {
+ result = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_FAVORITE, false, 1, null, true);
+ if (null != result) {
+ cache.setFavoritePoints(Integer.parseInt(result));
+ }
+ } catch (NumberFormatException e) {
+ Log.w("cgeoBase.parseSearch: Failed to parse favourite count");
+ }
+
+ if (cache.getNameSp() == null) {
+ cache.setNameSp((new Spannable.Factory()).newSpannable(cache.getName()));
+ if (cache.isDisabled() || cache.isArchived()) { // strike
+ cache.getNameSp().setSpan(new StrikethroughSpan(), 0, cache.getNameSp().toString().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+
+ searchResult.addCache(cache);
+ }
+
+ // total caches found
+ try {
+ String result = BaseUtils.getMatch(page, GCConstants.PATTERN_SEARCH_TOTALCOUNT, false, 1, null, true);
+ if (null != result) {
+ searchResult.setTotal(Integer.parseInt(result));
+ }
+ } catch (NumberFormatException e) {
+ Log.w("cgeoBase.parseSearch: Failed to parse cache count");
+ }
+
+ if (thread != null && recaptchaChallenge != null) {
+ if (thread.getText() == null) {
+ thread.waitForUser();
+ }
+
+ recaptchaText = thread.getText();
+ }
+
+ if (cids.size() > 0 && (Settings.isPremiumMember() || showCaptcha) && (recaptchaChallenge == null || StringUtils.isNotBlank(recaptchaText))) {
+ Log.i("Trying to get .loc for " + cids.size() + " caches");
+
+ try {
+ // get coordinates for parsed caches
+ final Parameters params = new Parameters(
+ "__EVENTTARGET", "",
+ "__EVENTARGUMENT", "");
+ if (ArrayUtils.isNotEmpty(searchResult.viewstates)) {
+ params.put("__VIEWSTATE", searchResult.viewstates[0]);
+ if (searchResult.viewstates.length > 1) {
+ for (int i = 1; i < searchResult.viewstates.length; i++) {
+ params.put("__VIEWSTATE" + i, searchResult.viewstates[i]);
+ }
+ params.put("__VIEWSTATEFIELDCOUNT", String.valueOf(searchResult.viewstates.length));
+ }
+ }
+ for (String cid : cids) {
+ params.put("CID", cid);
+ }
+
+ if (recaptchaChallenge != null && StringUtils.isNotBlank(recaptchaText)) {
+ params.put("recaptcha_challenge_field", recaptchaChallenge);
+ params.put("recaptcha_response_field", recaptchaText);
+ }
+ params.put("ctl00$ContentBody$uxDownloadLoc", "Download Waypoints");
+
+ final String coordinates = Network.getResponseData(Network.postRequest("http://www.geocaching.com/seek/nearest.aspx", params), false);
+
+ if (StringUtils.isNotBlank(coordinates)) {
+ if (coordinates.contains("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;
+ }
+ }
+
+ LocParser.parseLoc(searchResult, coordinates);
+
+ } catch (Exception e) {
+ Log.e("cgBase.parseSearch.CIDs: " + e.toString());
+ }
+ }
+
+ // get direction images
+ if (Settings.getLoadDirImg()) {
+ final Set<cgCache> caches = searchResult.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB);
+ for (cgCache cache : caches) {
+ if (cache.getCoords() == null && StringUtils.isNotEmpty(cache.getDirectionImg())) {
+ DirectionImage.getDrawable(cache.getGeocode(), cache.getDirectionImg());
+ }
+ }
+ }
+
+ return searchResult;
+ }
+
+ public static SearchResult parseCache(final String page, final CancellableHandler handler) {
+ final SearchResult searchResult = parseCacheFromText(page, handler);
+ if (searchResult != null && !searchResult.getGeocodes().isEmpty()) {
+ final cgCache cache = searchResult.getFirstCacheFromResult(LoadFlags.LOAD_CACHE_OR_DB);
+ getExtraOnlineInfo(cache, page, handler);
+ cache.setUpdated(System.currentTimeMillis());
+ cache.setDetailedUpdate(cache.getUpdated());
+ cache.setDetailed(true);
+ if (CancellableHandler.isCancelled(handler)) {
+ return null;
+ }
+ // update progress message so user knows we're still working. Otherwise it will remain on whatever was set
+ // in getExtraOnlineInfo (which could be logs, gcvote, or elevation)
+ CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_render);
+ // save full detailed caches
+ cgeoapplication.getInstance().saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB));
+ }
+ return searchResult;
+ }
+
+ static SearchResult parseCacheFromText(final String page, final CancellableHandler handler) {
+ CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_details);
+
+ if (StringUtils.isBlank(page)) {
+ Log.e("cgeoBase.parseCache: No page given");
+ return null;
+ }
+
+ final SearchResult searchResult = new SearchResult();
+
+ if (page.contains("Cache is Unpublished") || page.contains("you cannot view this cache listing until it has been published")) {
+ searchResult.setError(StatusCode.UNPUBLISHED_CACHE);
+ return searchResult;
+ }
+
+ if (page.contains("Sorry, the owner of this listing has made it viewable to Premium Members only.")) {
+ searchResult.setError(StatusCode.PREMIUM_ONLY);
+ return searchResult;
+ }
+
+ if (page.contains("has chosen to make this cache listing visible to Premium Members only.")) {
+ searchResult.setError(StatusCode.PREMIUM_ONLY);
+ return searchResult;
+ }
+
+ final String cacheName = Html.fromHtml(BaseUtils.getMatch(page, GCConstants.PATTERN_NAME, true, "")).toString();
+ if ("An Error Has Occurred".equalsIgnoreCase(cacheName)) {
+ searchResult.setError(StatusCode.UNKNOWN_ERROR);
+ return searchResult;
+ }
+
+ final cgCache cache = new cgCache();
+ cache.setDisabled(page.contains("<li>This cache is temporarily unavailable."));
+
+ cache.setArchived(page.contains("<li>This cache has been archived,"));
+
+ cache.setPremiumMembersOnly(BaseUtils.matches(page, GCConstants.PATTERN_PREMIUMMEMBERS));
+
+ cache.setFavorite(BaseUtils.matches(page, GCConstants.PATTERN_FAVORITE));
+
+ // cache geocode
+ cache.setGeocode(BaseUtils.getMatch(page, GCConstants.PATTERN_GEOCODE, true, cache.getGeocode()));
+
+ // cache id
+ cache.setCacheId(BaseUtils.getMatch(page, GCConstants.PATTERN_CACHEID, true, cache.getCacheId()));
+
+ // cache guid
+ cache.setGuid(BaseUtils.getMatch(page, GCConstants.PATTERN_GUID, true, cache.getGuid()));
+
+ // name
+ cache.setName(cacheName);
+
+ // owner real name
+ cache.setOwnerReal(URLDecoder.decode(BaseUtils.getMatch(page, GCConstants.PATTERN_OWNERREAL, true, cache.getOwnerReal())));
+
+ final String username = Settings.getUsername();
+ if (cache.getOwnerReal() != null && username != null && cache.getOwnerReal().equalsIgnoreCase(username)) {
+ cache.setOwn(true);
+ }
+
+ String tableInside = page;
+
+ int pos = tableInside.indexOf("id=\"cacheDetails\"");
+ if (pos == -1) {
+ Log.e("cgeoBase.parseCache: ID \"cacheDetails\" not found on page");
+ return null;
+ }
+
+ tableInside = tableInside.substring(pos);
+
+ pos = tableInside.indexOf("<div class=\"CacheInformationTable\"");
+ if (pos == -1) {
+ Log.e("cgeoBase.parseCache: class \"CacheInformationTable\" not found on page");
+ return null;
+ }
+
+ tableInside = tableInside.substring(0, pos);
+
+ if (StringUtils.isNotBlank(tableInside)) {
+ // cache terrain
+ String result = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_TERRAIN, true, null);
+ if (result != null) {
+ cache.setTerrain(Float.parseFloat(StringUtils.replaceChars(result, '_', '.')));
+ }
+
+ // cache difficulty
+ result = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_DIFFICULTY, true, null);
+ if (result != null) {
+ cache.setDifficulty(Float.parseFloat(StringUtils.replaceChars(result, '_', '.')));
+ }
+
+ // owner
+ cache.setOwner(Html.fromHtml(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_OWNER, true, cache.getOwner())).toString());
+
+ // hidden
+ try {
+ String hiddenString = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDEN, true, null);
+ if (StringUtils.isNotBlank(hiddenString)) {
+ cache.setHidden(Login.parseGcCustomDate(hiddenString));
+ }
+ if (cache.getHiddenDate() == null) {
+ // event date
+ hiddenString = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDENEVENT, true, null);
+ if (StringUtils.isNotBlank(hiddenString)) {
+ cache.setHidden(Login.parseGcCustomDate(hiddenString));
+ }
+ }
+ } catch (ParseException e) {
+ // failed to parse cache hidden date
+ Log.w("cgeoBase.parseCache: Failed to parse cache hidden (event) date");
+ }
+
+ // favourite
+ cache.setFavoritePoints(Integer.parseInt(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_FAVORITECOUNT, true, "0")));
+
+ // cache size
+ cache.setSize(CacheSize.getById(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_SIZE, true, CacheSize.NOT_CHOSEN.id).toLowerCase()));
+ }
+
+ // cache found
+ cache.setFound(BaseUtils.matches(page, GCConstants.PATTERN_FOUND) || BaseUtils.matches(page, GCConstants.PATTERN_FOUND_ALTERNATIVE));
+
+ // cache type
+ cache.setType(CacheType.getByPattern(BaseUtils.getMatch(page, GCConstants.PATTERN_TYPE, true, cache.getType().id)));
+
+ // on watchlist
+ cache.setOnWatchlist(BaseUtils.matches(page, GCConstants.PATTERN_WATCHLIST));
+
+ // latitude and longitude. Can only be retrieved if user is logged in
+ cache.setLatlon(BaseUtils.getMatch(page, GCConstants.PATTERN_LATLON, true, cache.getLatlon()));
+ if (StringUtils.isNotEmpty(cache.getLatlon())) {
+ try {
+ cache.setCoords(new Geopoint(cache.getLatlon()));
+ cache.setReliableLatLon(true);
+ } catch (Geopoint.GeopointException e) {
+ Log.w("cgeoBase.parseCache: Failed to parse cache coordinates: " + e.toString());
+ }
+ }
+
+ // cache location
+ cache.setLocation(BaseUtils.getMatch(page, GCConstants.PATTERN_LOCATION, true, cache.getLocation()));
+
+ // cache hint
+ String result = BaseUtils.getMatch(page, GCConstants.PATTERN_HINT, false, null);
+ if (result != null) {
+ // replace linebreak and paragraph tags
+ String hint = GCConstants.PATTERN_LINEBREAK.matcher(result).replaceAll("\n");
+ if (hint != null) {
+ cache.setHint(StringUtils.replace(hint, "</p>", "").trim());
+ }
+ }
+
+ cache.checkFields();
+
+ // cache personal note
+ cache.setPersonalNote(BaseUtils.getMatch(page, GCConstants.PATTERN_PERSONALNOTE, true, cache.getPersonalNote()));
+
+ // cache short description
+ cache.setShortdesc(BaseUtils.getMatch(page, GCConstants.PATTERN_SHORTDESC, true, cache.getShortdesc()));
+
+ // cache description
+ cache.setDescription(BaseUtils.getMatch(page, GCConstants.PATTERN_DESC, true, ""));
+
+ // cache attributes
+ try {
+ final String attributesPre = BaseUtils.getMatch(page, GCConstants.PATTERN_ATTRIBUTES, true, null);
+ if (null != attributesPre) {
+ final Matcher matcherAttributesInside = GCConstants.PATTERN_ATTRIBUTESINSIDE.matcher(attributesPre);
+
+ while (matcherAttributesInside.find()) {
+ if (matcherAttributesInside.groupCount() > 1 && !matcherAttributesInside.group(2).equalsIgnoreCase("blank")) {
+ // by default, use the tooltip of the attribute
+ String attribute = matcherAttributesInside.group(2).toLowerCase();
+
+ // if the image name can be recognized, use the image name as attribute
+ String imageName = matcherAttributesInside.group(1).trim();
+ if (imageName.length() > 0) {
+ int start = imageName.lastIndexOf('/');
+ int end = imageName.lastIndexOf('.');
+ if (start >= 0 && end >= 0) {
+ attribute = imageName.substring(start + 1, end).replace('-', '_').toLowerCase();
+ }
+ }
+ cache.addAttribute(attribute);
+ }
+ }
+ }
+ } catch (Exception e) {
+ // failed to parse cache attributes
+ Log.w("cgeoBase.parseCache: Failed to parse cache attributes");
+ }
+
+ // cache spoilers
+ try {
+ final String spoilers = BaseUtils.getMatch(page, GCConstants.PATTERN_SPOILERS, false, null);
+ if (null != spoilers) {
+ if (CancellableHandler.isCancelled(handler)) {
+ return null;
+ }
+ CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_spoilers);
+
+ final Matcher matcherSpoilersInside = GCConstants.PATTERN_SPOILERSINSIDE.matcher(spoilers);
+
+ while (matcherSpoilersInside.find()) {
+ // the original spoiler URL (include .../display/... contains a low-resolution image
+ // if we shorten the URL we get the original-resolution image
+ String url = matcherSpoilersInside.group(1).replace("/display", "");
+
+ String title = null;
+ if (matcherSpoilersInside.group(2) != null) {
+ title = matcherSpoilersInside.group(2);
+ }
+ String description = null;
+ if (matcherSpoilersInside.group(3) != null) {
+ description = matcherSpoilersInside.group(3);
+ }
+ final cgImage spoiler = new cgImage(url, title, description);
+
+ if (cache.getSpoilers() == null) {
+ cache.setSpoilers(new ArrayList<cgImage>());
+ }
+ cache.getSpoilers().add(spoiler);
+ }
+ }
+ } catch (Exception e) {
+ // failed to parse cache spoilers
+ Log.w("cgeoBase.parseCache: Failed to parse cache spoilers");
+ }
+
+ // cache inventory
+ try {
+ cache.setInventoryItems(0);
+
+ final Matcher matcherInventory = GCConstants.PATTERN_INVENTORY.matcher(page);
+ if (matcherInventory.find()) {
+ if (cache.getInventory() == null) {
+ cache.setInventory(new ArrayList<cgTrackable>());
+ }
+
+ if (matcherInventory.groupCount() > 1) {
+ final String inventoryPre = matcherInventory.group(2);
+
+ if (StringUtils.isNotBlank(inventoryPre)) {
+ final Matcher matcherInventoryInside = GCConstants.PATTERN_INVENTORYINSIDE.matcher(inventoryPre);
+
+ while (matcherInventoryInside.find()) {
+ if (matcherInventoryInside.groupCount() > 0) {
+ final cgTrackable inventoryItem = new cgTrackable();
+ inventoryItem.setGuid(matcherInventoryInside.group(1));
+ inventoryItem.setName(matcherInventoryInside.group(2));
+
+ cache.getInventory().add(inventoryItem);
+ cache.setInventoryItems(cache.getInventoryItems() + 1);
+ }
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ // failed to parse cache inventory
+ Log.w("cgeoBase.parseCache: Failed to parse cache inventory (2)");
+ }
+
+ // cache logs counts
+ try
+ {
+ final String countlogs = BaseUtils.getMatch(page, GCConstants.PATTERN_COUNTLOGS, true, null);
+ if (null != countlogs) {
+ final Matcher matcherLog = GCConstants.PATTERN_COUNTLOG.matcher(countlogs);
+
+ while (matcherLog.find())
+ {
+ String typeStr = matcherLog.group(1);
+ String countStr = matcherLog.group(2).replaceAll("[.,]", "");
+
+ if (StringUtils.isNotBlank(typeStr)
+ && LogType.LOG_UNKNOWN != LogType.getByIconName(typeStr)
+ && StringUtils.isNotBlank(countStr)) {
+ cache.getLogCounts().put(LogType.getByIconName(typeStr), Integer.parseInt(countStr));
+ }
+ }
+ }
+ } catch (Exception e)
+ {
+ // failed to parse logs
+ Log.w("cgeoBase.parseCache: Failed to parse cache log count");
+ }
+
+ // add waypoint for original coordinates in case of user-modified listing-coordinates
+ try {
+ final String originalCoords = BaseUtils.getMatch(page, GCConstants.PATTERN_LATLON_ORIG, false, null);
+
+ if (null != originalCoords) {
+ final cgWaypoint waypoint = new cgWaypoint(cgeoapplication.getInstance().getString(R.string.cache_coordinates_original), WaypointType.WAYPOINT, false);
+ waypoint.setCoords(new Geopoint(originalCoords));
+ cache.addWaypoint(waypoint, false);
+ cache.setUserModifiedCoords(true);
+ }
+ } catch (Geopoint.GeopointException e) {
+ }
+
+ // waypoints
+ int wpBegin = 0;
+ int wpEnd = 0;
+
+ wpBegin = page.indexOf("<table class=\"Table\" id=\"ctl00_ContentBody_Waypoints\">");
+ if (wpBegin != -1) { // parse waypoints
+ if (CancellableHandler.isCancelled(handler)) {
+ return null;
+ }
+ CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_waypoints);
+
+ String wpList = page.substring(wpBegin);
+
+ wpEnd = wpList.indexOf("</p>");
+ if (wpEnd > -1 && wpEnd <= wpList.length()) {
+ wpList = wpList.substring(0, wpEnd);
+ }
+
+ if (!wpList.contains("No additional waypoints to display.")) {
+ wpEnd = wpList.indexOf("</table>");
+ wpList = wpList.substring(0, wpEnd);
+
+ wpBegin = wpList.indexOf("<tbody>");
+ wpEnd = wpList.indexOf("</tbody>");
+ if (wpBegin >= 0 && wpEnd >= 0 && wpEnd <= wpList.length()) {
+ wpList = wpList.substring(wpBegin + 7, wpEnd);
+ }
+
+ final String[] wpItems = wpList.split("<tr");
+
+ String[] wp;
+ for (int j = 1; j < wpItems.length; j++) {
+ wp = wpItems[j].split("<td");
+
+ // waypoint name
+ // res is null during the unit tests
+ final String name = BaseUtils.getMatch(wp[6], GCConstants.PATTERN_WPNAME, true, 1, cgeoapplication.getInstance().getString(R.string.waypoint), true);
+
+ // waypoint type
+ final String resulttype = BaseUtils.getMatch(wp[3], GCConstants.PATTERN_WPTYPE, null);
+
+ final cgWaypoint waypoint = new cgWaypoint(name, WaypointType.findById(resulttype), false);
+
+ // waypoint prefix
+ waypoint.setPrefix(BaseUtils.getMatch(wp[4], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, true, 2, waypoint.getPrefix(), false));
+
+ // waypoint lookup
+ waypoint.setLookup(BaseUtils.getMatch(wp[5], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, true, 2, waypoint.getLookup(), false));
+
+ // waypoint latitude and logitude
+ String latlon = Html.fromHtml(BaseUtils.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");
+ }
+
+ // waypoint note
+ waypoint.setNote(BaseUtils.getMatch(wp[3], GCConstants.PATTERN_WPNOTE, waypoint.getNote()));
+
+ cache.addWaypoint(waypoint, false);
+ }
+ }
+ }
+
+ cache.parseWaypointsFromNote();
+
+ // logs
+ cache.setLogs(loadLogsFromDetails(page, cache, false, true));
+
+ // last check for necessary cache conditions
+ if (StringUtils.isBlank(cache.getGeocode())) {
+ searchResult.setError(StatusCode.UNKNOWN_ERROR);
+ return searchResult;
+ }
+
+ searchResult.addCache(cache);
+ return searchResult;
+ }
+
+ public static SearchResult searchByNextPage(cgSearchThread thread, final SearchResult search, boolean showCaptcha) {
+ if (search == null) {
+ return search;
+ }
+ final String[] viewstates = search.getViewstates();
+
+ final String url = search.getUrl();
+
+ if (StringUtils.isBlank(url)) {
+ Log.e("cgeoBase.searchByNextPage: No url found");
+ return search;
+ }
+
+ if (Login.isEmpty(viewstates)) {
+ Log.e("cgeoBase.searchByNextPage: No viewstate given");
+ return search;
+ }
+
+ // As in the original code, remove the query string
+ final String uri = Uri.parse(url).buildUpon().query(null).build().toString();
+
+ final Parameters params = new Parameters(
+ "__EVENTTARGET", "ctl00$ContentBody$pgrBottom$ctl08",
+ "__EVENTARGUMENT", "");
+ Login.putViewstates(params, viewstates);
+
+ String page = Network.getResponseData(Network.postRequest(uri, params));
+ if (!Login.getLoginStatus(page)) {
+ final StatusCode loginState = Login.login();
+ if (loginState == StatusCode.NO_ERROR) {
+ page = Network.getResponseData(Network.postRequest(uri, params));
+ } else if (loginState == StatusCode.NO_LOGIN_INFO_STORED) {
+ Log.i("Working as guest.");
+ } else {
+ search.setError(loginState);
+ Log.e("cgeoBase.searchByNextPage: Can not log in geocaching");
+ return search;
+ }
+ }
+
+ if (StringUtils.isBlank(page)) {
+ Log.e("cgeoBase.searchByNextPage: No data from server");
+ return search;
+ }
+
+ final SearchResult searchResult = parseSearch(thread, url, page, showCaptcha);
+ if (searchResult == null || CollectionUtils.isEmpty(searchResult.getGeocodes())) {
+ Log.e("cgeoBase.searchByNextPage: No cache parsed");
+ return search;
+ }
+
+ // save to application
+ search.setError(searchResult.getError());
+ search.setViewstates(searchResult.viewstates);
+ for (String geocode : searchResult.getGeocodes()) {
+ search.addGeocode(geocode);
+ }
+ return search;
+ }
+
+ /**
+ * 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
+ * @return the original params if not null, maybe augmented with f=1, or a new Parameters with f=1 or null otherwise
+ */
+ public static Parameters addFToParams(final Parameters params, final boolean my, final boolean addF) {
+ if (!my && Settings.isExcludeMyCaches() && addF) {
+ if (params == null) {
+ return new Parameters("f", "1");
+ }
+ params.put("f", "1");
+ Log.i("Skipping caches found or hidden by user.");
+ }
+
+ return params;
+ }
+
+ /**
+ * @param thread
+ * thread to run the captcha if needed
+ * @param cacheType
+ * @param listId
+ * @param showCaptcha
+ * @param params
+ * the parameters to add to the request URI
+ * @return
+ */
+ private static SearchResult searchByAny(final cgSearchThread thread, final CacheType cacheType, final boolean my, final boolean showCaptcha, final Parameters params) {
+ insertCacheType(params, cacheType);
+
+ final String uri = "http://www.geocaching.com/seek/nearest.aspx";
+ final String fullUri = uri + "?" + addFToParams(params, false, true);
+ final String page = Login.getRequestLogged(uri, addFToParams(params, my, true));
+
+ if (StringUtils.isBlank(page)) {
+ Log.e("cgeoBase.searchByAny: No data from server");
+ return null;
+ }
+
+ final SearchResult searchResult = parseSearch(thread, fullUri, page, showCaptcha);
+ if (searchResult == null || CollectionUtils.isEmpty(searchResult.getGeocodes())) {
+ Log.e("cgeoBase.searchByAny: No cache parsed");
+ return searchResult;
+ }
+
+ final SearchResult search = searchResult.filterSearchResults(Settings.isExcludeDisabledCaches(), false, cacheType);
+
+ Login.getLoginStatus(page);
+
+ return search;
+ }
+
+ public static SearchResult searchByCoords(final cgSearchThread thread, final Geopoint coords, final CacheType cacheType, final boolean showCaptcha) {
+ final Parameters params = new Parameters("lat", Double.toString(coords.getLatitude()), "lng", Double.toString(coords.getLongitude()));
+ return searchByAny(thread, cacheType, false, showCaptcha, params);
+ }
+
+ public static SearchResult searchByKeyword(final cgSearchThread thread, final String keyword, final CacheType cacheType, final boolean showCaptcha) {
+ if (StringUtils.isBlank(keyword)) {
+ Log.e("cgeoBase.searchByKeyword: No keyword given");
+ return null;
+ }
+
+ final Parameters params = new Parameters("key", keyword);
+ return searchByAny(thread, cacheType, false, showCaptcha, params);
+ }
+
+ public static SearchResult searchByUsername(final cgSearchThread thread, final String userName, final CacheType cacheType, final boolean showCaptcha) {
+ if (StringUtils.isBlank(userName)) {
+ Log.e("cgeoBase.searchByUsername: No user name given");
+ return null;
+ }
+
+ final Parameters params = new Parameters("ul", userName);
+
+ boolean my = false;
+ if (userName.equalsIgnoreCase(Settings.getLogin().left)) {
+ my = true;
+ Log.i("cgBase.searchByUsername: Overriding users choice, downloading all caches.");
+ }
+
+ return searchByAny(thread, cacheType, my, showCaptcha, params);
+ }
+
+ public static SearchResult searchByOwner(final cgSearchThread thread, final String userName, final CacheType cacheType, final boolean showCaptcha) {
+ if (StringUtils.isBlank(userName)) {
+ Log.e("cgeoBase.searchByOwner: No user name given");
+ return null;
+ }
+
+ final Parameters params = new Parameters("u", userName);
+ return searchByAny(thread, cacheType, false, showCaptcha, params);
+ }
+
+ public static cgTrackable searchTrackable(final String geocode, final String guid, final String id) {
+ if (StringUtils.isBlank(geocode) && StringUtils.isBlank(guid) && StringUtils.isBlank(id)) {
+ Log.w("cgeoBase.searchTrackable: No geocode nor guid nor id given");
+ return null;
+ }
+
+ cgTrackable trackable = new cgTrackable();
+
+ final Parameters params = new Parameters();
+ if (StringUtils.isNotBlank(geocode)) {
+ params.put("tracker", geocode);
+ trackable.setGeocode(geocode);
+ } else if (StringUtils.isNotBlank(guid)) {
+ params.put("guid", guid);
+ } else if (StringUtils.isNotBlank(id)) {
+ params.put("id", id);
+ }
+
+ final String page = Login.getRequestLogged("http://www.geocaching.com/track/details.aspx", params);
+
+ if (StringUtils.isBlank(page)) {
+ Log.e("cgeoBase.searchTrackable: No data from server");
+ return trackable;
+ }
+
+ trackable = parseTrackable(page, cgeoapplication.getInstance(), geocode);
+ if (trackable == null) {
+ Log.e("cgeoBase.searchTrackable: No trackable parsed");
+ return null;
+ }
+
+ return trackable;
+ }
+
+ public static StatusCode postLog(final String geocode, final String cacheid, final String[] viewstates,
+ final LogType logType, final int year, final int month, final int day,
+ final String log, final List<TrackableLog> trackables) {
+ if (Login.isEmpty(viewstates)) {
+ Log.e("cgeoBase.postLog: No viewstate given");
+ return StatusCode.LOG_POST_ERROR;
+ }
+
+ if (StringUtils.isBlank(log)) {
+ Log.e("cgeoBase.postLog: No log text given");
+ return StatusCode.NO_LOG_TEXT;
+ }
+
+ // fix log (non-Latin characters converted to HTML entities)
+ final int logLen = log.length();
+ final StringBuilder logUpdated = new StringBuilder();
+
+ for (int i = 0; i < logLen; i++) {
+ char c = log.charAt(i);
+
+ if (c > 300) {
+ logUpdated.append("&#");
+ logUpdated.append(Integer.toString(c));
+ logUpdated.append(';');
+ } else {
+ logUpdated.append(c);
+ }
+ }
+
+ final String logInfo = logUpdated.toString().replace("\n", "\r\n").trim(); // windows' eol and remove leading and trailing whitespaces
+
+ if (trackables != null) {
+ Log.i("Trying to post log for cache #" + cacheid + " - action: " + logType + "; date: " + year + "." + month + "." + day + ", log: " + logInfo + "; trackables: " + trackables.size());
+ } else {
+ Log.i("Trying to post log for cache #" + cacheid + " - action: " + logType + "; date: " + year + "." + month + "." + day + ", log: " + logInfo + "; trackables: 0");
+ }
+
+ final Parameters params = new Parameters(
+ "__EVENTTARGET", "",
+ "__EVENTARGUMENT", "",
+ "__LASTFOCUS", "",
+ "ctl00$ContentBody$LogBookPanel1$ddLogType", Integer.toString(logType.id),
+ "ctl00$ContentBody$LogBookPanel1$DateTimeLogged", String.format("%02d", month) + "/" + String.format("%02d", day) + "/" + String.format("%04d", year),
+ "ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Month", Integer.toString(month),
+ "ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Day", Integer.toString(day),
+ "ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Year", Integer.toString(year),
+ "ctl00$ContentBody$LogBookPanel1$uxLogInfo", logInfo,
+ "ctl00$ContentBody$LogBookPanel1$LogButton", "Submit Log Entry",
+ "ctl00$ContentBody$uxVistOtherListingGC", "");
+ Login.putViewstates(params, viewstates);
+ if (trackables != null && !trackables.isEmpty()) { // we have some trackables to proceed
+ final StringBuilder hdnSelected = new StringBuilder();
+
+ for (final TrackableLog tb : trackables) {
+ if (tb.action != LogTypeTrackable.DO_NOTHING) {
+ hdnSelected.append(Integer.toString(tb.id));
+ hdnSelected.append(tb.action.action);
+ hdnSelected.append(',');
+ }
+ }
+
+ params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$hdnSelectedActions", hdnSelected.toString(), // selected trackables
+ "ctl00$ContentBody$LogBookPanel1$uxTrackables$hdnCurrentFilter", "");
+ }
+
+ final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/seek/log.aspx").encodedQuery("ID=" + cacheid).build().toString();
+ String page = Network.getResponseData(Network.postRequest(uri, params));
+ if (!Login.getLoginStatus(page)) {
+ final StatusCode loginState = Login.login();
+ if (loginState == StatusCode.NO_ERROR) {
+ page = Network.getResponseData(Network.postRequest(uri, params));
+ } else {
+ Log.e("cgeoBase.postLog: Can not log in geocaching (error: " + loginState + ")");
+ return loginState;
+ }
+ }
+
+ if (StringUtils.isBlank(page)) {
+ Log.e("cgeoBase.postLog: No data from server");
+ return StatusCode.NO_DATA_FROM_SERVER;
+ }
+
+ // maintenance, archived needs to be confirmed
+
+ final Matcher matcher = GCConstants.PATTERN_MAINTENANCE.matcher(page);
+
+ try {
+ if (matcher.find() && matcher.groupCount() > 0) {
+ final String[] viewstatesConfirm = Login.getViewstates(page);
+
+ if (Login.isEmpty(viewstatesConfirm)) {
+ Log.e("cgeoBase.postLog: No viewstate for confirm log");
+ return StatusCode.LOG_POST_ERROR;
+ }
+
+ params.clear();
+ Login.putViewstates(params, viewstatesConfirm);
+ params.put("__EVENTTARGET", "");
+ params.put("__EVENTARGUMENT", "");
+ params.put("__LASTFOCUS", "");
+ params.put("ctl00$ContentBody$LogBookPanel1$btnConfirm", "Yes");
+ params.put("ctl00$ContentBody$LogBookPanel1$uxLogInfo", logInfo);
+ params.put("ctl00$ContentBody$uxVistOtherListingGC", "");
+ if (trackables != null && !trackables.isEmpty()) { // we have some trackables to proceed
+ final StringBuilder hdnSelected = new StringBuilder();
+
+ for (TrackableLog tb : trackables) {
+ String ctl = null;
+ final String action = Integer.toString(tb.id) + tb.action.action;
+
+ if (tb.ctl < 10) {
+ ctl = "0" + Integer.toString(tb.ctl);
+ } else {
+ ctl = Integer.toString(tb.ctl);
+ }
+
+ params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$repTravelBugs$ctl" + ctl + "$ddlAction", action);
+ if (tb.action != LogTypeTrackable.DO_NOTHING) {
+ hdnSelected.append(action);
+ hdnSelected.append(',');
+ }
+ }
+
+ params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$hdnSelectedActions", hdnSelected.toString()); // selected trackables
+ params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$hdnCurrentFilter", "");
+ }
+
+ page = Network.getResponseData(Network.postRequest(uri, params));
+ }
+ } catch (Exception e) {
+ Log.e("cgeoBase.postLog.confim: " + e.toString());
+ }
+
+ try {
+
+ final Matcher matcherOk = GCConstants.PATTERN_OK1.matcher(page);
+ if (matcherOk.find()) {
+ Log.i("Log successfully posted to cache #" + cacheid);
+
+ if (geocode != null) {
+ cgeoapplication.getInstance().saveVisitDate(geocode);
+ }
+
+ Login.getLoginStatus(page);
+ // the log-successful-page contains still the old value
+ if (Login.getActualCachesFound() >= 0) {
+ Login.setActualCachesFound(Login.getActualCachesFound() + 1);
+ }
+ return StatusCode.NO_ERROR;
+ }
+ } catch (Exception e) {
+ Log.e("cgeoBase.postLog.check: " + e.toString());
+ }
+
+ Log.e("cgeoBase.postLog: Failed to post log because of unknown error");
+ return StatusCode.LOG_POST_ERROR;
+ }
+
+ public static StatusCode postLogTrackable(final String tbid, final String trackingCode, final String[] viewstates,
+ final LogType logType, final int year, final int month, final int day, final String log) {
+ if (Login.isEmpty(viewstates)) {
+ Log.e("cgeoBase.postLogTrackable: No viewstate given");
+ return StatusCode.LOG_POST_ERROR;
+ }
+
+ if (StringUtils.isBlank(log)) {
+ Log.e("cgeoBase.postLogTrackable: No log text given");
+ return StatusCode.NO_LOG_TEXT;
+ }
+
+ Log.i("Trying to post log for trackable #" + trackingCode + " - action: " + logType + "; date: " + year + "." + month + "." + day + ", log: " + log);
+
+ final String logInfo = log.replace("\n", "\r\n"); // windows' eol
+
+ final Calendar currentDate = Calendar.getInstance();
+ final Parameters params = new Parameters(
+ "__EVENTTARGET", "",
+ "__EVENTARGUMENT", "",
+ "__LASTFOCUS", "",
+ "ctl00$ContentBody$LogBookPanel1$ddLogType", Integer.toString(logType.id),
+ "ctl00$ContentBody$LogBookPanel1$tbCode", trackingCode);
+ Login.putViewstates(params, viewstates);
+ if (currentDate.get(Calendar.YEAR) == year && (currentDate.get(Calendar.MONTH) + 1) == month && currentDate.get(Calendar.DATE) == day) {
+ params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged", "");
+ } else {
+ params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged", Integer.toString(month) + "/" + Integer.toString(day) + "/" + Integer.toString(year));
+ }
+ params.put(
+ "ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Day", Integer.toString(day),
+ "ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Month", Integer.toString(month),
+ "ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Year", Integer.toString(year),
+ "ctl00$ContentBody$LogBookPanel1$uxLogInfo", logInfo,
+ "ctl00$ContentBody$LogBookPanel1$LogButton", "Submit Log Entry",
+ "ctl00$ContentBody$uxVistOtherListingGC", "");
+
+ final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/track/log.aspx").encodedQuery("wid=" + tbid).build().toString();
+ String page = Network.getResponseData(Network.postRequest(uri, params));
+ if (!Login.getLoginStatus(page)) {
+ final StatusCode loginState = Login.login();
+ if (loginState == StatusCode.NO_ERROR) {
+ page = Network.getResponseData(Network.postRequest(uri, params));
+ } else {
+ Log.e("cgeoBase.postLogTrackable: Can not log in geocaching (error: " + loginState + ")");
+ return loginState;
+ }
+ }
+
+ if (StringUtils.isBlank(page)) {
+ Log.e("cgeoBase.postLogTrackable: No data from server");
+ return StatusCode.NO_DATA_FROM_SERVER;
+ }
+
+ try {
+
+ final Matcher matcherOk = GCConstants.PATTERN_OK2.matcher(page);
+ if (matcherOk.find()) {
+ Log.i("Log successfully posted to trackable #" + trackingCode);
+ return StatusCode.NO_ERROR;
+ }
+ } catch (Exception e) {
+ Log.e("cgeoBase.postLogTrackable.check: " + e.toString());
+ }
+
+ Log.e("cgeoBase.postLogTrackable: Failed to post log because of unknown error");
+ return StatusCode.LOG_POST_ERROR;
+ }
+
+ /**
+ * Adds the cache to the watchlist of the user.
+ *
+ * @param cache
+ * the cache to add
+ * @return -1: error occured
+ */
+ public static int addToWatchlist(final cgCache cache) {
+ final String uri = "http://www.geocaching.com/my/watchlist.aspx?w=" + cache.getCacheId();
+ String page = Login.postRequestLogged(uri);
+
+ if (StringUtils.isBlank(page)) {
+ Log.e("cgBase.addToWatchlist: No data from server");
+ return -1; // error
+ }
+
+ boolean guidOnPage = cache.isGuidContainedInPage(page);
+ if (guidOnPage) {
+ Log.i("cgBase.addToWatchlist: cache is on watchlist");
+ cache.setOnWatchlist(true);
+ } else {
+ Log.e("cgBase.addToWatchlist: cache is not on watchlist");
+ }
+ return guidOnPage ? 1 : -1; // on watchlist (=added) / else: error
+ }
+
+ /**
+ * Removes the cache from the watchlist
+ *
+ * @param cache
+ * the cache to remove
+ * @return -1: error occured
+ */
+ public static int removeFromWatchlist(final cgCache cache) {
+ final String uri = "http://www.geocaching.com/my/watchlist.aspx?ds=1&action=rem&id=" + cache.getCacheId();
+ String page = Login.postRequestLogged(uri);
+
+ if (StringUtils.isBlank(page)) {
+ Log.e("cgBase.removeFromWatchlist: No data from server");
+ return -1; // error
+ }
+
+ // removing cache from list needs approval by hitting "Yes" button
+ final Parameters params = new Parameters(
+ "__EVENTTARGET", "",
+ "__EVENTARGUMENT", "",
+ "ctl00$ContentBody$btnYes", "Yes");
+ Login.transferViewstates(page, params);
+
+ page = Network.getResponseData(Network.postRequest(uri, params));
+ boolean guidOnPage = cache.isGuidContainedInPage(page);
+ if (!guidOnPage) {
+ Log.i("cgBase.removeFromWatchlist: cache removed from watchlist");
+ cache.setOnWatchlist(false);
+ } else {
+ Log.e("cgBase.removeFromWatchlist: cache not removed from watchlist");
+ }
+ return guidOnPage ? -1 : 0; // on watchlist (=error) / not on watchlist
+ }
+
+ /**
+ * Parse a trackable HTML description into a cgTrackable object
+ *
+ * @param page
+ * the HTML page to parse, already processed through {@link BaseUtils#replaceWhitespace}
+ * @param app
+ * if not null, the application to use to save the trackable
+ * @return the parsed trackable, or null if none could be parsed
+ */
+ public static cgTrackable parseTrackable(final String page, final cgeoapplication app, final String possibleTrackingcode) {
+ if (StringUtils.isBlank(page)) {
+ Log.e("cgeoBase.parseTrackable: No page given");
+ return null;
+ }
+
+ final cgTrackable trackable = new cgTrackable();
+
+ // trackable geocode
+ trackable.setGeocode(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GEOCODE, true, trackable.getGeocode()).toUpperCase());
+
+ // trackable id
+ trackable.setGuid(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GUID, true, trackable.getGuid()));
+
+ // trackable icon
+ trackable.setIconUrl(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_ICON, true, trackable.getIconUrl()));
+
+ // trackable name
+ trackable.setName(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_NAME, true, trackable.getName()));
+
+ // trackable type
+ if (StringUtils.isNotBlank(trackable.getName())) {
+ trackable.setType(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_TYPE, true, trackable.getType()));
+ }
+
+ // trackable owner name
+ try {
+ final Matcher matcherOwner = GCConstants.PATTERN_TRACKABLE_OWNER.matcher(page);
+ if (matcherOwner.find() && matcherOwner.groupCount() > 0) {
+ trackable.setOwnerGuid(matcherOwner.group(1));
+ trackable.setOwner(matcherOwner.group(2).trim());
+ }
+ } catch (Exception e) {
+ // failed to parse trackable owner name
+ Log.w("cgeoBase.parseTrackable: Failed to parse trackable owner name");
+ }
+
+ // trackable origin
+ trackable.setOrigin(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_ORIGIN, true, trackable.getOrigin()));
+
+ // trackable spotted
+ try {
+ final Matcher matcherSpottedCache = GCConstants.PATTERN_TRACKABLE_SPOTTEDCACHE.matcher(page);
+ if (matcherSpottedCache.find() && matcherSpottedCache.groupCount() > 0) {
+ trackable.setSpottedGuid(matcherSpottedCache.group(1));
+ trackable.setSpottedName(matcherSpottedCache.group(2).trim());
+ trackable.setSpottedType(cgTrackable.SPOTTED_CACHE);
+ }
+
+ final Matcher matcherSpottedUser = GCConstants.PATTERN_TRACKABLE_SPOTTEDUSER.matcher(page);
+ if (matcherSpottedUser.find() && matcherSpottedUser.groupCount() > 0) {
+ trackable.setSpottedGuid(matcherSpottedUser.group(1));
+ trackable.setSpottedName(matcherSpottedUser.group(2).trim());
+ trackable.setSpottedType(cgTrackable.SPOTTED_USER);
+ }
+
+ if (BaseUtils.matches(page, GCConstants.PATTERN_TRACKABLE_SPOTTEDUNKNOWN)) {
+ trackable.setSpottedType(cgTrackable.SPOTTED_UNKNOWN);
+ }
+
+ if (BaseUtils.matches(page, GCConstants.PATTERN_TRACKABLE_SPOTTEDOWNER)) {
+ trackable.setSpottedType(cgTrackable.SPOTTED_OWNER);
+ }
+ } catch (Exception e) {
+ // failed to parse trackable last known place
+ Log.w("cgeoBase.parseTrackable: Failed to parse trackable last known place");
+ }
+
+ // released date - can be missing on the page
+ try {
+ String releaseString = BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_RELEASES, false, null);
+ if (releaseString != null) {
+ trackable.setReleased(dateTbIn1.parse(releaseString));
+ if (trackable.getReleased() == null) {
+ trackable.setReleased(dateTbIn2.parse(releaseString));
+ }
+ }
+ } catch (ParseException e1) {
+ trackable.setReleased(null);
+ }
+
+ // trackable distance
+ try {
+ final String distance = BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_DISTANCE, false, null);
+ if (null != distance) {
+ trackable.setDistance(DistanceParser.parseDistance(distance, Settings.isUseMetricUnits()));
+ }
+ } catch (NumberFormatException e) {
+ throw e;
+ }
+
+ // trackable goal
+ trackable.setGoal(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GOAL, true, trackable.getGoal()));
+
+ // trackable details & image
+ try {
+ final Matcher matcherDetailsImage = GCConstants.PATTERN_TRACKABLE_DETAILSIMAGE.matcher(page);
+ if (matcherDetailsImage.find() && matcherDetailsImage.groupCount() > 0) {
+ final String image = StringUtils.trim(matcherDetailsImage.group(3));
+ final String details = StringUtils.trim(matcherDetailsImage.group(4));
+
+ if (StringUtils.isNotEmpty(image)) {
+ trackable.setImage(image);
+ }
+ if (StringUtils.isNotEmpty(details) && !StringUtils.equals(details, "No additional details available.")) {
+ trackable.setDetails(details);
+ }
+ }
+ } catch (Exception e) {
+ // failed to parse trackable details & image
+ Log.w("cgeoBase.parseTrackable: Failed to parse trackable details & image");
+ }
+
+ // trackable logs
+ try
+ {
+ final Matcher matcherLogs = GCConstants.PATTERN_TRACKABLE_LOG.matcher(page);
+ /*
+ * 1. Type (img)
+ * 2. Date
+ * 3. Author
+ * 4. Cache-GUID
+ * 5. <ignored> (strike-through property for ancien caches)
+ * 6. Cache-name
+ * 7. Logtext
+ */
+ while (matcherLogs.find())
+ {
+ final LogEntry logDone = new LogEntry();
+
+ logDone.type = LogType.getByIconName(matcherLogs.group(1));
+ logDone.author = Html.fromHtml(matcherLogs.group(3)).toString().trim();
+
+ try
+ {
+ logDone.date = Login.parseGcCustomDate(matcherLogs.group(2)).getTime();
+ } catch (ParseException e) {
+ }
+
+ logDone.log = matcherLogs.group(7).trim();
+
+ if (matcherLogs.group(4) != null && matcherLogs.group(6) != null)
+ {
+ logDone.cacheGuid = matcherLogs.group(4);
+ logDone.cacheName = matcherLogs.group(6);
+ }
+
+ // Apply the pattern for images in a trackable log entry against each full log (group(0))
+ final Matcher matcherLogImages = GCConstants.PATTERN_TRACKABLE_LOG_IMAGES.matcher(matcherLogs.group(0));
+ /*
+ * 1. Image URL
+ * 2. Image title
+ */
+ while (matcherLogImages.find())
+ {
+ final cgImage logImage = new cgImage(matcherLogImages.group(1), matcherLogImages.group(2));
+ logDone.addLogImage(logImage);
+ }
+
+ trackable.getLogs().add(logDone);
+ }
+ } catch (Exception e) {
+ // failed to parse logs
+ Log.w("cgeoBase.parseCache: Failed to parse cache logs" + e.toString());
+ }
+
+ // trackingcode
+ if (!StringUtils.equalsIgnoreCase(trackable.getGeocode(), possibleTrackingcode)) {
+ trackable.setTrackingcode(possibleTrackingcode);
+ }
+
+ if (app != null) {
+ app.saveTrackable(trackable);
+ }
+
+ return trackable;
+ }
+
+ /**
+ * Load logs from a cache details page.
+ *
+ * @param page
+ * the text of the details page
+ * @param cache
+ * the cache object to put the logs in
+ * @param friends
+ * retrieve friend logs
+ */
+ private static List<LogEntry> loadLogsFromDetails(final String page, final cgCache cache, final boolean friends, final boolean getDataFromPage) {
+ String rawResponse = null;
+
+ if (!getDataFromPage) {
+ final Matcher userTokenMatcher = GCConstants.PATTERN_USERTOKEN2.matcher(page);
+ if (!userTokenMatcher.find()) {
+ Log.e("cgBase.loadLogsFromDetails: unable to extract userToken");
+ return null;
+ }
+
+ final String userToken = userTokenMatcher.group(1);
+ final Parameters params = new Parameters(
+ "tkn", userToken,
+ "idx", "1",
+ "num", String.valueOf(GCConstants.NUMBER_OF_LOGS),
+ "decrypt", "true",
+ // "sp", Boolean.toString(personal), // personal logs
+ "sf", Boolean.toString(friends));
+
+ final HttpResponse response = Network.getRequest("http://www.geocaching.com/seek/geocache.logbook", params);
+ if (response == null) {
+ Log.e("cgBase.loadLogsFromDetails: cannot log logs, response is null");
+ return null;
+ }
+ final int statusCode = response.getStatusLine().getStatusCode();
+ if (statusCode != 200) {
+ Log.e("cgBase.loadLogsFromDetails: error " + statusCode + " when requesting log information");
+ return null;
+ }
+ rawResponse = Network.getResponseData(response);
+ if (rawResponse == null) {
+ Log.e("cgBase.loadLogsFromDetails: unable to read whole response");
+ return null;
+ }
+ } else {
+ // extract embedded JSON data from page
+ rawResponse = BaseUtils.getMatch(page, GCConstants.PATTERN_LOGBOOK, "");
+ }
+
+ List<LogEntry> logs = new ArrayList<LogEntry>();
+
+ try {
+ final JSONObject resp = new JSONObject(rawResponse);
+ if (!resp.getString("status").equals("success")) {
+ Log.e("cgBase.loadLogsFromDetails: status is " + resp.getString("status"));
+ return null;
+ }
+
+ final JSONArray data = resp.getJSONArray("data");
+
+ for (int index = 0; index < data.length(); index++) {
+ final JSONObject entry = data.getJSONObject(index);
+ final LogEntry logDone = new LogEntry();
+ logDone.friend = friends;
+
+ // FIXME: use the "LogType" field instead of the "LogTypeImage" one.
+ final String logIconNameExt = entry.optString("LogTypeImage", ".gif");
+ final String logIconName = logIconNameExt.substring(0, logIconNameExt.length() - 4);
+ logDone.type = LogType.getByIconName(logIconName);
+
+ try {
+ logDone.date = Login.parseGcCustomDate(entry.getString("Visited")).getTime();
+ } catch (ParseException e) {
+ Log.e("cgBase.loadLogsFromDetails: failed to parse log date.");
+ }
+
+ logDone.author = entry.getString("UserName");
+ logDone.found = entry.getInt("GeocacheFindCount");
+ logDone.log = entry.getString("LogText");
+
+ final JSONArray images = entry.getJSONArray("Images");
+ for (int i = 0; i < images.length(); i++) {
+ final JSONObject image = images.getJSONObject(i);
+ String url = "http://img.geocaching.com/cache/log/" + image.getString("FileName");
+ String title = image.getString("Name");
+ final cgImage logImage = new cgImage(url, title);
+ logDone.addLogImage(logImage);
+ }
+
+ logs.add(logDone);
+ }
+ } catch (JSONException e) {
+ // failed to parse logs
+ Log.w("cgBase.loadLogsFromDetails: Failed to parse cache logs", e);
+ }
+
+ return logs;
+ }
+
+ public static List<LogType> parseTypes(String page) {
+ if (StringUtils.isEmpty(page)) {
+ return null;
+ }
+
+ final List<LogType> types = new ArrayList<LogType>();
+
+ final Matcher typeBoxMatcher = GCConstants.PATTERN_TYPEBOX.matcher(page);
+ String typesText = null;
+ if (typeBoxMatcher.find()) {
+ if (typeBoxMatcher.groupCount() > 0) {
+ typesText = typeBoxMatcher.group(1);
+ }
+ }
+
+ if (typesText != null) {
+
+ final Matcher typeMatcher = GCConstants.PATTERN_TYPE2.matcher(typesText);
+ while (typeMatcher.find()) {
+ if (typeMatcher.groupCount() > 1) {
+ final int type = Integer.parseInt(typeMatcher.group(2));
+
+ if (type > 0) {
+ types.add(LogType.getById(type));
+ }
+ }
+ }
+ }
+
+ return types;
+ }
+
+ public static List<TrackableLog> parseTrackableLog(final String page) {
+ if (StringUtils.isEmpty(page)) {
+ return null;
+ }
+
+ String table = StringUtils.substringBetween(page, "<table id=\"tblTravelBugs\"", "</table>");
+
+ // if no trackables are currently in the account, the table is not available, so return an empty list instead of null
+ if (StringUtils.isBlank(table)) {
+ return Collections.emptyList();
+ }
+
+ table = StringUtils.substringBetween(table, "<tbody>", "</tbody>");
+ if (StringUtils.isBlank(table)) {
+ Log.e("cgeoBase.parseTrackableLog: tbody not found on page");
+ return null;
+ }
+
+ final List<TrackableLog> trackableLogs = new ArrayList<TrackableLog>();
+
+ final Matcher trackableMatcher = GCConstants.PATTERN_TRACKABLE.matcher(page);
+ while (trackableMatcher.find()) {
+ if (trackableMatcher.groupCount() > 0) {
+
+ final String trackCode = trackableMatcher.group(1);
+ final String name = Html.fromHtml(trackableMatcher.group(2)).toString();
+ try {
+ final Integer ctl = Integer.valueOf(trackableMatcher.group(3));
+ final Integer id = Integer.valueOf(trackableMatcher.group(5));
+ if (trackCode != null && name != null && ctl != null && id != null) {
+ final TrackableLog entry = new TrackableLog(trackCode, name, id.intValue(), ctl.intValue());
+
+ Log.i("Trackable in inventory (#" + entry.ctl + "/" + entry.id + "): " + entry.trackCode + " - " + entry.name);
+ trackableLogs.add(entry);
+ }
+ } catch (NumberFormatException e) {
+ Log.e("GCParser.parseTrackableLog", e);
+ }
+ }
+ }
+
+ return trackableLogs;
+ }
+
+ /**
+ * Insert the right cache type restriction in parameters
+ *
+ * @param params
+ * the parameters to insert the restriction into
+ * @param cacheType
+ * the type of cache, or null to include everything
+ */
+ static private void insertCacheType(final Parameters params, final CacheType cacheType) {
+ params.put("tx", cacheType.guid);
+ }
+
+ private static void getExtraOnlineInfo(final cgCache cache, final String page, final CancellableHandler handler) {
+ if (CancellableHandler.isCancelled(handler)) {
+ return;
+ }
+
+ //cache.setLogs(loadLogsFromDetails(page, cache, false));
+ if (Settings.isFriendLogsWanted()) {
+ CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_logs);
+ List<LogEntry> allLogs = cache.getLogs();
+ List<LogEntry> friendLogs = loadLogsFromDetails(page, cache, true, false);
+ if (friendLogs != null) {
+ for (LogEntry log : friendLogs) {
+ if (allLogs.contains(log)) {
+ allLogs.get(allLogs.indexOf(log)).friend = true;
+ } else {
+ allLogs.add(log);
+ }
+ }
+ }
+ }
+
+ if (Settings.isElevationWanted()) {
+ if (CancellableHandler.isCancelled(handler)) {
+ return;
+ }
+ CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_elevation);
+ if (cache.getCoords() != null) {
+ cache.setElevation(cache.getCoords().getElevation());
+ }
+ }
+
+ if (Settings.isRatingWanted()) {
+ if (CancellableHandler.isCancelled(handler)) {
+ return;
+ }
+ CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_gcvote);
+ final GCVoteRating rating = GCVote.getRating(cache.getGuid(), cache.getGeocode());
+ if (rating != null) {
+ cache.setRating(rating.getRating());
+ cache.setVotes(rating.getVotes());
+ cache.setMyVote(rating.getMyVote());
+ }
+ }
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/connector/gc/Login.java b/main/src/cgeo/geocaching/connector/gc/Login.java
new file mode 100644
index 0000000..1e614df
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/gc/Login.java
@@ -0,0 +1,434 @@
+package cgeo.geocaching.connector.gc;
+
+import cgeo.geocaching.R;
+import cgeo.geocaching.Settings;
+import cgeo.geocaching.cgeoapplication;
+import cgeo.geocaching.enumerations.StatusCode;
+import cgeo.geocaching.network.Cookies;
+import cgeo.geocaching.network.HtmlImage;
+import cgeo.geocaching.network.Network;
+import cgeo.geocaching.network.Parameters;
+import cgeo.geocaching.utils.BaseUtils;
+import cgeo.geocaching.utils.Log;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.http.HttpResponse;
+
+import android.content.Context;
+import android.graphics.drawable.BitmapDrawable;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Matcher;
+
+public abstract class Login {
+
+ private final static String ENGLISH = "English&#9660;";
+
+ // false = not logged in
+ private static boolean actualLoginStatus = false;
+ private static String actualUserName = "";
+ private static int actualCachesFound = -1;
+ private static String actualStatus = "";
+
+ private final static Map<String, SimpleDateFormat> gcCustomDateFormats;
+ static {
+ final String[] formats = new String[] {
+ "MM/dd/yyyy",
+ "yyyy-MM-dd",
+ "yyyy/MM/dd",
+ "dd/MMM/yyyy",
+ "MMM/dd/yyyy",
+ "dd MMM yy",
+ "dd/MM/yyyy"
+ };
+
+ Map<String, SimpleDateFormat> map = new HashMap<String, SimpleDateFormat>();
+
+ for (String format : formats) {
+ map.put(format, new SimpleDateFormat(format, Locale.ENGLISH));
+ }
+
+ gcCustomDateFormats = Collections.unmodifiableMap(map);
+ }
+
+ public static StatusCode login() {
+ final ImmutablePair<String, String> login = Settings.getLogin();
+
+ if (login == null || StringUtils.isEmpty(login.left) || StringUtils.isEmpty(login.right)) {
+ Login.setActualStatus(cgeoapplication.getInstance().getString(R.string.err_login));
+ Log.e("cgeoBase.login: No login information stored");
+ return StatusCode.NO_LOGIN_INFO_STORED;
+ }
+
+ Login.setActualStatus(cgeoapplication.getInstance().getString(R.string.init_login_popup_working));
+ HttpResponse loginResponse = Network.getRequest("https://www.geocaching.com/login/default.aspx");
+ String loginData = Network.getResponseData(loginResponse);
+ if (loginResponse != null && loginResponse.getStatusLine().getStatusCode() == 503 && BaseUtils.matches(loginData, GCConstants.PATTERN_MAINTENANCE)) {
+ return StatusCode.MAINTENANCE;
+ }
+
+ if (StringUtils.isBlank(loginData)) {
+ Log.e("cgeoBase.login: Failed to retrieve login page (1st)");
+ return StatusCode.CONNECTION_FAILED; // no loginpage
+ }
+
+ if (Login.getLoginStatus(loginData)) {
+ Log.i("Already logged in Geocaching.com as " + login.left);
+ Login.switchToEnglish(loginData);
+ return StatusCode.NO_ERROR; // logged in
+ }
+
+ Cookies.clearCookies();
+ Settings.setCookieStore(null);
+
+ final Parameters params = new Parameters(
+ "__EVENTTARGET", "",
+ "__EVENTARGUMENT", "",
+ "ctl00$ContentBody$tbUsername", login.left,
+ "ctl00$ContentBody$tbPassword", login.right,
+ "ctl00$ContentBody$cbRememberMe", "on",
+ "ctl00$ContentBody$btnSignIn", "Login");
+ final String[] viewstates = Login.getViewstates(loginData);
+ if (isEmpty(viewstates)) {
+ Log.e("cgeoBase.login: Failed to find viewstates");
+ return StatusCode.LOGIN_PARSE_ERROR; // no viewstates
+ }
+ Login.putViewstates(params, viewstates);
+
+ loginResponse = Network.postRequest("https://www.geocaching.com/login/default.aspx", params);
+ loginData = Network.getResponseData(loginResponse);
+
+ if (StringUtils.isNotBlank(loginData)) {
+ if (Login.getLoginStatus(loginData)) {
+ Log.i("Successfully logged in Geocaching.com as " + login.left);
+
+ Login.switchToEnglish(loginData);
+ Settings.setCookieStore(Cookies.dumpCookieStore());
+
+ return StatusCode.NO_ERROR; // logged in
+ } else {
+ if (loginData.contains("Your username/password combination does not match.")) {
+ Log.i("Failed to log in Geocaching.com as " + login.left + " because of wrong username/password");
+ return StatusCode.WRONG_LOGIN_DATA; // wrong login
+ } else {
+ Log.i("Failed to log in Geocaching.com as " + login.left + " for some unknown reason");
+ return StatusCode.UNKNOWN_ERROR; // can't login
+ }
+ }
+ } else {
+ Log.e("cgeoBase.login: Failed to retrieve login page (2nd)");
+ // FIXME: should it be CONNECTION_FAILED to match the first attempt?
+ return StatusCode.COMMUNICATION_ERROR; // no login page
+ }
+ }
+
+ public static StatusCode logout() {
+ HttpResponse logoutResponse = Network.getRequest("https://www.geocaching.com/login/default.aspx?RESET=Y&redir=http%3a%2f%2fwww.geocaching.com%2fdefault.aspx%3f");
+ String logoutData = Network.getResponseData(logoutResponse);
+ if (logoutResponse != null && logoutResponse.getStatusLine().getStatusCode() == 503 && BaseUtils.matches(logoutData, GCConstants.PATTERN_MAINTENANCE)) {
+ return StatusCode.MAINTENANCE;
+ }
+
+ Cookies.clearCookies();
+ Settings.setCookieStore(null);
+ return StatusCode.NO_ERROR;
+ }
+
+ public static void setActualCachesFound(final int found) {
+ actualCachesFound = found;
+ }
+
+ public static String getActualStatus() {
+ return actualStatus;
+ }
+
+ public static void setActualStatus(final String status) {
+ actualStatus = status;
+ }
+
+ public static boolean isActualLoginStatus() {
+ return actualLoginStatus;
+ }
+
+ public static void setActualLoginStatus(boolean loginStatus) {
+ actualLoginStatus = loginStatus;
+ }
+
+ public static String getActualUserName() {
+ return actualUserName;
+ }
+
+ public static void setActualUserName(String userName) {
+ actualUserName = userName;
+ }
+
+ public static int getActualCachesFound() {
+ return actualCachesFound;
+ }
+
+ /**
+ * Check if the user has been logged in when he retrieved the data.
+ *
+ * @param page
+ * @return <code>true</code> if user is logged in, <code>false</code> otherwise
+ */
+ public static boolean getLoginStatus(final String page) {
+ if (StringUtils.isBlank(page)) {
+ Log.e("cgeoBase.checkLogin: No page given");
+ return false;
+ }
+
+ setActualStatus(cgeoapplication.getInstance().getString(R.string.init_login_popup_ok));
+
+ // on every page except login page
+ setActualLoginStatus(BaseUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME));
+ if (isActualLoginStatus()) {
+ setActualUserName(BaseUtils.getMatch(page, GCConstants.PATTERN_LOGIN_NAME, true, "???"));
+ setActualCachesFound(Integer.parseInt(BaseUtils.getMatch(page, GCConstants.PATTERN_CACHES_FOUND, true, "0").replaceAll("[,.]", "")));
+ return true;
+ }
+
+ // login page
+ setActualLoginStatus(BaseUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME_LOGIN_PAGE));
+ if (isActualLoginStatus()) {
+ setActualUserName(Settings.getUsername());
+ // number of caches found is not part of this page
+ return true;
+ }
+
+ setActualStatus(cgeoapplication.getInstance().getString(R.string.init_login_popup_failed));
+ return false;
+ }
+
+ private static void switchToEnglish(String previousPage) {
+ if (previousPage != null && previousPage.indexOf(ENGLISH) >= 0) {
+ Log.i("Geocaching.com language already set to English");
+ // get find count
+ getLoginStatus(Network.getResponseData(Network.getRequest("http://www.geocaching.com/email/")));
+ } else {
+ final String page = Network.getResponseData(Network.getRequest("http://www.geocaching.com/default.aspx"));
+ getLoginStatus(page);
+ if (page == null) {
+ Log.e("Failed to read viewstates to set geocaching.com language");
+ }
+ final Parameters params = new Parameters(
+ "__EVENTTARGET", "ctl00$uxLocaleList$uxLocaleList$ctl00$uxLocaleItem", // switch to english
+ "__EVENTARGUMENT", "");
+ Login.transferViewstates(page, params);
+ final HttpResponse response = Network.postRequest("http://www.geocaching.com/default.aspx", params);
+ if (!Network.isSuccess(response)) {
+ Log.e("Failed to set geocaching.com language to English");
+ }
+ }
+ }
+
+ public static BitmapDrawable downloadAvatarAndGetMemberStatus(final Context context) {
+ try {
+ final String profile = BaseUtils.replaceWhitespace(Network.getResponseData(Network.getRequest("http://www.geocaching.com/my/")));
+
+ Settings.setMemberStatus(BaseUtils.getMatch(profile, GCConstants.PATTERN_MEMBER_STATUS, true, null));
+
+ setActualCachesFound(Integer.parseInt(BaseUtils.getMatch(profile, GCConstants.PATTERN_CACHES_FOUND, true, "-1").replaceAll("[,.]", "")));
+
+ final String avatarURL = BaseUtils.getMatch(profile, GCConstants.PATTERN_AVATAR_IMAGE_PROFILE_PAGE, false, null);
+ if (null != avatarURL) {
+ final HtmlImage imgGetter = new HtmlImage(context, "", false, 0, false);
+ return imgGetter.getDrawable(avatarURL);
+ }
+ // No match? There may be no avatar set by user.
+ Log.d("No avatar set for user");
+ } catch (Exception e) {
+ Log.w("Error when retrieving user avatar", e);
+ }
+ return null;
+ }
+
+ /**
+ * Detect user date settings on geocaching.com
+ */
+ public static void detectGcCustomDate() {
+
+ final String result = Network.getResponseData(Network.getRequest("http://www.geocaching.com/account/ManagePreferences.aspx"));
+
+ if (null == result) {
+ Log.w("cgeoBase.detectGcCustomDate: result is null");
+ return;
+ }
+
+ String customDate = BaseUtils.getMatch(result, GCConstants.PATTERN_CUSTOMDATE, true, null);
+ if (null != customDate) {
+ Settings.setGcCustomDate(customDate);
+ }
+ }
+
+ public static Date parseGcCustomDate(final String input, final String format) throws ParseException {
+ if (StringUtils.isBlank(input)) {
+ throw new ParseException("Input is null", 0);
+ }
+
+ final String trimmed = input.trim();
+
+ if (gcCustomDateFormats.containsKey(format)) {
+ try {
+ return gcCustomDateFormats.get(format).parse(trimmed);
+ } catch (ParseException e) {
+ }
+ }
+
+ for (SimpleDateFormat sdf : gcCustomDateFormats.values()) {
+ try {
+ return sdf.parse(trimmed);
+ } catch (ParseException e) {
+ }
+ }
+
+ throw new ParseException("No matching pattern", 0);
+ }
+
+ public static Date parseGcCustomDate(final String input) throws ParseException {
+ return parseGcCustomDate(input, Settings.getGcCustomDate());
+ }
+
+ /**
+ * checks if an Array of Strings is empty or not. Empty means:
+ * - Array is null
+ * - or all elements are null or empty strings
+ */
+ public static boolean isEmpty(String[] a) {
+ if (a == null) {
+ return true;
+ }
+
+ for (String s : a) {
+ if (StringUtils.isNotEmpty(s)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * read all viewstates from page
+ *
+ * @return String[] with all view states
+ */
+ public static String[] getViewstates(String page) {
+ // Get the number of viewstates.
+ // If there is only one viewstate, __VIEWSTATEFIELDCOUNT is not present
+
+ if (page == null) { // no network access
+ return null;
+ }
+
+ int count = 1;
+ final Matcher matcherViewstateCount = GCConstants.PATTERN_VIEWSTATEFIELDCOUNT.matcher(page);
+ if (matcherViewstateCount.find()) {
+ try {
+ count = Integer.parseInt(matcherViewstateCount.group(1));
+ } catch (NumberFormatException e) {
+ Log.e("getViewStates", e);
+ }
+ }
+
+ String[] viewstates = new String[count];
+
+ // Get the viewstates
+ int no;
+ final Matcher matcherViewstates = GCConstants.PATTERN_VIEWSTATES.matcher(page);
+ while (matcherViewstates.find()) {
+ String sno = matcherViewstates.group(1); // number of viewstate
+ if (sno.length() == 0) {
+ no = 0;
+ }
+ else {
+ try {
+ no = Integer.parseInt(sno);
+ } catch (NumberFormatException e) {
+ Log.e("getViewStates", e);
+ no = 0;
+ }
+ }
+ viewstates[no] = matcherViewstates.group(2);
+ }
+
+ if (viewstates.length != 1 || viewstates[0] != null) {
+ return viewstates;
+ }
+ // no viewstates were present
+ return null;
+ }
+
+ /**
+ * put viewstates into request parameters
+ */
+ public static void putViewstates(final Parameters params, final String[] viewstates) {
+ if (ArrayUtils.isEmpty(viewstates)) {
+ return;
+ }
+ params.put("__VIEWSTATE", viewstates[0]);
+ if (viewstates.length > 1) {
+ for (int i = 1; i < viewstates.length; i++) {
+ params.put("__VIEWSTATE" + i, viewstates[i]);
+ }
+ params.put("__VIEWSTATEFIELDCOUNT", String.valueOf(viewstates.length));
+ }
+ }
+
+ /**
+ * transfers the viewstates variables from a page (response) to parameters
+ * (next request)
+ */
+ public static void transferViewstates(final String page, final Parameters params) {
+ putViewstates(params, getViewstates(page));
+ }
+
+ /**
+ * POST HTTP request. Do the request a second time if the user is not logged in
+ *
+ * @param uri
+ * @return
+ */
+ public static String postRequestLogged(final String uri) {
+ HttpResponse response = Network.postRequest(uri, null);
+ String data = Network.getResponseData(response);
+
+ if (!getLoginStatus(data)) {
+ if (login() == StatusCode.NO_ERROR) {
+ response = Network.postRequest(uri, null);
+ data = Network.getResponseData(response);
+ } else {
+ Log.i("Working as guest.");
+ }
+ }
+ return data;
+ }
+
+ /**
+ * GET HTTP request. Do the request a second time if the user is not logged in
+ *
+ * @param uri
+ * @param params
+ * @return
+ */
+ public static String getRequestLogged(final String uri, final Parameters params) {
+ final String data = Network.getResponseData(Network.getRequest(uri, params));
+
+ if (!getLoginStatus(data)) {
+ if (login() == StatusCode.NO_ERROR) {
+ return Network.getResponseData(Network.getRequest(uri, params));
+ } else {
+ Log.i("Working as guest.");
+ }
+ }
+ return data;
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/connector/gc/Tile.java b/main/src/cgeo/geocaching/connector/gc/Tile.java
index 2a146cc..289c9db 100644
--- a/main/src/cgeo/geocaching/connector/gc/Tile.java
+++ b/main/src/cgeo/geocaching/connector/gc/Tile.java
@@ -1,12 +1,12 @@
package cgeo.geocaching.connector.gc;
-import cgeo.geocaching.Settings;
import cgeo.geocaching.geopoint.Geopoint;
+import cgeo.geocaching.geopoint.Viewport;
import cgeo.geocaching.network.Network;
+import cgeo.geocaching.network.Parameters;
import cgeo.geocaching.utils.Log;
import org.apache.http.HttpResponse;
-import org.apache.http.client.methods.HttpGet;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -45,22 +45,16 @@ public class Tile {
private final int tileX;
private final int tileY;
private final int zoomlevel;
+ private final Viewport viewPort;
public Tile(Geopoint origin, int zoomlevel) {
- assert zoomlevel >= ZOOMLEVEL_MIN && zoomlevel <= ZOOMLEVEL_MAX : "zoomlevel out of range";
this.zoomlevel = Math.max(Math.min(zoomlevel, ZOOMLEVEL_MAX), ZOOMLEVEL_MIN);
tileX = calcX(origin);
tileY = calcY(origin);
- }
-
- public Tile(int tileX, int tileY, int zoomlevel) {
- assert zoomlevel >= ZOOMLEVEL_MIN && zoomlevel <= ZOOMLEVEL_MAX : "zoomlevel out of range";
- this.zoomlevel = zoomlevel;
- this.tileX = tileX;
- this.tileY = tileY;
+ viewPort = new Viewport(getCoord(new UTFGridPosition(0, 0)), getCoord(new UTFGridPosition(63, 63)));
}
public int getZoomlevel() {
@@ -161,7 +155,7 @@ public class Tile {
public static int calcZoomLat(final Geopoint bottom, final Geopoint top) {
int zoom = (int) Math.ceil(
- Math.log(2 * Math.PI /
+ Math.log(2.0 * Math.PI /
Math.abs(
asinh(tanGrad(bottom.getLatitude()))
- asinh(tanGrad(top.getLatitude()))
@@ -213,26 +207,23 @@ public class Tile {
}
/** Request JSON informations for a tile */
- public static String requestMapInfo(final String url, final String referer) {
- final HttpGet request = new HttpGet(url);
- request.addHeader("Accept", "application/json, text/javascript, */*; q=0.01");
- request.addHeader("Referer", referer);
- request.addHeader("X-Requested-With", "XMLHttpRequest");
- return Network.getResponseData(Network.request(request), false);
+ public static String requestMapInfo(final String url, final Parameters params, final String referer) {
+ return Network.getResponseData(Network.getRequest(url, params, new Parameters("Referer", referer)));
}
/** Request .png image for a tile. */
- public static Bitmap requestMapTile(final String url, final String referer) {
- final HttpGet request = new HttpGet(url);
- request.addHeader("Accept", "image/png,image/*;q=0.8,*/*;q=0.5");
- request.addHeader("Referer", referer);
- request.addHeader("X-Requested-With", "XMLHttpRequest");
- final HttpResponse response = Network.request(request);
+ public static Bitmap requestMapTile(final String url, final Parameters params, final String referer) {
+ final HttpResponse response = Network.getRequest(url, params, new Parameters("Referer", referer));
try {
return response != null ? BitmapFactory.decodeStream(response.getEntity().getContent()) : null;
} catch (IOException e) {
- Log.e(Settings.tag, "cgBase.requestMapTile() " + e.getMessage());
+ Log.e("cgBase.requestMapTile() " + e.getMessage());
}
return null;
}
+
+ public boolean containsPoint(Geopoint coords) {
+
+ return viewPort.contains(coords);
+ }
}