aboutsummaryrefslogtreecommitdiffstats
path: root/main/src/cgeo/geocaching/connector
diff options
context:
space:
mode:
Diffstat (limited to 'main/src/cgeo/geocaching/connector')
-rw-r--r--main/src/cgeo/geocaching/connector/AbstractConnector.java50
-rw-r--r--main/src/cgeo/geocaching/connector/ConnectorFactory.java53
-rw-r--r--main/src/cgeo/geocaching/connector/IConnector.java53
-rw-r--r--main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java1
-rw-r--r--main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java2
-rw-r--r--main/src/cgeo/geocaching/connector/gc/AbstractSearchThread.java2
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCConnector.java85
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCConstants.java12
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCMap.java27
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCParser.java226
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCSmiliesProvider.java2
-rw-r--r--main/src/cgeo/geocaching/connector/gc/IconDecoder.java609
-rw-r--r--main/src/cgeo/geocaching/connector/gc/Login.java13
-rw-r--r--main/src/cgeo/geocaching/connector/gc/Tile.java5
-rw-r--r--main/src/cgeo/geocaching/connector/gc/UTFGrid.java2
-rw-r--r--main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java5
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java514
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCApiConnector.java6
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCConnector.java6
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java52
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCXMLClient.java110
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OkapiClient.java8
22 files changed, 1584 insertions, 259 deletions
diff --git a/main/src/cgeo/geocaching/connector/AbstractConnector.java b/main/src/cgeo/geocaching/connector/AbstractConnector.java
index 112d6cf..9604b5f 100644
--- a/main/src/cgeo/geocaching/connector/AbstractConnector.java
+++ b/main/src/cgeo/geocaching/connector/AbstractConnector.java
@@ -1,8 +1,8 @@
package cgeo.geocaching.connector;
-import cgeo.geocaching.SearchResult;
import cgeo.geocaching.cgCache;
-import cgeo.geocaching.geopoint.Viewport;
+import cgeo.geocaching.enumerations.CacheRealm;
+import cgeo.geocaching.geopoint.Geopoint;
import org.apache.commons.lang3.StringUtils;
@@ -19,6 +19,31 @@ public abstract class AbstractConnector implements IConnector {
}
@Override
+ public boolean supportsOwnCoordinates() {
+ return false;
+ }
+
+ /**
+ * Uploading modified coordinates to website
+ *
+ * @param cache
+ * @param wpt
+ * @return success
+ */
+ @Override
+ public boolean uploadModifiedCoordinates(cgCache cache, Geopoint wpt) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@link IConnector}
+ */
+ @Override
+ public boolean deleteModifiedCoordinates(cgCache cache) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean supportsFavoritePoints() {
return false;
}
@@ -38,11 +63,6 @@ public abstract class AbstractConnector implements IConnector {
return false;
}
- @Override
- public SearchResult searchByViewport(Viewport viewport, String tokens[]) {
- return null;
- }
-
protected static boolean isNumericId(final String string) {
try {
return Integer.parseInt(string) > 0;
@@ -81,4 +101,20 @@ public abstract class AbstractConnector implements IConnector {
}
abstract protected String getCacheUrlPrefix();
+
+ /**
+ * {@link IConnector}
+ */
+ @Override
+ public CacheRealm getCacheRealm() {
+ return CacheRealm.OTHER;
+ }
+
+ /**
+ * {@link IConnector}
+ */
+ @Override
+ public boolean isActivated() {
+ return false;
+ }
}
diff --git a/main/src/cgeo/geocaching/connector/ConnectorFactory.java b/main/src/cgeo/geocaching/connector/ConnectorFactory.java
index bc4dcc0..4802c3e 100644
--- a/main/src/cgeo/geocaching/connector/ConnectorFactory.java
+++ b/main/src/cgeo/geocaching/connector/ConnectorFactory.java
@@ -3,25 +3,31 @@ package cgeo.geocaching.connector;
import cgeo.geocaching.ICache;
import cgeo.geocaching.SearchResult;
import cgeo.geocaching.cgTrackable;
+import cgeo.geocaching.connector.capability.ISearchByCenter;
+import cgeo.geocaching.connector.capability.ISearchByViewPort;
import cgeo.geocaching.connector.gc.GCConnector;
import cgeo.geocaching.connector.oc.OCApiConnector;
import cgeo.geocaching.connector.oc.OCConnector;
+import cgeo.geocaching.connector.oc.OCXMLApiConnector;
import cgeo.geocaching.connector.ox.OXConnector;
import cgeo.geocaching.geopoint.Viewport;
import org.apache.commons.lang3.StringUtils;
+import java.util.ArrayList;
+import java.util.List;
+
public final class ConnectorFactory {
private static final UnknownConnector UNKNOWN_CONNECTOR = new UnknownConnector();
private static final IConnector[] connectors = new IConnector[] {
GCConnector.getInstance(),
- new OCConnector("OpenCaching.DE", "www.opencaching.de", "OC"),
+ new OCXMLApiConnector("OpenCaching.DE", "www.opencaching.de", "OC"),
new OCConnector("OpenCaching.CZ", "www.opencaching.cz", "OZ"),
new OCApiConnector("OpenCaching.CO.UK", "www.opencaching.org.uk", "OK", "arU4okouc4GEjMniE2fq"),
new OCConnector("OpenCaching.ES", "www.opencachingspain.es", "OC"),
new OCConnector("OpenCaching.IT", "www.opencaching.it", "OC"),
new OCConnector("OpenCaching.JP", "www.opencaching.jp", "OJ"),
- new OCConnector("OpenCaching.NO/SE", "www.opencaching.no", "OS"),
+ new OCConnector("OpenCaching.NO/SE", "www.opencaching.se", "OS"),
new OCApiConnector("OpenCaching.NL", "www.opencaching.nl", "OB", "PdzU8jzIlcfMADXaYN8j"),
new OCApiConnector("OpenCaching.PL", "www.opencaching.pl", "OP", "GkxM47WkUkLQXXsZ9qSh"),
new OCApiConnector("OpenCaching.US", "www.opencaching.us", "OU", "pTsYAYSXFcfcRQnYE6uA"),
@@ -31,10 +37,37 @@ public final class ConnectorFactory {
UNKNOWN_CONNECTOR // the unknown connector MUST be the last one
};
+ private static final ISearchByViewPort[] searchByViewPortConns;
+
+ private static final ISearchByCenter[] searchByCenterConns;
+
+ static {
+ List<ISearchByViewPort> vpConns = new ArrayList<ISearchByViewPort>();
+ for (IConnector conn : connectors) {
+ if (conn instanceof ISearchByViewPort) {
+ vpConns.add((ISearchByViewPort) conn);
+ }
+ }
+ searchByViewPortConns = vpConns.toArray(new ISearchByViewPort[] {});
+
+ List<ISearchByCenter> centerConns = new ArrayList<ISearchByCenter>();
+ for (IConnector conn : connectors) {
+ // GCConnector is handled specially, omit it here!
+ if (conn instanceof ISearchByCenter && !(conn instanceof GCConnector)) {
+ centerConns.add((ISearchByCenter) conn);
+ }
+ }
+ searchByCenterConns = centerConns.toArray(new ISearchByCenter[] {});
+ }
+
public static IConnector[] getConnectors() {
return connectors;
}
+ public static ISearchByCenter[] getSearchByCenterConnectors() {
+ return searchByCenterConns;
+ }
+
public static boolean canHandle(final String geocode) {
if (isInvalidGeocode(geocode)) {
return false;
@@ -74,11 +107,19 @@ public final class ConnectorFactory {
return StringUtils.isBlank(geocode) || !Character.isLetterOrDigit(geocode.charAt(0));
}
- /** @see IConnector#searchByViewport */
+ /** @see ISearchByViewPort#searchByViewport */
public static SearchResult searchByViewport(final Viewport viewport, final String[] tokens) {
- // We have only connector capable of doing a 'searchByViewport()'
- // If there is a second connector the information has to be collected from all collectors
- return GCConnector.getInstance().searchByViewport(viewport, tokens);
+
+ SearchResult result = new SearchResult();
+ for (ISearchByViewPort vpconn : searchByViewPortConns) {
+ if (vpconn.isActivated()) {
+ SearchResult temp = vpconn.searchByViewport(viewport, tokens);
+ if (temp != null) {
+ result.addGeocodes(temp.getGeocodes());
+ }
+ }
+ }
+ return result;
}
public static String getGeocodeFromURL(final String url) {
diff --git a/main/src/cgeo/geocaching/connector/IConnector.java b/main/src/cgeo/geocaching/connector/IConnector.java
index d6ac2ff..2944bed 100644
--- a/main/src/cgeo/geocaching/connector/IConnector.java
+++ b/main/src/cgeo/geocaching/connector/IConnector.java
@@ -1,8 +1,8 @@
package cgeo.geocaching.connector;
-import cgeo.geocaching.SearchResult;
import cgeo.geocaching.cgCache;
-import cgeo.geocaching.geopoint.Viewport;
+import cgeo.geocaching.enumerations.CacheRealm;
+import cgeo.geocaching.geopoint.Geopoint;
public interface IConnector {
/**
@@ -72,15 +72,6 @@ public interface IConnector {
public boolean supportsUserActions();
/**
- * Search caches by viewport.
- *
- * @param viewport
- * @param tokens
- * @return
- */
- public SearchResult searchByViewport(final Viewport viewport, final String[] tokens);
-
- /**
* return true if this is a ZIP file containing a GPX file
*
* @param fileName
@@ -106,10 +97,48 @@ public interface IConnector {
/**
* extract a geocode from the given URL, if this connector can handle that URL somehow
- *
+ *
* @param url
* @return
*/
public String getGeocodeFromUrl(final String url);
+ /**
+ * enable/disable uploading modified coordinates to website
+ *
+ * @return true, when uploading is possible
+ */
+ public boolean supportsOwnCoordinates();
+
+ /**
+ * Uploading modified coordinates to website
+ *
+ * @param cache
+ * @param wpt
+ * @return success
+ */
+ public boolean uploadModifiedCoordinates(cgCache cache, Geopoint wpt);
+
+ /**
+ * Reseting of modified coordinates on website to details
+ *
+ * @param cache
+ * @return success
+ */
+ public boolean deleteModifiedCoordinates(cgCache cache);
+
+ /**
+ * The CacheRealm this cache belongs to
+ *
+ * @return
+ */
+ public CacheRealm getCacheRealm();
+
+ /**
+ * Return true if this connector is activated for online
+ * interaction (download details, do searches, ...)
+ *
+ * @return
+ */
+ public boolean isActivated();
}
diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java b/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java
index 62645c2..3fdd61f 100644
--- a/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java
+++ b/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java
@@ -10,4 +10,5 @@ import cgeo.geocaching.geopoint.Geopoint;
public interface ISearchByCenter {
public SearchResult searchByCenter(final Geopoint center);
+ public boolean isActivated();
}
diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java b/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java
index 316cf00..f1bd2ce 100644
--- a/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java
+++ b/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java
@@ -5,4 +5,6 @@ import cgeo.geocaching.geopoint.Viewport;
public interface ISearchByViewPort {
public SearchResult searchByViewport(final Viewport viewport, final String[] tokens);
+
+ public boolean isActivated();
}
diff --git a/main/src/cgeo/geocaching/connector/gc/AbstractSearchThread.java b/main/src/cgeo/geocaching/connector/gc/AbstractSearchThread.java
index d0e45f6..f19064d 100644
--- a/main/src/cgeo/geocaching/connector/gc/AbstractSearchThread.java
+++ b/main/src/cgeo/geocaching/connector/gc/AbstractSearchThread.java
@@ -12,7 +12,7 @@ abstract public class AbstractSearchThread extends Thread {
private final Handler handler;
private static AbstractSearchThread currentInstance;
- public AbstractSearchThread(final Handler handler) {
+ protected AbstractSearchThread(final Handler handler) {
this.handler = handler;
}
diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java
index 197d4e6..8943835 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java
@@ -3,10 +3,12 @@ package cgeo.geocaching.connector.gc;
import cgeo.geocaching.R;
import cgeo.geocaching.SearchResult;
import cgeo.geocaching.cgCache;
-import cgeo.geocaching.cgeoapplication;
+import cgeo.geocaching.cgData;
import cgeo.geocaching.connector.AbstractConnector;
import cgeo.geocaching.connector.capability.ISearchByCenter;
import cgeo.geocaching.connector.capability.ISearchByGeocode;
+import cgeo.geocaching.connector.capability.ISearchByViewPort;
+import cgeo.geocaching.enumerations.CacheRealm;
import cgeo.geocaching.enumerations.StatusCode;
import cgeo.geocaching.geopoint.Geopoint;
import cgeo.geocaching.geopoint.Viewport;
@@ -18,21 +20,24 @@ import org.apache.commons.lang3.StringUtils;
import java.util.regex.Pattern;
-public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter {
+public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort {
private static final String HTTP_COORD_INFO = "http://coord.info/";
- private static GCConnector instance;
private static final Pattern gpxZipFilePattern = Pattern.compile("\\d{7,}(_.+)?\\.zip", Pattern.CASE_INSENSITIVE);
private GCConnector() {
// singleton
}
+ /**
+ * initialization on demand holder pattern
+ */
+ private static class Holder {
+ private static final GCConnector INSTANCE = new GCConnector();
+ }
+
public static GCConnector getInstance() {
- if (instance == null) {
- instance = new GCConnector();
- }
- return instance;
+ return Holder.INSTANCE;
}
@Override
@@ -50,6 +55,11 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
}
@Override
+ public boolean supportsOwnCoordinates() {
+ return true;
+ }
+
+ @Override
public boolean supportsWatchList() {
return true;
}
@@ -83,11 +93,10 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
if (StringUtils.isEmpty(page)) {
final SearchResult search = new SearchResult();
- if (cgeoapplication.getInstance().isThere(geocode, guid, true, false)) {
+ if (cgData.isThere(geocode, guid, true, false)) {
if (StringUtils.isBlank(geocode) && StringUtils.isNotBlank(guid)) {
Log.i("Loading old cache from cache.");
-
- search.addGeocode(cgeoapplication.getInstance().getGeocode(guid));
+ search.addGeocode(cgData.getGeocodeForGuid(guid));
} else {
search.addGeocode(geocode);
}
@@ -103,7 +112,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
final SearchResult searchResult = GCParser.parseCache(page, handler);
if (searchResult == null || CollectionUtils.isEmpty(searchResult.getGeocodes())) {
- Log.e("GCConnector.searchByGeocode: No cache parsed");
+ Log.w("GCConnector.searchByGeocode: No cache parsed");
return searchResult;
}
@@ -129,7 +138,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
public static boolean addToWatchlist(cgCache cache) {
final boolean added = GCParser.addToWatchlist(cache);
if (added) {
- cgeoapplication.getInstance().updateCache(cache);
+ cgData.saveChangedCache(cache);
}
return added;
}
@@ -137,28 +146,64 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
public static boolean removeFromWatchlist(cgCache cache) {
final boolean removed = GCParser.removeFromWatchlist(cache);
if (removed) {
- cgeoapplication.getInstance().updateCache(cache);
+ cgData.saveChangedCache(cache);
}
return removed;
}
+ /**
+ * Add a cache to the favorites list.
+ *
+ * This must not be called from the UI thread.
+ *
+ * @param cache the cache to add
+ * @return <code>true</code> if the cache was sucessfully added, <code>false</code> otherwise
+ */
+
public static boolean addToFavorites(cgCache cache) {
final boolean added = GCParser.addToFavorites(cache);
if (added) {
- cgeoapplication.getInstance().updateCache(cache);
+ cgData.saveChangedCache(cache);
}
return added;
}
+ /**
+ * Remove a cache from the favorites list.
+ *
+ * This must not be called from the UI thread.
+ *
+ * @param cache the cache to add
+ * @return <code>true</code> if the cache was sucessfully added, <code>false</code> otherwise
+ */
+
public static boolean removeFromFavorites(cgCache cache) {
final boolean removed = GCParser.removeFromFavorites(cache);
if (removed) {
- cgeoapplication.getInstance().updateCache(cache);
+ cgData.saveChangedCache(cache);
}
return removed;
}
@Override
+ public boolean uploadModifiedCoordinates(cgCache cache, Geopoint wpt) {
+ final boolean uploaded = GCParser.uploadModifiedCoordinates(cache, wpt);
+ if (uploaded) {
+ cgData.saveChangedCache(cache);
+ }
+ return uploaded;
+ }
+
+ @Override
+ public boolean deleteModifiedCoordinates(cgCache cache) {
+ final boolean deleted = GCParser.deleteModifiedCoordinates(cache);
+ if (deleted) {
+ cgData.saveChangedCache(cache);
+ }
+ return deleted;
+ }
+
+ @Override
public SearchResult searchByCenter(Geopoint center) {
// TODO make search by coordinate use this method. currently it is just a marker that this connector supports search by center
return null;
@@ -173,4 +218,14 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
protected String getCacheUrlPrefix() {
return HTTP_COORD_INFO;
}
+
+ @Override
+ public CacheRealm getCacheRealm() {
+ return CacheRealm.GC;
+ }
+
+ @Override
+ public boolean isActivated() {
+ return true;
+ }
}
diff --git a/main/src/cgeo/geocaching/connector/gc/GCConstants.java b/main/src/cgeo/geocaching/connector/gc/GCConstants.java
index 2b4ca46..282c88c 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCConstants.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCConstants.java
@@ -1,5 +1,6 @@
package cgeo.geocaching.connector.gc;
+import java.util.Locale;
import java.util.regex.Pattern;
/**
@@ -49,7 +50,7 @@ public final class GCConstants {
public final static Pattern PATTERN_FAVORITE = Pattern.compile("<div id=\"pnlFavoriteCache\">"); // without 'class="hideMe"' inside the tag !
public final static Pattern PATTERN_FAVORITECOUNT = Pattern.compile("<a id=\"uxFavContainerLink\"[^>]+>[^<]*<div[^<]*<span class=\"favorite-value\">\\D*([0-9]+?)</span>");
public final static Pattern PATTERN_COUNTLOGS = Pattern.compile("<span id=\"ctl00_ContentBody_lblFindCounts\"><p(.+?)</p></span>");
- public final static Pattern PATTERN_LOGBOOK = Pattern.compile("initalLogs = (\\{.+\\});");
+ public final static Pattern PATTERN_LOGBOOK = Pattern.compile("initalLogs = (\\{.+\\});"); // The "inital" typo really comes from gc.com site
/** Two groups ! */
public final static Pattern PATTERN_COUNTLOG = Pattern.compile("<img src=\"/images/logtypes/([0-9]+)\\.png\"[^>]+> (\\d*[,.]?\\d+)");
public static final Pattern PATTERN_PREMIUMMEMBERS = Pattern.compile("<p class=\"Warning NoBottomSpacing\">This is a Premium Member Only cache.</p>");
@@ -103,7 +104,7 @@ public final class GCConstants {
public final static Pattern PATTERN_TRACKABLE_ICON = Pattern.compile("<img id=\"ctl00_ContentBody_BugTypeImage\" class=\"TravelBugHeaderIcon\" src=\"([^\"]+)\"[^>]*>");
public final static Pattern PATTERN_TRACKABLE_TYPE = Pattern.compile("<img id=\"ctl00_ContentBody_BugTypeImage\" class=\"TravelBugHeaderIcon\" src=\"[^\"]+\" alt=\"([^\"]+)\"[^>]*>");
public final static Pattern PATTERN_TRACKABLE_DISTANCE = Pattern.compile("<h4[^>]*\\W*Tracking History \\(([0-9.,]+(km|mi))[^\\)]*\\)");
- public final static Pattern PATTERN_TRACKABLE_LOG = Pattern.compile("<tr class=\"Data BorderTop .+?src=\"/images/logtypes/([^.]+)\\.png[^>]+>&nbsp;([^<]+)</td>.+?guid.+?>([^<]+)</a>.+?(?:guid=([^\"]+)\">(<span[^>]+>)?([^<]+)</.+?)?<td colspan=\"4\">(.+?)(?:<ul.+?ul>)?\\s*</td>\\s*</tr>");
+ public final static Pattern PATTERN_TRACKABLE_LOG = Pattern.compile("<tr class=\"Data BorderTop .+?src=\"/images/logtypes/([^.]+)\\.png[^>]+>&nbsp;([^<]+)</td>.+?guid.+?>([^<]+)</a>.+?(?:guid=([^\"]+)\">(<span[^>]+>)?([^<]+)</.+?)?<td colspan=\"4\">\\s*<div>(.*?)</div>\\s*(?:<ul.+?ul>)?\\s*</td>\\s*</tr>");
public final static Pattern PATTERN_TRACKABLE_LOG_IMAGES = Pattern.compile("<li><a href=\"([^\"]+)\".+?LogImgTitle.+?>([^<]+)</");
/**
@@ -164,8 +165,9 @@ public final class GCConstants {
public final static String STRING_PREMIUMONLY_2 = "Sorry, the owner of this listing has made it viewable to Premium Members only.";
public final static String STRING_PREMIUMONLY_1 = "has chosen to make this cache listing visible to Premium Members only.";
- public final static String STRING_UNPUBLISHED_OWNER = "Cache is Unpublished";
+ public final static String STRING_UNPUBLISHED_OWNER = "cache has not been published yet";
public final static String STRING_UNPUBLISHED_OTHER = "you cannot view this cache listing until it has been published";
+ public final static String STRING_UNPUBLISHED_FROM_SEARCH = "UnpublishedCacheSearchWidget";
public final static String STRING_UNKNOWN_ERROR = "An Error Has Occurred";
public final static String STRING_DISABLED = "<li>This cache is temporarily unavailable.";
public final static String STRING_ARCHIVED = "<li>This cache has been archived,";
@@ -185,14 +187,14 @@ public final class GCConstants {
* see http://support.groundspeak.com/index.php?pg=kb.printer.friendly&id=1#p221
*/
public static long gccodeToGCId(final String gccode) {
- long gcid = 0;
long base = GC_BASE31;
- String geocodeWO = gccode.substring(2).toUpperCase();
+ String geocodeWO = gccode.substring(2).toUpperCase(Locale.US);
if ((geocodeWO.length() < 4) || (geocodeWO.length() == 4 && SEQUENCE_GCID.indexOf(geocodeWO.charAt(0)) < 16)) {
base = GC_BASE16;
}
+ long gcid = 0;
for (int p = 0; p < geocodeWO.length(); p++) {
gcid = base * gcid + SEQUENCE_GCID.indexOf(geocodeWO.charAt(p));
}
diff --git a/main/src/cgeo/geocaching/connector/gc/GCMap.java b/main/src/cgeo/geocaching/connector/gc/GCMap.java
index 681a1d7..9a8123d 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCMap.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCMap.java
@@ -3,6 +3,7 @@ package cgeo.geocaching.connector.gc;
import cgeo.geocaching.SearchResult;
import cgeo.geocaching.Settings;
import cgeo.geocaching.cgCache;
+import cgeo.geocaching.cgData;
import cgeo.geocaching.cgeoapplication;
import cgeo.geocaching.enumerations.CacheSize;
import cgeo.geocaching.enumerations.CacheType;
@@ -40,10 +41,10 @@ public class GCMap {
final SearchResult result = new SearchResult();
final String geocodeList = StringUtils.join(geocodes.toArray(), "|");
- final String referer = GCConstants.URL_LIVE_MAP_DETAILS;
try {
final Parameters params = new Parameters("i", geocodeList, "_", String.valueOf(System.currentTimeMillis()));
+ final String referer = GCConstants.URL_LIVE_MAP_DETAILS;
final String data = StringUtils.defaultString(Tile.requestMapInfo(referer, params, referer));
// Example JSON information
@@ -166,6 +167,8 @@ public class GCMap {
// iterate over the data and construct all caches in this tile
Map<String, List<UTFGridPosition>> positions = new HashMap<String, List<UTFGridPosition>>(); // JSON id as key
+ Map<String, List<UTFGridPosition>> singlePositions = new HashMap<String, List<UTFGridPosition>>(); // 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)) {
@@ -177,12 +180,20 @@ public class GCMap {
nameCache.put(id, cacheInfo.getString("n"));
List<UTFGridPosition> listOfPositions = positions.get(id);
+ List<UTFGridPosition> singleListOfPositions = singlePositions.get(id);
+
if (listOfPositions == null) {
listOfPositions = new ArrayList<UTFGridPosition>();
positions.put(id, listOfPositions);
+ singleListOfPositions = new ArrayList<UTFGridPosition>();
+ singlePositions.put(id, singleListOfPositions);
}
listOfPositions.add(pos);
+ if (dataForKey.length() == 1) {
+ singleListOfPositions.add(pos);
+ }
+
}
}
}
@@ -198,12 +209,16 @@ public class GCMap {
cache.setName(nameCache.get(id));
cache.setZoomlevel(tile.getZoomlevel());
cache.setCoords(tile.getCoord(xy));
- 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());
+ if (strategy.flags.contains(StrategyFlag.PARSE_TILES) && bitmap != null) {
+ for (UTFGridPosition singlePos : singlePositions.get(id)) {
+ if (IconDecoder.parseMapPNG(cache, bitmap, singlePos, tile.getZoomlevel())) {
+ break; // cache parsed
+ }
+ }
} else {
cache.setType(CacheType.UNKNOWN);
}
+
boolean exclude = false;
if (Settings.isExcludeMyCaches() && (cache.isFound() || cache.isOwn())) { // workaround for BM
exclude = true;
@@ -313,7 +328,7 @@ public class GCMap {
String data = Tile.requestMapInfo(GCConstants.URL_MAP_INFO, params, GCConstants.URL_LIVE_MAP);
if (StringUtils.isEmpty(data)) {
- Log.e("GCBase.searchByViewport: No data from server for tile (" + tile.getX() + "/" + tile.getY() + ")");
+ Log.w("GCBase.searchByViewport: No data from server for tile (" + tile.getX() + "/" + tile.getY() + ")");
} else {
final SearchResult search = GCMap.parseMapJSON(data, tile, bitmap, strategy);
if (search == null || CollectionUtils.isEmpty(search.getGeocodes())) {
@@ -341,7 +356,7 @@ public class GCMap {
if (search != null && !search.isEmpty()) {
final Set<String> geocodes = search.getGeocodes();
if (Settings.isPremiumMember()) {
- lastSearchViewport = cgeoapplication.getInstance().getBounds(geocodes);
+ lastSearchViewport = cgData.getBounds(geocodes);
} else {
lastSearchViewport = new Viewport(center, center);
}
diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java
index 9edc34f..91b0ddd 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCParser.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java
@@ -6,6 +6,7 @@ import cgeo.geocaching.SearchResult;
import cgeo.geocaching.Settings;
import cgeo.geocaching.TrackableLog;
import cgeo.geocaching.cgCache;
+import cgeo.geocaching.cgData;
import cgeo.geocaching.cgImage;
import cgeo.geocaching.cgTrackable;
import cgeo.geocaching.cgWaypoint;
@@ -30,6 +31,7 @@ import cgeo.geocaching.utils.BaseUtils;
import cgeo.geocaching.utils.CancellableHandler;
import cgeo.geocaching.utils.LazyInitializedList;
import cgeo.geocaching.utils.Log;
+import cgeo.geocaching.utils.MatcherWrapper;
import ch.boye.httpclientandroidlib.HttpResponse;
@@ -43,12 +45,7 @@ import org.json.JSONObject;
import android.net.Uri;
import android.text.Html;
-import android.text.Spannable;
-import android.text.Spanned;
-import android.text.style.ForegroundColorSpan;
-import android.text.style.StrikethroughSpan;
-import java.net.URLDecoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -58,7 +55,6 @@ 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
@@ -71,8 +67,6 @@ public abstract class GCParser {
}
final List<String> cids = new ArrayList<String>();
- String recaptchaChallenge = null;
- String recaptchaText = null;
String page = pageContent;
final SearchResult searchResult = new SearchResult();
@@ -81,6 +75,7 @@ public abstract class GCParser {
// recaptcha
AbstractSearchThread thread = AbstractSearchThread.getCurrentInstance();
+ String recaptchaChallenge = null;
if (showCaptcha) {
String recaptchaJsParam = BaseUtils.getMatch(page, GCConstants.PATTERN_SEARCH_RECAPTCHA, false, null);
@@ -133,7 +128,7 @@ public abstract class GCParser {
}
try {
- final Matcher matcherGuidAndDisabled = GCConstants.PATTERN_SEARCH_GUIDANDDISABLED.matcher(row);
+ final MatcherWrapper matcherGuidAndDisabled = new MatcherWrapper(GCConstants.PATTERN_SEARCH_GUIDANDDISABLED, row);
while (matcherGuidAndDisabled.find()) {
if (matcherGuidAndDisabled.groupCount() > 0) {
@@ -163,29 +158,32 @@ public abstract class GCParser {
continue;
}
- String inventoryPre = null;
-
- cache.setGeocode(BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_GEOCODE, true, 1, cache.getGeocode(), true).toUpperCase());
+ cache.setGeocode(BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_GEOCODE, true, 1, cache.getGeocode(), true));
// 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.setDirectionImg(Network.decode(BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION, true, 1, cache.getDirectionImg(), true)));
}
// cache inventory
- final Matcher matcherTbs = GCConstants.PATTERN_SEARCH_TRACKABLES.matcher(row);
+ final MatcherWrapper matcherTbs = new MatcherWrapper(GCConstants.PATTERN_SEARCH_TRACKABLES, row);
+ String inventoryPre = null;
while (matcherTbs.find()) {
if (matcherTbs.groupCount() > 0) {
- cache.setInventoryItems(Integer.parseInt(matcherTbs.group(1)));
+ try {
+ cache.setInventoryItems(Integer.parseInt(matcherTbs.group(1)));
+ } catch (NumberFormatException e) {
+ Log.e("Error parsing trackables count", e);
+ }
inventoryPre = matcherTbs.group(2);
}
}
if (StringUtils.isNotBlank(inventoryPre)) {
- final Matcher matcherTbsInside = GCConstants.PATTERN_SEARCH_TRACKABLESINSIDE.matcher(inventoryPre);
+ final MatcherWrapper matcherTbsInside = new MatcherWrapper(GCConstants.PATTERN_SEARCH_TRACKABLESINSIDE, inventoryPre);
while (matcherTbsInside.find()) {
if (matcherTbsInside.groupCount() == 2 &&
matcherTbsInside.group(2) != null &&
@@ -222,16 +220,6 @@ public abstract class GCParser {
Log.w("GCParser.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);
- }
- if (cache.isArchived()) {
- cache.getNameSp().setSpan(new ForegroundColorSpan(cgeoapplication.getInstance().getResources().getColor(R.color.archived_cache_color)), 0, cache.getNameSp().toString().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- }
-
searchResult.addCache(cache);
}
@@ -245,6 +233,7 @@ public abstract class GCParser {
Log.w("GCParser.parseSearch: Failed to parse cache count");
}
+ String recaptchaText = null;
if (thread != null && recaptchaChallenge != null) {
if (thread.getText() == null) {
thread.waitForUser();
@@ -253,7 +242,7 @@ public abstract class GCParser {
recaptchaText = thread.getText();
}
- if (cids.size() > 0 && (Settings.isPremiumMember() || showCaptcha) && (recaptchaChallenge == null || StringUtils.isNotBlank(recaptchaText))) {
+ if (!cids.isEmpty() && (Settings.isPremiumMember() || showCaptcha) && (recaptchaChallenge == null || StringUtils.isNotBlank(recaptchaText))) {
Log.i("Trying to get .loc for " + cids.size() + " caches");
try {
@@ -326,7 +315,7 @@ public abstract class GCParser {
// save full detailed caches
CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_cache);
- cgeoapplication.getInstance().saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB));
+ cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB));
// update progress message so user knows we're still working. This is more of a place holder than
// actual indication of what the program is doing
@@ -345,7 +334,7 @@ public abstract class GCParser {
final SearchResult searchResult = new SearchResult();
- if (page.contains(GCConstants.STRING_UNPUBLISHED_OTHER) || page.contains(GCConstants.STRING_UNPUBLISHED_OWNER)) {
+ if (page.contains(GCConstants.STRING_UNPUBLISHED_OTHER) || page.contains(GCConstants.STRING_UNPUBLISHED_OWNER) || page.contains(GCConstants.STRING_UNPUBLISHED_FROM_SEARCH)) {
searchResult.setError(StatusCode.UNPUBLISHED_CACHE);
return searchResult;
}
@@ -383,10 +372,12 @@ public abstract class GCParser {
cache.setName(cacheName);
// owner real name
- cache.setOwnerUserId(URLDecoder.decode(BaseUtils.getMatch(page, GCConstants.PATTERN_OWNER_USERID, true, cache.getOwnerUserId())));
+ cache.setOwnerUserId(Network.decode(BaseUtils.getMatch(page, GCConstants.PATTERN_OWNER_USERID, true, cache.getOwnerUserId())));
cache.setOwn(StringUtils.equalsIgnoreCase(cache.getOwnerUserId(), Settings.getUsername()));
+ cache.setUserModifiedCoords(false);
+
String tableInside = page;
int pos = tableInside.indexOf(GCConstants.STRING_CACHEDETAILS);
@@ -401,13 +392,21 @@ public abstract class GCParser {
// cache terrain
String result = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_TERRAIN, true, null);
if (result != null) {
- cache.setTerrain(Float.parseFloat(StringUtils.replaceChars(result, '_', '.')));
+ try {
+ cache.setTerrain(Float.parseFloat(StringUtils.replaceChars(result, '_', '.')));
+ } catch (NumberFormatException e) {
+ Log.e("Error parsing terrain value", e);
+ }
}
// cache difficulty
result = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_DIFFICULTY, true, null);
if (result != null) {
- cache.setDifficulty(Float.parseFloat(StringUtils.replaceChars(result, '_', '.')));
+ try {
+ cache.setDifficulty(Float.parseFloat(StringUtils.replaceChars(result, '_', '.')));
+ } catch (NumberFormatException e) {
+ Log.e("Error parsing difficulty value", e);
+ }
}
// owner
@@ -432,10 +431,14 @@ public abstract class GCParser {
}
// favourite
- cache.setFavoritePoints(Integer.parseInt(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_FAVORITECOUNT, true, "0")));
+ try {
+ cache.setFavoritePoints(Integer.parseInt(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_FAVORITECOUNT, true, "0")));
+ } catch (NumberFormatException e) {
+ Log.e("Error parsing favourite count", e);
+ }
// cache size
- cache.setSize(CacheSize.getById(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_SIZE, true, CacheSize.NOT_CHOSEN.id).toLowerCase()));
+ cache.setSize(CacheSize.getById(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_SIZE, true, CacheSize.NOT_CHOSEN.id)));
}
// cache found
@@ -497,21 +500,21 @@ public abstract class GCParser {
try {
final String attributesPre = BaseUtils.getMatch(page, GCConstants.PATTERN_ATTRIBUTES, true, null);
if (null != attributesPre) {
- final Matcher matcherAttributesInside = GCConstants.PATTERN_ATTRIBUTESINSIDE.matcher(attributesPre);
+ final MatcherWrapper matcherAttributesInside = new MatcherWrapper(GCConstants.PATTERN_ATTRIBUTESINSIDE, attributesPre);
final ArrayList<String> attributes = new ArrayList<String>();
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();
+ String attribute = matcherAttributesInside.group(2).toLowerCase(Locale.US);
// if the image name can be recognized, use the image name as attribute
- String imageName = matcherAttributesInside.group(1).trim();
- if (imageName.length() > 0) {
+ final String imageName = matcherAttributesInside.group(1).trim();
+ if (StringUtils.isNotEmpty(imageName)) {
int start = imageName.lastIndexOf('/');
int end = imageName.lastIndexOf('.');
if (start >= 0 && end >= 0) {
- attribute = imageName.substring(start + 1, end).replace('-', '_').toLowerCase();
+ attribute = imageName.substring(start + 1, end).replace('-', '_').toLowerCase(Locale.US);
}
}
attributes.add(attribute);
@@ -531,24 +534,20 @@ public abstract class GCParser {
}
CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_spoilers);
- final Matcher matcherSpoilersInside = GCConstants.PATTERN_SPOILER_IMAGE.matcher(page);
+ final MatcherWrapper matcherSpoilersInside = new MatcherWrapper(GCConstants.PATTERN_SPOILER_IMAGE, page);
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
- //
- // Create a new string to avoid referencing the whole page though the matcher (@see BaseUtils.getMatch())
- String url = new String(matcherSpoilersInside.group(1).replace("/display", ""));
+ String url = matcherSpoilersInside.group(1).replace("/display", "");
String title = null;
if (matcherSpoilersInside.group(3) != null) {
- // Create a new string to avoid referencing the whole page though the matcher (@see BaseUtils.getMatch())
- title = new String(matcherSpoilersInside.group(3));
+ title = matcherSpoilersInside.group(3);
}
String description = null;
if (matcherSpoilersInside.group(4) != null) {
- // Create a new string to avoid referencing the whole page though the matcher (@see BaseUtils.getMatch())
- description = new String(matcherSpoilersInside.group(4));
+ description = matcherSpoilersInside.group(4);
}
cache.addSpoiler(new cgImage(url, title, description));
}
@@ -561,7 +560,7 @@ public abstract class GCParser {
try {
cache.setInventoryItems(0);
- final Matcher matcherInventory = GCConstants.PATTERN_INVENTORY.matcher(page);
+ final MatcherWrapper matcherInventory = new MatcherWrapper(GCConstants.PATTERN_INVENTORY, page);
if (matcherInventory.find()) {
if (cache.getInventory() == null) {
cache.setInventory(new ArrayList<cgTrackable>());
@@ -571,15 +570,13 @@ public abstract class GCParser {
final String inventoryPre = matcherInventory.group(2);
if (StringUtils.isNotBlank(inventoryPre)) {
- final Matcher matcherInventoryInside = GCConstants.PATTERN_INVENTORYINSIDE.matcher(inventoryPre);
+ final MatcherWrapper matcherInventoryInside = new MatcherWrapper(GCConstants.PATTERN_INVENTORYINSIDE, inventoryPre);
while (matcherInventoryInside.find()) {
if (matcherInventoryInside.groupCount() > 0) {
final cgTrackable inventoryItem = new cgTrackable();
- // Create a new string to avoid referencing the whole page though the matcher (@see BaseUtils.getMatch())
- inventoryItem.setGuid(new String(matcherInventoryInside.group(1)));
- // Create a new string to avoid referencing the whole page though the matcher (@see BaseUtils.getMatch())
- inventoryItem.setName(new String(matcherInventoryInside.group(2)));
+ inventoryItem.setGuid(matcherInventoryInside.group(1));
+ inventoryItem.setName(matcherInventoryInside.group(2));
cache.getInventory().add(inventoryItem);
cache.setInventoryItems(cache.getInventoryItems() + 1);
@@ -594,14 +591,12 @@ public abstract class GCParser {
}
// cache logs counts
- try
- {
+ try {
final String countlogs = BaseUtils.getMatch(page, GCConstants.PATTERN_COUNTLOGS, true, null);
if (null != countlogs) {
- final Matcher matcherLog = GCConstants.PATTERN_COUNTLOG.matcher(countlogs);
+ final MatcherWrapper matcherLog = new MatcherWrapper(GCConstants.PATTERN_COUNTLOG, countlogs);
- while (matcherLog.find())
- {
+ while (matcherLog.find()) {
String typeStr = matcherLog.group(1);
String countStr = matcherLog.group(2).replaceAll("[.,]", "");
@@ -612,8 +607,7 @@ public abstract class GCParser {
}
}
}
- } catch (Exception e)
- {
+ } catch (Exception e) {
// failed to parse logs
Log.w("GCParser.parseCache: Failed to parse cache log count");
}
@@ -626,7 +620,7 @@ public abstract class GCParser {
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);
+ final cgWaypoint waypoint = new cgWaypoint(cgeoapplication.getInstance().getString(R.string.cache_coordinates_original), WaypointType.ORIGINAL, false);
waypoint.setCoords(new Geopoint(originalCoords));
cache.addOrChangeWaypoint(waypoint, false);
cache.setUserModifiedCoords(true);
@@ -634,10 +628,7 @@ public abstract class GCParser {
} catch (Geopoint.GeopointException e) {
}
- int wpBegin;
- int wpEnd;
-
- wpBegin = page.indexOf("<table class=\"Table\" id=\"ctl00_ContentBody_Waypoints\">");
+ int wpBegin = page.indexOf("<table class=\"Table\" id=\"ctl00_ContentBody_Waypoints\">");
if (wpBegin != -1) { // parse waypoints
if (CancellableHandler.isCancelled(handler)) {
return null;
@@ -646,7 +637,7 @@ public abstract class GCParser {
String wpList = page.substring(wpBegin);
- wpEnd = wpList.indexOf("</p>");
+ int wpEnd = wpList.indexOf("</p>");
if (wpEnd > -1 && wpEnd <= wpList.length()) {
wpList = wpList.substring(0, wpEnd);
}
@@ -663,9 +654,8 @@ public abstract class GCParser {
final String[] wpItems = wpList.split("<tr");
- String[] wp;
for (int j = 1; j < wpItems.length; j++) {
- wp = wpItems[j].split("<td");
+ String[] wp = wpItems[j].split("<td");
// waypoint name
// res is null during the unit tests
@@ -756,7 +746,7 @@ public abstract class GCParser {
final SearchResult searchResult = parseSearch(url, page, showCaptcha);
if (searchResult == null || CollectionUtils.isEmpty(searchResult.getGeocodes())) {
- Log.e("GCParser.searchByNextPage: No cache parsed");
+ Log.w("GCParser.searchByNextPage: No cache parsed");
return search;
}
@@ -923,7 +913,7 @@ public abstract class GCParser {
trackable = parseTrackable(page, geocode);
if (trackable == null) {
- Log.e("GCParser.searchTrackable: No trackable parsed");
+ Log.w("GCParser.searchTrackable: No trackable parsed");
return null;
}
@@ -1004,7 +994,7 @@ public abstract class GCParser {
// maintenance, archived needs to be confirmed
- final Matcher matcher = GCConstants.PATTERN_MAINTENANCE.matcher(page);
+ final MatcherWrapper matcher = new MatcherWrapper(GCConstants.PATTERN_MAINTENANCE, page);
try {
if (matcher.find() && matcher.groupCount() > 0) {
@@ -1053,12 +1043,12 @@ public abstract class GCParser {
try {
- final Matcher matcherOk = GCConstants.PATTERN_OK1.matcher(page);
+ final MatcherWrapper matcherOk = new MatcherWrapper(GCConstants.PATTERN_OK1, page);
if (matcherOk.find()) {
Log.i("Log successfully posted to cache #" + cacheid);
if (geocode != null) {
- cgeoapplication.getInstance().saveVisitDate(geocode);
+ cgData.saveVisitDate(geocode);
}
Login.getLoginStatus(page);
@@ -1122,7 +1112,7 @@ public abstract class GCParser {
try {
- final Matcher matcherOk = GCConstants.PATTERN_OK2.matcher(page);
+ final MatcherWrapper matcherOk = new MatcherWrapper(GCConstants.PATTERN_OK2, page);
if (matcherOk.find()) {
Log.i("Log successfully posted to trackable #" + trackingCode);
return StatusCode.NO_ERROR;
@@ -1211,6 +1201,8 @@ public abstract class GCParser {
/**
* Adds the cache to the favorites of the user.
*
+ * This must not be called from the UI thread.
+ *
* @param cache
* the cache to add
* @return <code>false</code> if an error occurred, <code>true</code> otherwise
@@ -1241,7 +1233,9 @@ public abstract class GCParser {
}
/**
- * Removes the cache from the Favorites
+ * Removes the cache from the favorites.
+ *
+ * This must not be called from the UI thread.
*
* @param cache
* the cache to remove
@@ -1256,8 +1250,6 @@ public abstract class GCParser {
*
* @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
*/
static cgTrackable parseTrackable(final String page, final String possibleTrackingcode) {
@@ -1273,7 +1265,7 @@ public abstract class GCParser {
final cgTrackable trackable = new cgTrackable();
// trackable geocode
- trackable.setGeocode(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GEOCODE, true, trackable.getGeocode()).toUpperCase());
+ trackable.setGeocode(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GEOCODE, true, trackable.getGeocode()));
// trackable id
trackable.setGuid(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GUID, true, trackable.getGuid()));
@@ -1291,7 +1283,7 @@ public abstract class GCParser {
// trackable owner name
try {
- final Matcher matcherOwner = GCConstants.PATTERN_TRACKABLE_OWNER.matcher(page);
+ final MatcherWrapper matcherOwner = new MatcherWrapper(GCConstants.PATTERN_TRACKABLE_OWNER, page);
if (matcherOwner.find() && matcherOwner.groupCount() > 0) {
trackable.setOwnerGuid(matcherOwner.group(1));
trackable.setOwner(matcherOwner.group(2).trim());
@@ -1306,14 +1298,14 @@ public abstract class GCParser {
// trackable spotted
try {
- final Matcher matcherSpottedCache = GCConstants.PATTERN_TRACKABLE_SPOTTEDCACHE.matcher(page);
+ final MatcherWrapper matcherSpottedCache = new MatcherWrapper(GCConstants.PATTERN_TRACKABLE_SPOTTEDCACHE, 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);
+ final MatcherWrapper matcherSpottedUser = new MatcherWrapper(GCConstants.PATTERN_TRACKABLE_SPOTTEDUSER, page);
if (matcherSpottedUser.find() && matcherSpottedUser.groupCount() > 0) {
trackable.setSpottedGuid(matcherSpottedUser.group(1));
trackable.setSpottedName(matcherSpottedUser.group(2).trim());
@@ -1356,7 +1348,7 @@ public abstract class GCParser {
// trackable details & image
try {
- final Matcher matcherDetailsImage = GCConstants.PATTERN_TRACKABLE_DETAILSIMAGE.matcher(page);
+ final MatcherWrapper matcherDetailsImage = new MatcherWrapper(GCConstants.PATTERN_TRACKABLE_DETAILSIMAGE, page);
if (matcherDetailsImage.find() && matcherDetailsImage.groupCount() > 0) {
final String image = StringUtils.trim(matcherDetailsImage.group(3));
final String details = StringUtils.trim(matcherDetailsImage.group(4));
@@ -1375,7 +1367,7 @@ public abstract class GCParser {
// trackable logs
try {
- final Matcher matcherLogs = GCConstants.PATTERN_TRACKABLE_LOG.matcher(page);
+ final MatcherWrapper matcherLogs = new MatcherWrapper(GCConstants.PATTERN_TRACKABLE_LOG, page);
/*
* 1. Type (image)
* 2. Date
@@ -1404,7 +1396,8 @@ public abstract class GCParser {
}
// 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));
+ final String logEntry = matcherLogs.group(0);
+ final MatcherWrapper matcherLogImages = new MatcherWrapper(GCConstants.PATTERN_TRACKABLE_LOG_IMAGES, logEntry);
/*
* 1. Image URL
* 2. Image title
@@ -1427,7 +1420,7 @@ public abstract class GCParser {
}
if (cgeoapplication.getInstance() != null) {
- cgeoapplication.getInstance().saveTrackable(trackable);
+ cgData.saveTrackable(trackable);
}
return trackable;
@@ -1454,7 +1447,7 @@ public abstract class GCParser {
String rawResponse;
if (!getDataFromPage) {
- final Matcher userTokenMatcher = GCConstants.PATTERN_USERTOKEN.matcher(page);
+ final MatcherWrapper userTokenMatcher = new MatcherWrapper(GCConstants.PATTERN_USERTOKEN, page);
if (!userTokenMatcher.find()) {
Log.e("GCParser.loadLogsFromDetails: unable to extract userToken");
return null;
@@ -1553,7 +1546,7 @@ public abstract class GCParser {
final List<LogType> types = new ArrayList<LogType>();
- final Matcher typeBoxMatcher = GCConstants.PATTERN_TYPEBOX.matcher(page);
+ final MatcherWrapper typeBoxMatcher = new MatcherWrapper(GCConstants.PATTERN_TYPEBOX, page);
String typesText = null;
if (typeBoxMatcher.find()) {
if (typeBoxMatcher.groupCount() > 0) {
@@ -1563,13 +1556,16 @@ public abstract class GCParser {
if (typesText != null) {
- final Matcher typeMatcher = GCConstants.PATTERN_TYPE2.matcher(typesText);
+ final MatcherWrapper typeMatcher = new MatcherWrapper(GCConstants.PATTERN_TYPE2, typesText);
while (typeMatcher.find()) {
if (typeMatcher.groupCount() > 1) {
- final int type = Integer.parseInt(typeMatcher.group(2));
-
- if (type > 0) {
- types.add(LogType.getById(type));
+ try {
+ int type = Integer.parseInt(typeMatcher.group(2));
+ if (type > 0) {
+ types.add(LogType.getById(type));
+ }
+ } catch (NumberFormatException e) {
+ Log.e("Error parsing log types", e);
}
}
}
@@ -1598,7 +1594,7 @@ public abstract class GCParser {
final List<TrackableLog> trackableLogs = new ArrayList<TrackableLog>();
- final Matcher trackableMatcher = GCConstants.PATTERN_TRACKABLE.matcher(page);
+ final MatcherWrapper trackableMatcher = new MatcherWrapper(GCConstants.PATTERN_TRACKABLE, page);
while (trackableMatcher.find()) {
if (trackableMatcher.groupCount() > 0) {
@@ -1679,4 +1675,48 @@ public abstract class GCParser {
}
}
+ public static boolean uploadModifiedCoordinates(cgCache cache, Geopoint wpt) {
+ return editModifiedCoordinates(cache, wpt);
+ }
+
+ public static boolean deleteModifiedCoordinates(cgCache cache) {
+ return editModifiedCoordinates(cache, null);
+ }
+
+ public static boolean editModifiedCoordinates(cgCache cache, Geopoint wpt) {
+ final String page = requestHtmlPage(cache.getGeocode(), null, "n", "0");
+ final String userToken = BaseUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, "");
+ 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 String uriPrefix = "http://www.geocaching.com/seek/cache_details.aspx/";
+ HttpResponse response = Network.postJsonRequest(uriPrefix + uriSuffix, jo);
+ Log.i("Sending to " + uriPrefix + uriSuffix + " :" + jo.toString());
+
+ if (response != null && response.getStatusLine().getStatusCode() == 200) {
+ Log.i("GCParser.editModifiedCoordinates - edited on GC.com");
+ return true;
+ }
+
+ } catch (JSONException e) {
+ Log.e("Unknown exception with json wrap code", e);
+ }
+ Log.e("GCParser.deleteModifiedCoordinates - cannot delete modified coords");
+ return false;
+ }
+
}
diff --git a/main/src/cgeo/geocaching/connector/gc/GCSmiliesProvider.java b/main/src/cgeo/geocaching/connector/gc/GCSmiliesProvider.java
index 1083a89..eba9301 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCSmiliesProvider.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCSmiliesProvider.java
@@ -26,7 +26,7 @@ public class GCSmiliesProvider {
public final String text;
- private Smiley(final String text) {
+ Smiley(final String text) {
this.text = text;
}
diff --git a/main/src/cgeo/geocaching/connector/gc/IconDecoder.java b/main/src/cgeo/geocaching/connector/gc/IconDecoder.java
index 74e78cc..1452157 100644
--- a/main/src/cgeo/geocaching/connector/gc/IconDecoder.java
+++ b/main/src/cgeo/geocaching/connector/gc/IconDecoder.java
@@ -11,106 +11,191 @@ import android.graphics.Bitmap;
*/
public abstract class IconDecoder {
- public static void parseMapPNG(final cgCache cache, Bitmap bitmap, UTFGridPosition xy, int zoomlevel) {
+ public static boolean parseMapPNG(final cgCache cache, Bitmap bitmap, UTFGridPosition xy, int zoomlevel) {
if (zoomlevel >= 14) {
- parseMapPNG14(cache, bitmap, xy);
- } else {
- parseMapPNG13(cache, bitmap, xy);
+ return parseMapPNG14(cache, bitmap, xy);
}
+ if (zoomlevel <= 11) {
+ return parseMapPNG11(cache, bitmap, xy);
+ }
+ return parseMapPNG13(cache, bitmap, xy);
}
- private static final int[] OFFSET_X = new int[] { 0, -1, -1, 0, 1, 1, 1, 0, -1, -2, -2, -2, -2, -1, 0, 1, 2, 2, 2, 2, 2, 1, 0, -1, -2 };
- private static final int[] OFFSET_Y = new int[] { 0, 0, 1, 1, 1, 0, -1, -1, -1, -1, 0, 1, 2, 2, 2, 2, 2, 1, 0, -1, -2, -2, -2, -2, -2 };
+ public static int CT_TRADITIONAL = 0;
+ public static int CT_MULTI = 1;
+ public static int CT_MYSTERY = 2;
+ public static int CT_EVENT = 3;
+ public static int CT_VIRTUAL = 4;
+ public static int CT_FOUND = 5;
+ public static int CT_OWN = 6;
+ public static int CT_MEGAEVENT = 7;
+ public static int CT_CITO = 8;
+ public static int CT_WEBCAM = 9;
+ public static int CT_WHEREIGO = 10;
+ public static int CT_EARTH = 11;
+ public static int CT_LETTERBOX = 12;
/**
- * The icon decoder walks a spiral around the center pixel position of the cache
- * and searches for characteristic colors.
+ * The icon decoder over all 16 pixels of image . It should not be invoked on any part of image that might overlay
+ * with other caches.
+ * Is uses decision tree to determine right type.
*
* @param cache
* @param bitmap
* @param xy
+ * @return true if parsing was successful
*/
- private static void parseMapPNG13(final cgCache cache, Bitmap bitmap, UTFGridPosition xy) {
- final int xCenter = xy.getX() * 4 + 2;
- final int yCenter = xy.getY() * 4 + 2;
+ private static boolean parseMapPNG13(final cgCache cache, Bitmap bitmap, UTFGridPosition xy) {
+ final int topX = xy.getX() * 4;
+ final int topY = xy.getY() * 4;
final int bitmapWidth = bitmap.getWidth();
final int bitmapHeight = bitmap.getHeight();
- int countMulti = 0;
- int countFound = 0;
+ if ((topX < 0) || (topY < 0) || (topX + 4 > bitmapWidth) || (topY + 4 > bitmapHeight)) {
+ return false; //out of image position
+ }
- for (int i = 0; i < OFFSET_X.length; i++) {
+ int[] pngType = new int[7];
+ for (int x = topX; x < topX + 4; x++) {
+ for (int y = topY; y < topY + 4; y++) {
+ int color = bitmap.getPixel(x, y);
- // assert that we are still in the tile
- final int x = xCenter + OFFSET_X[i];
- if (x < 0 || x >= bitmapWidth) {
- continue;
- }
-
- final int y = yCenter + OFFSET_Y[i];
- if (y < 0 || y >= bitmapHeight) {
- continue;
- }
+ if ((color & 0xFFFFFF) == 0x5f5f5f) {
+ continue; //Border in every icon is the same and therefore no use to us
+ }
+ if ((color >>> 24) != 255) {
+ continue; //transparent pixels (or semi_transparent) are only shadows of border
+ }
- int color = bitmap.getPixel(x, y) & 0x00FFFFFF;
+ int red = (color & 0xFF0000) >> 16;
+ int green = (color & 0xFF00) >> 8;
+ int blue = color & 0xFF;
- // transparent pixels are not interesting
- if (color == 0) {
- continue;
+ int type = getCacheTypeFromPixel13(red, green, blue);
+ pngType[type]++;
}
+ }
- int red = (color & 0xFF0000) >> 16;
- int green = (color & 0xFF00) >> 8;
- int blue = color & 0xFF;
+ int type = -1;
+ int count = 0;
- // these are quite sure, so one pixel is enough for matching
- if (green > 0x80 && green > red && green > blue) {
- cache.setType(CacheType.TRADITIONAL);
- return;
- }
- if (blue > 0x80 && blue > red && blue > green) {
- cache.setType(CacheType.MYSTERY);
- return;
- }
- if (red > 0x90 && blue < 0x10 && green < 0x10) {
- cache.setType(CacheType.EVENT);
- return;
+ for (int x = 0; x < 7; x++)
+ {
+ if (pngType[x] > count) {
+ count = pngType[x];
+ type = x;
}
+ }
- // next two are hard to distinguish, therefore we sample all pixels of the spiral
- if (red > 0xFA && green > 0xD0) {
- countMulti++;
+ if (count > 1) { // 2 pixels need to detect same type and we say good to go
+ switch (type) {
+ case 0:
+ cache.setType(CacheType.TRADITIONAL);
+ return true;
+ case 1:
+ cache.setType(CacheType.MULTI);
+ return true;
+ case 2:
+ cache.setType(CacheType.MYSTERY); //mystery, whereigo, groundspeak HQ and mystery is most common
+ return true;
+ case 3:
+ cache.setType(CacheType.EVENT); //event, cito, mega-event and event is most common
+ return true;
+ case 4:
+ cache.setType(CacheType.EARTH); //It's an image of ghost (webcam, earth, virtual) and earth in most common
+ return true;
+ case 5:
+ cache.setFound(true);
+ return true;
+ case 6:
+ cache.setOwn(true);
+ return true;
}
- if (red < 0xF3 && red > 0xa0 && green > 0x20 && blue < 0x80) {
- countFound++;
+ }
+ return false;
+ }
+
+ /**
+ * The icon decoder over all 16 pixels of image . It should not be invoked on any part of image that might overlay
+ * with other caches.
+ * Is uses decision tree to determine right type.
+ *
+ * @param cache
+ * @param bitmap
+ * @param xy
+ * @return true if parsing was successful
+ */
+ private static boolean parseMapPNG11(final cgCache cache, Bitmap bitmap, UTFGridPosition xy) {
+ final int topX = xy.getX() * 4;
+ final int topY = xy.getY() * 4;
+ final int bitmapWidth = bitmap.getWidth();
+ final int bitmapHeight = bitmap.getHeight();
+
+ if ((topX < 0) || (topY < 0) || (topX + 4 > bitmapWidth) || (topY + 4 > bitmapHeight)) {
+ return false; //out of image position
+ }
+
+ int[] pngType = new int[5];
+ for (int x = topX; x < topX + 4; x++) {
+ for (int y = topY; y < topY + 4; y++) {
+ int color = bitmap.getPixel(x, y);
+
+
+ if ((color >>> 24) != 255) {
+ continue; //transparent pixels (or semi_transparent) are only shadows of border
+ }
+
+ int r = (color & 0xFF0000) >> 16;
+ int g = (color & 0xFF00) >> 8;
+ int b = color & 0xFF;
+
+ //Duplicate colors does not add any value
+ if (((r == 52) && (g == 52) && (b == 52)) ||
+ ((r == 69) && (g == 69) && (b == 69)) ||
+ ((r == 90) && (g == 90) && (b == 90)) ||
+ ((r == 233) && (g == 233) && (b == 234)) ||
+ ((r == 255) && (g == 255) && (b == 255))) {
+ continue;
+ }
+
+ int type = getCacheTypeFromPixel11(r, g, b);
+ pngType[type]++;
}
}
- // now check whether we are sure about found/multi
- if (countFound > countMulti && countFound >= 2) {
- cache.setFound(true);
+ int type = -1;
+ int count = 0;
+
+ for (int x = 0; x < 5; x++)
+ {
+ if (pngType[x] > count) {
+ count = pngType[x];
+ type = x;
+ }
}
- if (countMulti > countFound && countMulti >= 5) {
- cache.setType(CacheType.MULTI);
+
+ if (count > 1) { // 2 pixels need to detect same type and we say good to go
+ switch (type) {
+ case 0:
+ cache.setType(CacheType.TRADITIONAL);
+ return true;
+ case 1:
+ cache.setType(CacheType.MULTI);
+ return true;
+ case 2:
+ cache.setType(CacheType.MYSTERY); //mystery, whereigo, groundspeak HQ and mystery is most common
+ return true;
+ case 3:
+ cache.setType(CacheType.EVENT); //event, cito, mega-event and event is most common
+ return true;
+ case 4:
+ cache.setType(CacheType.EARTH); //webcam, earth, virtual and earth in most common
+ return true;
+ }
}
+ return false;
}
- // Pixel colors in tile
- private final static int COLOR_BORDER_GRAY = 0x5F5F5F;
- private final static int COLOR_TRADITIONAL = 0x316013;
- private final static int COLOR_MYSTERY = 0x243C97;
- private final static int COLOR_MULTI = 0xFFDE19;
- private final static int COLOR_FOUND = 0xFBEA5D;
-
- // Offset inside cache icon
- private final static int POSX_TRADI = 7;
- private final static int POSY_TRADI = -12;
- private final static int POSX_MULTI = 5; // for orange 8
- private final static int POSY_MULTI = -9; // for orange 10
- private final static int POSX_MYSTERY = 5;
- private final static int POSY_MYSTERY = -13;
- private final static int POSX_FOUND = 10;
- private final static int POSY_FOUND = -8;
/**
* For level 14 find the borders of the icons and then use a single pixel and color to match.
@@ -119,44 +204,376 @@ public abstract class IconDecoder {
* @param bitmap
* @param xy
*/
- private static void parseMapPNG14(cgCache cache, Bitmap bitmap, UTFGridPosition xy) {
- int x = xy.getX() * 4 + 2;
- int y = xy.getY() * 4 + 2;
+ private static boolean parseMapPNG14(cgCache cache, Bitmap bitmap, UTFGridPosition xy) {
+ final int topX = xy.getX() * 4;
+ final int topY = xy.getY() * 4;
+ final int bitmapWidth = bitmap.getWidth();
+ final int bitmapHeight = bitmap.getHeight();
+
+ if ((topX < 0) || (topY < 0) || (topX + 4 > bitmapWidth) || (topY + 4 > bitmapHeight)) {
+ return false; //out of image position
+ }
+
+ int[] pngType = new int[13];
+ for (int x = topX; x < topX + 4; x++) {
+ for (int y = topY; y < topY + 4; y++) {
+ int color = bitmap.getPixel(x, y);
+
+ if ((color & 0xFFFFFF) == 0x5f5f5f) {
+ continue; //Border in every icon is the same and therefore no use to us
+ }
+ if ((color >>> 24) != 255) {
+ continue; //transparent pixels (or semi_transparent) are only shadows of border
+ }
+
+ int r = (color & 0xFF0000) >> 16;
+ int g = (color & 0xFF00) >> 8;
+ int b = color & 0xFF;
+
+ //Duplicate colors does not add any value
+ if (((r == 216) && (g == 216) && (b == 216)) ||
+ ((r == 23) && (g == 23) && (b == 23)) ||
+ ((r == 240) && (g == 240) && (b == 240)) ||
+ ((r == 44) && (g == 44) && (b == 44)) ||
+ ((r == 228) && (g == 228) && (b == 228)) ||
+ ((r == 225) && (g == 225) && (b == 225)) ||
+ ((r == 199) && (g == 199) && (b == 199)) ||
+ ((r == 161) && (g == 161) && (b == 161)) ||
+ ((r == 8) && (g == 8) && (b == 8)) ||
+ ((r == 200) && (g == 200) && (b == 200)) ||
+ ((r == 255) && (g == 255) && (b == 255)) ||
+ ((r == 250) && (g == 250) && (b == 250)) ||
+ ((r == 95) && (g == 95) && (b == 95)) ||
+ ((r == 236) && (g == 236) && (b == 236)) ||
+ ((r == 215) && (g == 215) && (b == 215)) ||
+ ((r == 232) && (g == 232) && (b == 232)) ||
+ ((r == 217) && (g == 217) && (b == 217)) ||
+ ((r == 0) && (g == 0) && (b == 0)) ||
+ ((r == 167) && (g == 167) && (b == 167)) ||
+ ((r == 247) && (g == 247) && (b == 247)) ||
+ ((r == 144) && (g == 144) && (b == 144)) ||
+ ((r == 231) && (g == 231) && (b == 231)) ||
+ ((r == 248) && (g == 248) && (b == 248))) {
+ continue;
+ }
- // search for left border
- int countX = 0;
- while ((bitmap.getPixel(x, y) & 0x00FFFFFF) != COLOR_BORDER_GRAY) {
- if (--x < 0 || ++countX > 20) {
- return;
+ int type = getCacheTypeFromPixel14(r, g, b);
+ pngType[type]++;
}
}
- // search for bottom border
- int countY = 0;
- while ((bitmap.getPixel(x, y) & 0x00FFFFFF) != 0x000000) {
- if (++y >= Tile.TILE_SIZE || ++countY > 20) {
- return;
+
+ int type = -1;
+ int count = 0;
+
+ for (int x = 0; x < 7; x++)
+ {
+ if (pngType[x] > count) {
+ count = pngType[x];
+ type = x;
+ }
+ }
+ /*
+ * public static int CT_MEGAEVENT = 7;
+ * public static int CT_CITO = 8;
+ * public static int CT_WEBCAM = 9;
+ * public static int CT_WHEREIGO = 10;
+ * public static int CT_EARTH = 11;
+ * public static int CT_LETTERBOX = 12;
+ */
+ if (count > 1) { // 2 pixels need to detect same type and we say good to go
+ switch (type) {
+ case 0:
+ cache.setType(CacheType.TRADITIONAL);
+ return true;
+ case 1:
+ cache.setType(CacheType.MULTI);
+ return true;
+ case 2:
+ cache.setType(CacheType.MYSTERY);
+ return true;
+ case 3:
+ cache.setType(CacheType.EVENT);
+ return true;
+ case 4:
+ cache.setType(CacheType.VIRTUAL);
+ return true;
+ case 5:
+ cache.setFound(true);
+ return true;
+ case 6:
+ cache.setOwn(true);
+ return true;
+ case 7:
+ cache.setType(CacheType.MEGA_EVENT);
+ return true;
+ case 8:
+ cache.setType(CacheType.CITO);
+ return true;
+ case 9:
+ cache.setType(CacheType.WEBCAM);
+ return true;
+ case 10:
+ cache.setType(CacheType.WHERIGO);
+ return true;
+ case 11:
+ cache.setType(CacheType.EARTH);
+ return true;
+ case 12:
+ cache.setType(CacheType.LETTERBOX);
+ return true;
+ }
+ }
+ return false;
+
+ }
+
+ /**
+ * This method returns detected type from specific pixel from geocaching.com live map.
+ * It was constructed based on classification tree made by Orange (http://orange.biolab.si/)
+ * Input file was made from every non-transparent pixel of every possible "middle" cache icon from GC map
+ *
+ * @param r
+ * Red component of pixel (from 0 - 255)
+ * @param g
+ * Green component of pixel (from 0 - 255)
+ * @param b
+ * Blue component of pixel (from 0 - 255)
+ * @return Value from 0 to 6 representing detected type or state of the cache.
+ */
+ private static int getCacheTypeFromPixel13(int r, int g, int b) {
+ if (g < 110) {
+ if (r > 87) {
+ return ((g > 73) && (b < 63)) ? CT_FOUND : CT_EVENT;
+ }
+ return CT_MYSTERY;
+ }
+ if (b > 137) {
+ if ((r < 184) && (g > 190)) {
+ return CT_TRADITIONAL;
+ }
+ if ((r < 184) && (g < 191) && (r < 136)) {
+ return CT_MYSTERY;
}
+ return CT_VIRTUAL;
}
+ if (r < 158) {
+ return ((r > 129) && (r < 153)) ? CT_FOUND : CT_TRADITIONAL;
+ }
+ if (b > 33) {
+ if (b > 57) {
+ if (b > 100) {
+ return (r > 229) ? CT_MULTI : CT_EVENT;
+ }
+ return ((r > 254) && (g < 236)) ? CT_MULTI : CT_FOUND;
+ }
+ if ((g > 173) && ((g < 224))) {
+ return ((r < 243) && (r > 223)) ? CT_FOUND : CT_OWN;
+ }
+ return CT_FOUND;
+ }
+ return CT_MULTI;
+ }
- try {
- if ((bitmap.getPixel(x + POSX_TRADI, y + POSY_TRADI) & 0x00FFFFFF) == COLOR_TRADITIONAL) {
- cache.setType(CacheType.TRADITIONAL);
- return;
+ /**
+ * This method returns detected type from specific pixel from geocaching.com live map level 14 or higher.
+ * It was constructed based on classification tree made by Orange (http://orange.biolab.si/)
+ * Input file was made from every non-transparent pixel of every possible "full" cache icon from GC map
+ *
+ * @param r
+ * Red component of pixel (from 0 - 255)
+ * @param g
+ * Green component of pixel (from 0 - 255)
+ * @param b
+ * Blue component of pixel (from 0 - 255)
+ * @return Value from 0 to 6 representing detected type or state of the cache.
+ */
+ private static int getCacheTypeFromPixel14(int r, int g, int b) {
+ if (b < 140) {
+ if (r > 155) {
+ if (g < 159) {
+ if (r < 173) {
+ return (r > 161) ? CT_MEGAEVENT : CT_OWN;
+ }
+ if (r < 206) {
+ if (b > 49) {
+ return (b > 83) ? CT_EVENT : CT_FOUND;
+ }
+ return (b < 31) ? CT_EARTH : CT_FOUND;
+ }
+ return (r < 221) ? CT_FOUND : CT_MULTI;
+ }
+ if (r > 210) {
+ if (g < 188) {
+ return CT_FOUND;
+ }
+ if (r < 246) {
+ return CT_OWN;
+ }
+ if (r < 254) {
+ return CT_FOUND;
+ }
+ if (r < 255) {
+ return CT_EVENT;
+ }
+ if (g < 208) {
+ return CT_EARTH;
+ }
+ if (g > 225) {
+ return CT_EARTH;
+ }
+ return (b < 36) ? CT_MULTI : CT_OWN;
+ }
+ return (b < 66) ? CT_OWN : CT_EARTH;
+ }
+ if (r < 63) {
+ if (b > 26) {
+ if (b < 29) {
+ return CT_WEBCAM;
+ }
+ if (g > 102) {
+ return CT_CITO;
+ }
+ return (r < 26) ? CT_CITO : CT_WEBCAM;
+ }
+ if (g < 38) {
+ return CT_WEBCAM;
+ }
+ return (r < 41) ? CT_EARTH : CT_TRADITIONAL;
+ }
+ if (b < 119) {
+ if (g < 81) {
+ return CT_WEBCAM;
+ }
+ if (b < 90) {
+ return CT_OWN;
+ }
+ return (r < 104) ? CT_WEBCAM : CT_OWN;
+ }
+ if (r < 132) {
+ return (b < 124) ? CT_MULTI : CT_WHEREIGO;
+ }
+ if (g > 164) {
+ return CT_TRADITIONAL;
+ }
+ if (b < 134) {
+ return CT_OWN;
+ }
+ return (b > 137) ? CT_OWN : CT_WHEREIGO;
+ }
+ if (b < 245) {
+ if (r < 180) {
+ if (b < 218) {
+ if (g < 71) {
+ return CT_MYSTERY;
+ }
+ if (r < 96) {
+ return CT_WHEREIGO;
+ }
+ if (b > 165) {
+ return CT_WHEREIGO;
+ }
+ if (r < 153) {
+ return CT_WHEREIGO;
+ }
+ if (r < 160) {
+ return CT_WEBCAM;
+ }
+ return (r < 162) ? CT_WHEREIGO : CT_WEBCAM;
+ }
+ return (r < 158) ? CT_MEGAEVENT : CT_EARTH;
}
- if ((bitmap.getPixel(x + POSX_MYSTERY, y + POSY_MYSTERY) & 0x00FFFFFF) == COLOR_MYSTERY) {
- cache.setType(CacheType.MYSTERY);
- return;
+ if (g > 232) {
+ if (g > 247) {
+ return CT_CITO;
+ }
+ if (r < 237) {
+ return CT_OWN;
+ }
+ if (g < 238) {
+ return CT_OWN;
+ }
+ if (r > 243) {
+ return CT_WEBCAM;
+ }
+ return (g > 238) ? CT_OWN : CT_WEBCAM;
}
- if ((bitmap.getPixel(x + POSX_MULTI, y + POSY_MULTI) & 0x00FFFFFF) == COLOR_MULTI) {
- cache.setType(CacheType.MULTI);
- return;
+ if (r < 228) {
+ if (b > 238) {
+ return CT_MYSTERY;
+ }
+ if (r < 193) {
+ if (r < 184) {
+ return CT_OWN;
+ }
+ if (g < 186) {
+ return CT_WHEREIGO;
+ }
+ return (r > 189) ? CT_WHEREIGO : CT_OWN;
+ }
+ if (g < 223) {
+ if (r > 216) {
+ return CT_OWN;
+ }
+ if (g > 217) {
+ return CT_WHEREIGO;
+ }
+ if (b > 211) {
+ return CT_WEBCAM;
+ }
+ if (b < 196) {
+ return CT_WEBCAM;
+ }
+ if (r > 210) {
+ return CT_OWN;
+ }
+ return (g > 206) ? CT_WHEREIGO : CT_OWN;
+ }
+ if (g < 224) {
+ return CT_OWN;
+ }
+ return (r < 226) ? CT_WHEREIGO : CT_OWN;
}
- if ((bitmap.getPixel(x + POSX_FOUND, y + POSY_FOUND) & 0x00FFFFFF) == COLOR_FOUND) {
- cache.setFound(true);
+ return (b < 216) ? CT_FOUND : CT_OWN;
+ }
+ if (r < 238) {
+ if (r > 141) {
+ return (r > 185) ? CT_LETTERBOX : CT_CITO;
}
- } catch (IllegalArgumentException e) {
- // intentionally left blank
+ return (r < 41) ? CT_EARTH : CT_LETTERBOX;
}
+ return (r < 243) ? CT_WHEREIGO : CT_OWN;
+ }
+ /**
+ * This method returns detected type from specific pixel from geocaching.com live map level 11 or lower.
+ * It was constructed based on classification tree made by Orange (http://orange.biolab.si/)
+ * Input file was made from every non-transparent pixel of every possible "full" cache icon from GC map
+ *
+ * @param r
+ * Red component of pixel (from 0 - 255)
+ * @param g
+ * Green component of pixel (from 0 - 255)
+ * @param b
+ * Blue component of pixel (from 0 - 255)
+ * @return Value from 0 to 4 representing detected type or state of the cache.
+ */
+ private static int getCacheTypeFromPixel11(int r, int g, int b) {
+ if (b < 139) {
+ if (g > 104) {
+ if (r < 173) {
+ return CT_TRADITIONAL;
+ }
+ return (r > 225) ? CT_MULTI : CT_EVENT;
+ }
+ if (b < 25) {
+ return CT_EVENT;
+ }
+ return (r < 87) ? CT_MYSTERY : CT_EVENT;
+ }
+ if (r > 140) {
+ return (r < 197) ? CT_TRADITIONAL : CT_VIRTUAL;
+ }
+ return CT_MYSTERY;
}
+
}
diff --git a/main/src/cgeo/geocaching/connector/gc/Login.java b/main/src/cgeo/geocaching/connector/gc/Login.java
index c3f29cc..9a60f65 100644
--- a/main/src/cgeo/geocaching/connector/gc/Login.java
+++ b/main/src/cgeo/geocaching/connector/gc/Login.java
@@ -10,6 +10,7 @@ import cgeo.geocaching.network.Network;
import cgeo.geocaching.network.Parameters;
import cgeo.geocaching.utils.BaseUtils;
import cgeo.geocaching.utils.Log;
+import cgeo.geocaching.utils.MatcherWrapper;
import ch.boye.httpclientandroidlib.HttpResponse;
@@ -26,7 +27,6 @@ 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 {
@@ -349,7 +349,7 @@ public abstract class Login {
}
int count = 1;
- final Matcher matcherViewstateCount = GCConstants.PATTERN_VIEWSTATEFIELDCOUNT.matcher(page);
+ final MatcherWrapper matcherViewstateCount = new MatcherWrapper(GCConstants.PATTERN_VIEWSTATEFIELDCOUNT, page);
if (matcherViewstateCount.find()) {
try {
count = Integer.parseInt(matcherViewstateCount.group(1));
@@ -361,14 +361,13 @@ public abstract class Login {
String[] viewstates = new String[count];
// Get the viewstates
- int no;
- final Matcher matcherViewstates = GCConstants.PATTERN_VIEWSTATES.matcher(page);
+ final MatcherWrapper matcherViewstates = new MatcherWrapper(GCConstants.PATTERN_VIEWSTATES, page);
while (matcherViewstates.find()) {
String sno = matcherViewstates.group(1); // number of viewstate
- if (sno.length() == 0) {
+ int no;
+ if (StringUtils.isEmpty(sno)) {
no = 0;
- }
- else {
+ } else {
try {
no = Integer.parseInt(sno);
} catch (NumberFormatException e) {
diff --git a/main/src/cgeo/geocaching/connector/gc/Tile.java b/main/src/cgeo/geocaching/connector/gc/Tile.java
index 5404446..73ded4d 100644
--- a/main/src/cgeo/geocaching/connector/gc/Tile.java
+++ b/main/src/cgeo/geocaching/connector/gc/Tile.java
@@ -17,6 +17,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
+import java.util.Locale;
import java.util.Set;
/**
@@ -99,7 +100,7 @@ public class Tile {
/**
* Calculate latitude/longitude for a given x/y position in this tile.
- *
+ *
* @see <a
* href="http://developers.cloudmade.com/projects/tiles/examples/convert-coordinates-to-tile-numbers">Cloudmade</a>
*/
@@ -115,7 +116,7 @@ public class Tile {
@Override
public String toString() {
- return String.format("(%d/%d), zoom=%d", tileX, tileY, zoomlevel);
+ return String.format(Locale.US, "(%d/%d), zoom=%d", tileX, tileY, zoomlevel);
}
/**
diff --git a/main/src/cgeo/geocaching/connector/gc/UTFGrid.java b/main/src/cgeo/geocaching/connector/gc/UTFGrid.java
index a4eeff5..6d20eb6 100644
--- a/main/src/cgeo/geocaching/connector/gc/UTFGrid.java
+++ b/main/src/cgeo/geocaching/connector/gc/UTFGrid.java
@@ -29,7 +29,7 @@ public final class UTFGrid {
}
/** Calculate from a list of positions (x/y) the coords */
- protected static UTFGridPosition getPositionInGrid(List<UTFGridPosition> positions) {
+ public static UTFGridPosition getPositionInGrid(List<UTFGridPosition> positions) {
int minX = GRID_MAXX;
int maxX = 0;
int minY = GRID_MAXY;
diff --git a/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java b/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java
index e34d277..5965fff 100644
--- a/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java
+++ b/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java
@@ -1,6 +1,7 @@
package cgeo.geocaching.connector.gc;
-import java.util.regex.Matcher;
+import cgeo.geocaching.utils.MatcherWrapper;
+
import java.util.regex.Pattern;
@@ -35,7 +36,7 @@ public final class UTFGridPosition {
* @return
*/
static UTFGridPosition fromString(String key) {
- final Matcher matcher = UTFGridPosition.PATTERN_JSON_KEY.matcher(key);
+ final MatcherWrapper matcher = new MatcherWrapper(UTFGridPosition.PATTERN_JSON_KEY, key);
try {
if (matcher.matches()) {
final int x = Integer.parseInt(matcher.group(1));
diff --git a/main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java b/main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java
new file mode 100644
index 0000000..d0c0e16
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java
@@ -0,0 +1,514 @@
+package cgeo.geocaching.connector.oc;
+
+import cgeo.geocaching.LogEntry;
+import cgeo.geocaching.Settings;
+import cgeo.geocaching.cgCache;
+import cgeo.geocaching.enumerations.CacheSize;
+import cgeo.geocaching.enumerations.CacheType;
+import cgeo.geocaching.enumerations.LogType;
+import cgeo.geocaching.geopoint.Geopoint;
+import cgeo.geocaching.utils.Log;
+
+import org.apache.commons.lang3.StringUtils;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import android.sax.Element;
+import android.sax.EndElementListener;
+import android.sax.EndTextElementListener;
+import android.sax.RootElement;
+import android.sax.StartElementListener;
+import android.util.Xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.regex.Pattern;
+
+public class OC11XMLParser {
+
+ private static Pattern STRIP_DATE = Pattern.compile("\\+0([0-9]){1}\\:00");
+
+ private static class CacheHolder {
+ public cgCache cache;
+ public String latitude;
+ public String longitude;
+ }
+
+ private static class CacheLog {
+ public String cacheId;
+ public LogEntry logEntry;
+ }
+
+ private static class CacheDescription {
+ public String cacheId;
+ public String shortDesc;
+ public String desc;
+ public String hint;
+ }
+
+ private static Date parseFullDate(final String date) {
+ final SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
+ ISO8601DATEFORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
+ final String strippedDate = STRIP_DATE.matcher(date).replaceAll("+0$100");
+ try {
+ return ISO8601DATEFORMAT.parse(strippedDate);
+ } catch (ParseException e) {
+ Log.e("OC11XMLParser.parseFullDate", e);
+ }
+ return null;
+ }
+
+ private static Date parseDayDate(final String date) {
+ final SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
+ ISO8601DATEFORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
+ final String strippedDate = STRIP_DATE.matcher(date).replaceAll("+0$100");
+ try {
+ return ISO8601DATEFORMAT.parse(strippedDate);
+ } catch (ParseException e) {
+ Log.e("OC11XMLParser.parseDayDate", e);
+ }
+ return null;
+ }
+
+ private static CacheSize getCacheSize(final String sizeId) {
+ int size = Integer.parseInt(sizeId);
+
+ switch (size) {
+ case 1:
+ return CacheSize.OTHER;
+ case 2:
+ return CacheSize.MICRO;
+ case 3:
+ return CacheSize.SMALL;
+ case 4:
+ return CacheSize.REGULAR;
+ case 5:
+ case 6:
+ return CacheSize.LARGE;
+ case 8:
+ return CacheSize.VIRTUAL;
+ default:
+ break;
+ }
+ return CacheSize.NOT_CHOSEN;
+ }
+
+ private static CacheType getCacheType(final String typeId) {
+ int type = Integer.parseInt(typeId);
+ switch (type) {
+ case 1: // Other/unbekannter Cachetyp
+ return CacheType.UNKNOWN;
+ case 2: // Trad./normaler Cache
+ return CacheType.TRADITIONAL;
+ case 3: // Multi/Multicache
+ return CacheType.MULTI;
+ case 4: // Virt./virtueller Cache
+ return CacheType.VIRTUAL;
+ case 5: // ICam./Webcam-Cache
+ return CacheType.WEBCAM;
+ case 6: // Event/Event-Cache
+ return CacheType.EVENT;
+ case 7: // Quiz/Rätselcache
+ return CacheType.MYSTERY;
+ case 8: // Math/Mathe-/Physikcache
+ return CacheType.MYSTERY;
+ case 9: // Moving/beweglicher Cache
+ return CacheType.UNKNOWN;
+ case 10: // Driv./Drive-In
+ return CacheType.TRADITIONAL;
+ }
+ return CacheType.UNKNOWN;
+ }
+
+ private static LogType getLogType(final int typeId) {
+ switch (typeId) {
+ case 1:
+ return LogType.FOUND_IT;
+ case 2:
+ return LogType.DIDNT_FIND_IT;
+ case 3:
+ return LogType.NOTE;
+ case 7:
+ return LogType.ATTENDED;
+ case 8:
+ return LogType.WILL_ATTEND;
+ }
+ return LogType.UNKNOWN;
+ }
+
+ private static void setCacheStatus(final int statusId, final cgCache cache) {
+
+ switch (statusId) {
+ case 1:
+ cache.setArchived(false);
+ cache.setDisabled(false);
+ break;
+ case 2:
+ cache.setArchived(false);
+ cache.setDisabled(true);
+ break;
+ default:
+ cache.setArchived(true);
+ cache.setDisabled(false);
+ break;
+ }
+ }
+
+ private static void resetCache(final CacheHolder cacheHolder) {
+ cacheHolder.cache = new cgCache(null);
+ cacheHolder.cache.setReliableLatLon(true);
+ cacheHolder.cache.setDescription(StringUtils.EMPTY);
+ cacheHolder.latitude = "0.0";
+ cacheHolder.longitude = "0.0";
+ }
+
+ private static void resetLog(final CacheLog log) {
+ log.cacheId = StringUtils.EMPTY;
+ log.logEntry = new LogEntry("", 0, LogType.UNKNOWN, "");
+ }
+
+ private static void resetDesc(final CacheDescription desc) {
+ desc.cacheId = StringUtils.EMPTY;
+ desc.shortDesc = StringUtils.EMPTY;
+ desc.desc = StringUtils.EMPTY;
+ desc.hint = StringUtils.EMPTY;
+ }
+
+ public static Collection<cgCache> parseCaches(final InputStream stream) throws IOException {
+
+ final int CACHE_PARSE_LIMIT = 250;
+
+ final Map<String, cgCache> caches = new HashMap<String, cgCache>();
+
+ final CacheHolder cacheHolder = new CacheHolder();
+ final CacheLog logHolder = new CacheLog();
+ final CacheDescription descHolder = new CacheDescription();
+
+ final RootElement root = new RootElement("oc11xml");
+ final Element cacheNode = root.getChild("cache");
+
+ // cache
+ cacheNode.setStartElementListener(new StartElementListener() {
+
+ @Override
+ public void start(Attributes attributes) {
+ resetCache(cacheHolder);
+ }
+
+ });
+
+ cacheNode.setEndElementListener(new EndElementListener() {
+
+ @Override
+ public void end() {
+ cgCache cache = cacheHolder.cache;
+ Geopoint coords = new Geopoint(cacheHolder.latitude, cacheHolder.longitude);
+ if (StringUtils.isNotBlank(cache.getGeocode())
+ && !coords.equals(Geopoint.ZERO)
+ && !cache.isArchived()
+ && caches.size() < CACHE_PARSE_LIMIT) {
+ cache.setCoords(coords);
+ caches.put(cache.getCacheId(), cache);
+ }
+ }
+ });
+
+ // cache.id
+ cacheNode.getChild("id").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ cacheHolder.cache.setCacheId(body);
+ }
+ });
+
+ // cache.longitude
+ cacheNode.getChild("longitude").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ String longitude = body.trim();
+ if (StringUtils.isNotBlank(longitude)) {
+ cacheHolder.longitude = longitude;
+ }
+ }
+ });
+
+ // cache.latitude
+ cacheNode.getChild("latitude").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ String latitude = body.trim();
+ if (StringUtils.isNotBlank(latitude)) {
+ cacheHolder.latitude = latitude;
+ }
+ }
+ });
+
+ // cache.name
+ cacheNode.getChild("name").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ final String content = body.trim();
+ cacheHolder.cache.setName(content);
+ }
+ });
+
+ // cache.waypoints[oc]
+ cacheNode.getChild("waypoints").setStartElementListener(new StartElementListener() {
+
+ @Override
+ public void start(Attributes attrs) {
+ if (attrs.getIndex("oc") > -1) {
+ cacheHolder.cache.setGeocode(attrs.getValue("oc"));
+ }
+ if (attrs.getIndex("gccom") > -1) {
+ String gccode = attrs.getValue("gccom");
+ if (!StringUtils.isBlank(gccode)) {
+ cacheHolder.cache.setDescription(String.format("Listed on geocaching com: <a href=\"http://coord.info/%s\">%s</a><br /><br />", gccode, gccode));
+ }
+ }
+ }
+ });
+
+ // cache.type[id]
+ cacheNode.getChild("type").setStartElementListener(new StartElementListener() {
+
+ @Override
+ public void start(Attributes attrs) {
+ if (attrs.getIndex("id") > -1) {
+ final String typeId = attrs.getValue("id");
+ cacheHolder.cache.setType(getCacheType(typeId));
+ }
+ }
+ });
+
+ // cache.status[id]
+ cacheNode.getChild("status").setStartElementListener(new StartElementListener() {
+
+ @Override
+ public void start(Attributes attrs) {
+ if (attrs.getIndex("id") > -1) {
+ try {
+ final int statusId = Integer.parseInt(attrs.getValue("id"));
+ setCacheStatus(statusId, cacheHolder.cache);
+ } catch (NumberFormatException e) {
+ Log.w(String.format("Failed to parse status of cache '%s'.", cacheHolder.cache.getGeocode()));
+ }
+ }
+ }
+ });
+
+ // cache.size[id]
+ cacheNode.getChild("size").setStartElementListener(new StartElementListener() {
+
+ @Override
+ public void start(Attributes attrs) {
+ if (attrs.getIndex("id") > -1) {
+ final String typeId = attrs.getValue("id");
+ cacheHolder.cache.setSize(getCacheSize(typeId));
+ }
+ }
+ });
+
+ // cache.difficulty
+ cacheNode.getChild("difficulty").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ final String content = body.trim();
+ cacheHolder.cache.setDifficulty(Float.valueOf(content));
+ }
+ });
+
+ // cache.terrain
+ cacheNode.getChild("terrain").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ final String content = body.trim();
+ cacheHolder.cache.setTerrain(Float.valueOf(content));
+ }
+ });
+
+ // cache.terrain
+ cacheNode.getChild("datehidden").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ final String content = body.trim();
+ cacheHolder.cache.setHidden(parseFullDate(content));
+ }
+ });
+
+ // cache.attributes.attribute
+ cacheNode.getChild("attributes").getChild("attribute").setEndTextElementListener(new EndTextElementListener() {
+ @Override
+ public void end(String body) {
+ if (StringUtils.isNotBlank(body)) {
+ cacheHolder.cache.getAttributes().add(body.trim());
+ }
+ }
+ });
+
+ // cachedesc
+ final Element cacheDesc = root.getChild("cachedesc");
+
+ cacheDesc.setStartElementListener(new StartElementListener() {
+
+ @Override
+ public void start(Attributes attributes) {
+ resetDesc(descHolder);
+ }
+ });
+
+ cacheDesc.setEndElementListener(new EndElementListener() {
+
+ @Override
+ public void end() {
+ final cgCache cache = caches.get(descHolder.cacheId);
+ if (cache != null) {
+ cache.setShortdesc(descHolder.shortDesc);
+ cache.setDescription(cache.getDescription() + descHolder.desc);
+ cache.setHint(descHolder.hint);
+ }
+ }
+ });
+
+ // cachedesc.cacheid
+ cacheDesc.getChild("cacheid").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ descHolder.cacheId = body;
+ }
+ });
+
+ // cachedesc.desc
+ cacheDesc.getChild("shortdesc").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ final String content = body.trim();
+ descHolder.shortDesc = content;
+ }
+ });
+
+ // cachedesc.desc
+ cacheDesc.getChild("desc").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ final String content = body.trim();
+ descHolder.desc = content;
+ }
+ });
+
+ // cachedesc.hint
+ cacheDesc.getChild("hint").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ final String content = body.trim();
+ descHolder.hint = content;
+ }
+ });
+
+ // cachelog
+ final Element cacheLog = root.getChild("cachelog");
+
+ cacheLog.setStartElementListener(new StartElementListener() {
+
+ @Override
+ public void start(Attributes attrs) {
+ resetLog(logHolder);
+ }
+ });
+
+ cacheLog.setEndElementListener(new EndElementListener() {
+
+ @Override
+ public void end() {
+ final cgCache cache = caches.get(logHolder.cacheId);
+ if (cache != null && logHolder.logEntry.type != LogType.UNKNOWN) {
+ cache.getLogs().prepend(logHolder.logEntry);
+ if (logHolder.logEntry.type == LogType.FOUND_IT
+ && StringUtils.equals(logHolder.logEntry.author, Settings.getOCConnectorUserName())) {
+ cache.setFound(true);
+ cache.setVisitedDate(logHolder.logEntry.date);
+ }
+ }
+ }
+ });
+
+ // cachelog.cacheid
+ cacheLog.getChild("cacheid").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ logHolder.cacheId = body;
+ }
+ });
+
+ // cachelog.date
+ cacheLog.getChild("date").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ try {
+ logHolder.logEntry.date = parseDayDate(body).getTime();
+ } catch (Exception e) {
+ Log.w("Failed to parse log date: " + e.toString());
+ }
+ }
+ });
+
+ // cachelog.logtype
+ cacheLog.getChild("logtype").setStartElementListener(new StartElementListener() {
+
+ @Override
+ public void start(Attributes attrs) {
+ if (attrs.getIndex("id") > -1) {
+ final int typeId = Integer.parseInt(attrs.getValue("id"));
+ logHolder.logEntry.type = getLogType(typeId);
+ }
+ }
+ });
+
+ // cachelog.userid
+ cacheLog.getChild("userid").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String finderName) {
+ logHolder.logEntry.author = finderName;
+ }
+ });
+
+ // cachelog.text
+ cacheLog.getChild("text").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String logText) {
+ logHolder.logEntry.log = logText;
+ }
+ });
+
+ try {
+ Xml.parse(stream, Xml.Encoding.UTF_8, root.getContentHandler());
+ return caches.values();
+ } catch (SAXException e) {
+ Log.e("Cannot parse .gpx file as oc11xml: could not parse XML - " + e.toString());
+ return null;
+ }
+ }
+}
diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java
index 17c961a..74968e7 100644
--- a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java
+++ b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java
@@ -34,4 +34,10 @@ public class OCApiConnector extends OCConnector implements ISearchByGeocode {
}
return new SearchResult(cache);
}
+
+ @Override
+ public boolean isActivated() {
+ // currently always active, but only for details download
+ return true;
+ }
}
diff --git a/main/src/cgeo/geocaching/connector/oc/OCConnector.java b/main/src/cgeo/geocaching/connector/oc/OCConnector.java
index c098d12..24fd7d6 100644
--- a/main/src/cgeo/geocaching/connector/oc/OCConnector.java
+++ b/main/src/cgeo/geocaching/connector/oc/OCConnector.java
@@ -2,6 +2,7 @@ package cgeo.geocaching.connector.oc;
import cgeo.geocaching.cgCache;
import cgeo.geocaching.connector.AbstractConnector;
+import cgeo.geocaching.enumerations.CacheRealm;
import java.util.regex.Pattern;
@@ -51,4 +52,9 @@ public class OCConnector extends AbstractConnector {
return "http://" + host + "/viewcache.php?wp=";
}
+ @Override
+ public CacheRealm getCacheRealm() {
+ return CacheRealm.OC;
+ }
+
}
diff --git a/main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java b/main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java
new file mode 100644
index 0000000..3a2f42e
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java
@@ -0,0 +1,52 @@
+package cgeo.geocaching.connector.oc;
+
+import cgeo.geocaching.SearchResult;
+import cgeo.geocaching.Settings;
+import cgeo.geocaching.cgCache;
+import cgeo.geocaching.connector.capability.ISearchByCenter;
+import cgeo.geocaching.connector.capability.ISearchByGeocode;
+import cgeo.geocaching.connector.capability.ISearchByViewPort;
+import cgeo.geocaching.geopoint.Geopoint;
+import cgeo.geocaching.geopoint.Viewport;
+import cgeo.geocaching.utils.CancellableHandler;
+
+public class OCXMLApiConnector extends OCConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort {
+
+ private final static double SEARCH_DISTANCE_LIMIT = 15.0;
+ private final static double NEARBY_SEARCH_DISTANCE = 5.0;
+
+ public OCXMLApiConnector(String name, String host, String prefix) {
+ super(name, host, prefix);
+ }
+
+ @Override
+ public SearchResult searchByGeocode(final String geocode, final String guid, CancellableHandler handler) {
+ final cgCache cache = OCXMLClient.getCache(geocode);
+ if (cache == null) {
+ return null;
+ }
+ return new SearchResult(cache);
+ }
+
+ @Override
+ public SearchResult searchByCenter(final Geopoint center) {
+ return new SearchResult(OCXMLClient.getCachesAround(center, NEARBY_SEARCH_DISTANCE));
+ }
+
+ @Override
+ public SearchResult searchByViewport(final Viewport viewport, final String[] tokens) {
+ final Geopoint center = viewport.getCenter();
+ double distance = center.distanceTo(viewport.bottomLeft) * 1.15;
+ if (distance > SEARCH_DISTANCE_LIMIT) {
+ distance = SEARCH_DISTANCE_LIMIT;
+ }
+ return new SearchResult(OCXMLClient.getCachesAround(center, distance));
+ }
+
+ @Override
+ public boolean isActivated() {
+ // currently only tested and working with oc.de
+ return Settings.isOCConnectorActive();
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/connector/oc/OCXMLClient.java b/main/src/cgeo/geocaching/connector/oc/OCXMLClient.java
new file mode 100644
index 0000000..26b42e3
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/oc/OCXMLClient.java
@@ -0,0 +1,110 @@
+package cgeo.geocaching.connector.oc;
+
+import cgeo.geocaching.cgCache;
+import cgeo.geocaching.cgData;
+import cgeo.geocaching.connector.ConnectorFactory;
+import cgeo.geocaching.connector.IConnector;
+import cgeo.geocaching.enumerations.LoadFlags;
+import cgeo.geocaching.geopoint.Geopoint;
+import cgeo.geocaching.geopoint.GeopointFormatter;
+import cgeo.geocaching.network.Network;
+import cgeo.geocaching.network.Parameters;
+import cgeo.geocaching.utils.Log;
+
+import ch.boye.httpclientandroidlib.HttpResponse;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.zip.GZIPInputStream;
+
+public class OCXMLClient {
+
+ private static final String SERVICE_CACHE = "/xml/ocxml11.php";
+
+ // Url for single cache requests
+ // http://www.opencaching.de/xml/ocxml11.php?modifiedsince=20060320000000&user=0&cache=1&cachedesc=1&cachelog=1&picture=1&removedobject=0&session=0&doctype=0&charset=utf-8&wp=OCC9BE
+
+ public static cgCache getCache(final String geoCode) {
+ try {
+ final Parameters params = getOCXmlQueryParameters(true, true);
+ params.put("wp", geoCode);
+ final InputStream data = request(ConnectorFactory.getConnector(geoCode), SERVICE_CACHE, params);
+
+ if (data == null) {
+ return null;
+ }
+
+ Collection<cgCache> caches = OC11XMLParser.parseCaches(new GZIPInputStream(data));
+ if (caches.iterator().hasNext()) {
+ cgCache cache = caches.iterator().next();
+ cache.setDetailed(true);
+ cgData.saveCache(cache, LoadFlags.SAVE_ALL);
+ return cache;
+ }
+ return null;
+ } catch (IOException e) {
+ Log.e("Error parsing cache '" + geoCode + "': " + e.toString());
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Collection<cgCache> getCachesAround(final Geopoint center, final double distance) {
+ try {
+ final Parameters params = getOCXmlQueryParameters(false, false);
+ params.put("lat", GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, center));
+ params.put("lon", GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, center));
+ params.put("distance", String.format(Locale.US, "%f", distance));
+ final InputStream data = request(ConnectorFactory.getConnector("OCXXX"), SERVICE_CACHE, params);
+
+ if (data == null) {
+ return CollectionUtils.EMPTY_COLLECTION;
+ }
+
+ return OC11XMLParser.parseCaches(new GZIPInputStream(data));
+ } catch (IOException e) {
+ Log.e("Error parsing nearby search result: " + e.toString());
+ return CollectionUtils.EMPTY_COLLECTION;
+ }
+ }
+
+ private static InputStream request(final IConnector connector, final String service, final Parameters params) {
+ if (connector == null) {
+ return null;
+ }
+ if (!(connector instanceof OCXMLApiConnector)) {
+ return null;
+ }
+
+ final String host = connector.getHost();
+ if (StringUtils.isBlank(host)) {
+ return null;
+ }
+
+ final String uri = "http://" + host + service;
+ HttpResponse resp = Network.getRequest(uri, params);
+ if (resp != null) {
+ try {
+ return resp.getEntity().getContent();
+ } catch (IllegalStateException e) {
+ // fall through and return null
+ } catch (IOException e) {
+ // fall through and return null
+ }
+ }
+ return null;
+ }
+
+ private static Parameters getOCXmlQueryParameters(final boolean withDescription, final boolean withLogs) {
+ final Parameters params = new Parameters("modifiedsince", "20060320000000",
+ "user", "0", "cache", "1", "cachedesc", withDescription ? "1" : "0",
+ "cachelog", withLogs ? "1" : "0", "picture", "0", "removedobject", "0",
+ "session", "0", "doctype", "0", "charset", "utf-8", "zip", "gzip");
+ return params;
+ }
+}
diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
index 87cc3a1..1121cc5 100644
--- a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
+++ b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
@@ -2,8 +2,8 @@ package cgeo.geocaching.connector.oc;
import cgeo.geocaching.LogEntry;
import cgeo.geocaching.cgCache;
+import cgeo.geocaching.cgData;
import cgeo.geocaching.cgImage;
-import cgeo.geocaching.cgeoapplication;
import cgeo.geocaching.connector.ConnectorFactory;
import cgeo.geocaching.connector.IConnector;
import cgeo.geocaching.enumerations.CacheSize;
@@ -153,9 +153,8 @@ final public class OkapiClient {
final JSONArray images = response.getJSONArray(CACHE_IMAGES);
if (images != null) {
- JSONObject imageResponse;
for (int i = 0; i < images.length(); i++) {
- imageResponse = images.getJSONObject(i);
+ JSONObject imageResponse = images.getJSONObject(i);
if (imageResponse.getBoolean(CACHE_IMAGE_IS_SPOILER)) {
final String title = imageResponse.getString(CACHE_IMAGE_CAPTION);
final String url = absoluteUrl(imageResponse.getString(CACHE_IMAGE_URL), cache.getGeocode());
@@ -171,9 +170,8 @@ final public class OkapiClient {
cache.setUpdated(System.currentTimeMillis());
cache.setDetailedUpdate(cache.getUpdated());
cache.setDetailed(true);
-
// save full detailed caches
- cgeoapplication.getInstance().saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB));
+ cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB));
} catch (JSONException e) {
Log.e("OkapiClient.parseCache", e);
}