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.java65
-rw-r--r--main/src/cgeo/geocaching/connector/ConnectorFactory.java85
-rw-r--r--main/src/cgeo/geocaching/connector/IConnector.java84
-rw-r--r--main/src/cgeo/geocaching/connector/ILoggingManager.java45
-rw-r--r--main/src/cgeo/geocaching/connector/ImageResult.java23
-rw-r--r--main/src/cgeo/geocaching/connector/LogResult.java23
-rw-r--r--main/src/cgeo/geocaching/connector/NoLoggingManager.java46
-rw-r--r--main/src/cgeo/geocaching/connector/UnknownConnector.java3
-rw-r--r--main/src/cgeo/geocaching/connector/WaymarkingConnector.java40
-rw-r--r--main/src/cgeo/geocaching/connector/capability/ILogin.java57
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCConnector.java118
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCConstants.java21
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java132
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCMap.java29
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCParser.java385
-rw-r--r--main/src/cgeo/geocaching/connector/gc/IconDecoder.java18
-rw-r--r--main/src/cgeo/geocaching/connector/gc/Login.java95
-rw-r--r--main/src/cgeo/geocaching/connector/gc/SearchHandler.java2
-rw-r--r--main/src/cgeo/geocaching/connector/gc/Tile.java7
-rw-r--r--main/src/cgeo/geocaching/connector/oc/AttributeParser.java327
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java754
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCApiConnector.java41
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java139
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java107
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCConnector.java13
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java67
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCXMLClient.java115
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OkapiClient.java512
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java70
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OkapiService.java21
-rw-r--r--main/src/cgeo/geocaching/connector/oc/UserInfo.java41
-rw-r--r--main/src/cgeo/geocaching/connector/ox/OXConnector.java2
-rw-r--r--main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java10
-rw-r--r--main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java42
-rw-r--r--main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java82
-rw-r--r--main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java19
-rw-r--r--main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java50
-rw-r--r--main/src/cgeo/geocaching/connector/trackable/UnknownTrackableConnector.java24
38 files changed, 2384 insertions, 1330 deletions
diff --git a/main/src/cgeo/geocaching/connector/AbstractConnector.java b/main/src/cgeo/geocaching/connector/AbstractConnector.java
index 413291c..28ad12b 100644
--- a/main/src/cgeo/geocaching/connector/AbstractConnector.java
+++ b/main/src/cgeo/geocaching/connector/AbstractConnector.java
@@ -1,11 +1,13 @@
package cgeo.geocaching.connector;
import cgeo.geocaching.Geocache;
-import cgeo.geocaching.enumerations.CacheRealm;
+import cgeo.geocaching.R;
import cgeo.geocaching.geopoint.Geopoint;
import org.apache.commons.lang3.StringUtils;
+import android.app.Activity;
+
public abstract class AbstractConnector implements IConnector {
@Override
@@ -19,17 +21,30 @@ public abstract class AbstractConnector implements IConnector {
}
@Override
+ public boolean addToWatchlist(Geocache cache) {
+ return false;
+ }
+
+ @Override
+ public boolean removeFromWatchlist(Geocache cache) {
+ return false;
+ }
+
+ @Override
+ public boolean supportsPersonalNote() {
+ return false;
+ }
+
+ @Override
+ public boolean uploadPersonalNote(Geocache cache) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean supportsOwnCoordinates() {
return false;
}
- /**
- * Uploading modified coordinates to website
- *
- * @param cache
- * @param wpt
- * @return success
- */
@Override
public boolean uploadModifiedCoordinates(Geocache cache, Geopoint wpt) {
throw new UnsupportedOperationException();
@@ -54,6 +69,21 @@ public abstract class AbstractConnector implements IConnector {
}
@Override
+ public boolean supportsLogImages() {
+ return false;
+ }
+
+ @Override
+ public boolean canLog(Geocache cache) {
+ return false;
+ }
+
+ @Override
+ public ILoggingManager getLoggingManager(Activity activity, Geocache cache) {
+ return new NoLoggingManager();
+ }
+
+ @Override
public String getLicenseText(final Geocache cache) {
return null;
}
@@ -84,11 +114,6 @@ public abstract class AbstractConnector implements IConnector {
}
@Override
- public String[] getTokens() {
- return null;
- }
-
- @Override
public String getGeocodeFromUrl(final String url) {
final String urlPrefix = getCacheUrlPrefix();
if (StringUtils.startsWith(url, urlPrefix)) {
@@ -111,15 +136,15 @@ public abstract class AbstractConnector implements IConnector {
* {@link IConnector}
*/
@Override
- public CacheRealm getCacheRealm() {
- return CacheRealm.OTHER;
+ public boolean isActivated() {
+ return false;
}
- /**
- * {@link IConnector}
- */
@Override
- public boolean isActivated() {
- return false;
+ public int getCacheMapMarkerId(boolean disabled) {
+ if (disabled) {
+ return R.drawable.marker_disabled_other;
+ }
+ return R.drawable.marker_other;
}
}
diff --git a/main/src/cgeo/geocaching/connector/ConnectorFactory.java b/main/src/cgeo/geocaching/connector/ConnectorFactory.java
index 561bae2..eb09978 100644
--- a/main/src/cgeo/geocaching/connector/ConnectorFactory.java
+++ b/main/src/cgeo/geocaching/connector/ConnectorFactory.java
@@ -1,15 +1,22 @@
package cgeo.geocaching.connector;
import cgeo.geocaching.ICache;
+import cgeo.geocaching.R;
import cgeo.geocaching.SearchResult;
import cgeo.geocaching.Trackable;
+import cgeo.geocaching.connector.capability.ILogin;
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.OCApiConnector.ApiSupport;
+import cgeo.geocaching.connector.oc.OCApiLiveConnector;
import cgeo.geocaching.connector.oc.OCConnector;
-import cgeo.geocaching.connector.oc.OCXMLApiConnector;
import cgeo.geocaching.connector.ox.OXConnector;
+import cgeo.geocaching.connector.trackable.GeokretyConnector;
+import cgeo.geocaching.connector.trackable.TrackableConnector;
+import cgeo.geocaching.connector.trackable.TravelBugConnector;
+import cgeo.geocaching.connector.trackable.UnknownTrackableConnector;
import cgeo.geocaching.geopoint.Viewport;
import org.apache.commons.lang3.StringUtils;
@@ -19,39 +26,47 @@ import java.util.List;
public final class ConnectorFactory {
private static final UnknownConnector UNKNOWN_CONNECTOR = new UnknownConnector();
- private static final IConnector[] connectors = new IConnector[] {
+ private static final IConnector[] CONNECTORS = new IConnector[] {
GCConnector.getInstance(),
- new OCXMLApiConnector("OpenCaching.DE", "www.opencaching.de", "OC"),
+ new OCApiLiveConnector("opencaching.de", "www.opencaching.de", "OC", R.string.oc_de_okapi_consumer_key, R.string.oc_de_okapi_consumer_secret, ApiSupport.current),
new OCConnector("OpenCaching.CZ", "www.opencaching.cz", "OZ"),
- new OCApiConnector("OpenCaching.CO.UK", "www.opencaching.org.uk", "OK", "arU4okouc4GEjMniE2fq"),
+ new OCApiConnector("OpenCaching.CO.UK", "www.opencaching.org.uk", "OK", "arU4okouc4GEjMniE2fq", ApiSupport.oldapi),
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.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"),
+ new OCApiConnector("OpenCaching.NL", "www.opencaching.nl", "OB", "PdzU8jzIlcfMADXaYN8j", ApiSupport.current),
+ new OCApiConnector("OpenCaching.PL", "www.opencaching.pl", "OP", "GkxM47WkUkLQXXsZ9qSh", ApiSupport.current),
+ new OCApiConnector("OpenCaching.US", "www.opencaching.us", "OU", "pTsYAYSXFcfcRQnYE6uA", ApiSupport.oldapi),
new OXConnector(),
new GeocachingAustraliaConnector(),
new GeopeitusConnector(),
+ new WaymarkingConnector(),
UNKNOWN_CONNECTOR // the unknown connector MUST be the last one
};
+ public static final UnknownTrackableConnector UNKNOWN_TRACKABLE_CONNECTOR = new UnknownTrackableConnector();
+ private static final TrackableConnector[] TRACKABLE_CONNECTORS = new TrackableConnector[] {
+ new GeokretyConnector(), // GK must be first, as it overlaps with the secret codes of travel bugs
+ TravelBugConnector.getInstance(),
+ UNKNOWN_TRACKABLE_CONNECTOR // must be last
+ };
+
private static final ISearchByViewPort[] searchByViewPortConns;
private static final ISearchByCenter[] searchByCenterConns;
static {
- List<ISearchByViewPort> vpConns = new ArrayList<ISearchByViewPort>();
- for (IConnector conn : connectors) {
+ final List<ISearchByViewPort> vpConns = new ArrayList<ISearchByViewPort>();
+ for (final IConnector conn : CONNECTORS) {
if (conn instanceof ISearchByViewPort) {
vpConns.add((ISearchByViewPort) conn);
}
}
searchByViewPortConns = vpConns.toArray(new ISearchByViewPort[vpConns.size()]);
- List<ISearchByCenter> centerConns = new ArrayList<ISearchByCenter>();
- for (IConnector conn : connectors) {
+ final List<ISearchByCenter> centerConns = new ArrayList<ISearchByCenter>();
+ for (final IConnector conn : CONNECTORS) {
// GCConnector is handled specially, omit it here!
if (conn instanceof ISearchByCenter && !(conn instanceof GCConnector)) {
centerConns.add((ISearchByCenter) conn);
@@ -61,18 +76,28 @@ public final class ConnectorFactory {
}
public static IConnector[] getConnectors() {
- return connectors;
+ return CONNECTORS;
}
public static ISearchByCenter[] getSearchByCenterConnectors() {
return searchByCenterConns;
}
+ public static ILogin[] getActiveLiveConnectors() {
+ final List<ILogin> liveConns = new ArrayList<ILogin>();
+ for (final IConnector conn : CONNECTORS) {
+ if (conn instanceof ILogin && conn.isActivated()) {
+ liveConns.add((ILogin) conn);
+ }
+ }
+ return liveConns.toArray(new ILogin[liveConns.size()]);
+ }
+
public static boolean canHandle(final String geocode) {
if (isInvalidGeocode(geocode)) {
return false;
}
- for (IConnector connector : connectors) {
+ for (final IConnector connector : CONNECTORS) {
if (connector.canHandle(geocode)) {
return true;
}
@@ -84,8 +109,17 @@ public final class ConnectorFactory {
return getConnector(cache.getGeocode());
}
- public static IConnector getConnector(Trackable trackable) {
- return getConnector(trackable.getGeocode());
+ public static TrackableConnector getConnector(Trackable trackable) {
+ return getTrackableConnector(trackable.getGeocode());
+ }
+
+ public static TrackableConnector getTrackableConnector(String geocode) {
+ for (final TrackableConnector connector : TRACKABLE_CONNECTORS) {
+ if (connector.canHandleTrackable(geocode)) {
+ return connector;
+ }
+ }
+ return UNKNOWN_TRACKABLE_CONNECTOR; // avoid null checks by returning a non implementing connector
}
public static IConnector getConnector(final String geocodeInput) {
@@ -94,12 +128,12 @@ public final class ConnectorFactory {
if (isInvalidGeocode(geocode)) {
return UNKNOWN_CONNECTOR;
}
- for (IConnector connector : connectors) {
+ for (final IConnector connector : CONNECTORS) {
if (connector.canHandle(geocode)) {
return connector;
}
}
- // in case of errors, take UNKNOWN
+ // in case of errors, take UNKNOWN to avoid null checks everywhere
return UNKNOWN_CONNECTOR;
}
@@ -110,21 +144,18 @@ public final class ConnectorFactory {
/** @see ISearchByViewPort#searchByViewport */
public static SearchResult searchByViewport(final Viewport viewport, final String[] tokens) {
- SearchResult result = new SearchResult();
- for (ISearchByViewPort vpconn : searchByViewPortConns) {
+ final SearchResult result = new SearchResult();
+ for (final ISearchByViewPort vpconn : searchByViewPortConns) {
if (vpconn.isActivated()) {
- SearchResult temp = vpconn.searchByViewport(viewport, tokens);
- if (temp != null) {
- result.addGeocodes(temp.getGeocodes());
- }
+ result.addSearchResult(vpconn.searchByViewport(viewport, tokens));
}
}
return result;
}
public static String getGeocodeFromURL(final String url) {
- for (IConnector connector : connectors) {
- String geocode = connector.getGeocodeFromUrl(url);
+ for (final IConnector connector : CONNECTORS) {
+ final String geocode = connector.getGeocodeFromUrl(url);
if (StringUtils.isNotBlank(geocode)) {
return geocode;
}
@@ -132,4 +163,8 @@ public final class ConnectorFactory {
return null;
}
+ public static TrackableConnector[] getTrackableConnectors() {
+ return TRACKABLE_CONNECTORS;
+ }
+
}
diff --git a/main/src/cgeo/geocaching/connector/IConnector.java b/main/src/cgeo/geocaching/connector/IConnector.java
index 9169b4a..0c175cd 100644
--- a/main/src/cgeo/geocaching/connector/IConnector.java
+++ b/main/src/cgeo/geocaching/connector/IConnector.java
@@ -2,9 +2,10 @@ package cgeo.geocaching.connector;
import cgeo.geocaching.Geocache;
import cgeo.geocaching.ICache;
-import cgeo.geocaching.enumerations.CacheRealm;
import cgeo.geocaching.geopoint.Geopoint;
+import android.app.Activity;
+
public interface IConnector {
/**
* get name for display (currently only used in links)
@@ -45,6 +46,22 @@ public interface IConnector {
public boolean supportsWatchList();
/**
+ * Add the cache to the watchlist
+ *
+ * @param cache
+ * @return True - success/False - failure
+ */
+ public boolean addToWatchlist(Geocache cache);
+
+ /**
+ * Remove the cache from the watchlist
+ *
+ * @param cache
+ * @return True - success/False - failure
+ */
+ public boolean removeFromWatchlist(Geocache cache);
+
+ /**
* enable/disable favorite points controls in cache details
*
* @return
@@ -59,6 +76,20 @@ public interface IConnector {
public boolean supportsLogging();
/**
+ * enable/disable attaching image to log
+ *
+ * @return
+ */
+ public boolean supportsLogImages();
+
+ /**
+ * Get an ILoggingManager to guide the logging process.
+ *
+ * @return
+ */
+ public ILoggingManager getLoggingManager(Activity activity, Geocache cache);
+
+ /**
* get host name of the connector server for dynamic loading of data
*
* @return
@@ -98,13 +129,6 @@ public interface IConnector {
public boolean isReliableLatLon(boolean cacheHasReliableLatLon);
/**
- * Return required tokens for specific following actions
- *
- * @return
- */
- public String[] getTokens();
-
- /**
* extract a geocode from the given URL, if this connector can handle that URL somehow
*
* @param url
@@ -113,23 +137,29 @@ public interface IConnector {
public String getGeocodeFromUrl(final String url);
/**
- * enable/disable uploading modified coordinates to website
+ * enable/disable uploading personal note
*
* @return true, when uploading is possible
*/
- public boolean supportsOwnCoordinates();
+ public boolean supportsPersonalNote();
/**
- * Uploading modified coordinates to website
+ * Uploading personal note to website
*
* @param cache
- * @param wpt
* @return success
*/
- public boolean uploadModifiedCoordinates(Geocache cache, Geopoint wpt);
+ public boolean uploadPersonalNote(Geocache cache);
/**
- * Reseting of modified coordinates on website to details
+ * enable/disable uploading modified coordinates to website
+ *
+ * @return true, when uploading is possible
+ */
+ public boolean supportsOwnCoordinates();
+
+ /**
+ * Resetting of modified coordinates on website to details
*
* @param cache
* @return success
@@ -137,11 +167,13 @@ public interface IConnector {
public boolean deleteModifiedCoordinates(Geocache cache);
/**
- * The CacheRealm this cache belongs to
+ * Uploading modified coordinates to website
*
- * @return
+ * @param cache
+ * @param wpt
+ * @return success
*/
- public CacheRealm getCacheRealm();
+ public boolean uploadModifiedCoordinates(Geocache cache, Geopoint wpt);
/**
* Return true if this connector is activated for online
@@ -159,4 +191,22 @@ public interface IConnector {
* @return <code>true</code> if the current user is the cache owner, <code>false</code> otherwise
*/
public boolean isOwner(final ICache cache);
+
+ /**
+ * Check if the cache information is complete enough to be
+ * able to log online.
+ *
+ * @param geocache
+ * @return
+ */
+ public boolean canLog(Geocache geocache);
+
+ /**
+ * Return the marker id of the caches for this connector. This creates the different backgrounds for cache markers
+ * on the map.
+ *
+ * @param disabled
+ * Whether to return the enabled or disabled marker type
+ */
+ public int getCacheMapMarkerId(boolean disabled);
}
diff --git a/main/src/cgeo/geocaching/connector/ILoggingManager.java b/main/src/cgeo/geocaching/connector/ILoggingManager.java
new file mode 100644
index 0000000..c5586b3
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/ILoggingManager.java
@@ -0,0 +1,45 @@
+package cgeo.geocaching.connector;
+
+import cgeo.geocaching.Geocache;
+import cgeo.geocaching.TrackableLog;
+import cgeo.geocaching.enumerations.LogType;
+
+import android.net.Uri;
+
+import java.util.Calendar;
+import java.util.List;
+
+public interface ILoggingManager {
+
+ /**
+ * Post a log for a cache online
+ *
+ * @param cache
+ * @param logType
+ * @param date
+ * @param log
+ * @param logPassword
+ * optional, maybe null
+ * @param trackableLogs
+ * @return
+ */
+ LogResult postLog(Geocache cache,
+ LogType logType,
+ Calendar date,
+ String log,
+ String logPassword,
+ List<TrackableLog> trackableLogs);
+
+ ImageResult postLogImage(String logId,
+ String imageCaption,
+ String imageDescription,
+ Uri imageUri);
+
+ public boolean hasLoaderError();
+
+ public List<TrackableLog> getTrackables();
+
+ public List<LogType> getPossibleLogTypes();
+
+ public void init();
+}
diff --git a/main/src/cgeo/geocaching/connector/ImageResult.java b/main/src/cgeo/geocaching/connector/ImageResult.java
new file mode 100644
index 0000000..9314cad
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/ImageResult.java
@@ -0,0 +1,23 @@
+package cgeo.geocaching.connector;
+
+import cgeo.geocaching.enumerations.StatusCode;
+
+public class ImageResult {
+
+ private final StatusCode postResult;
+ private final String imageUri;
+
+ public ImageResult(StatusCode postResult, String imageUri) {
+ this.postResult = postResult;
+ this.imageUri = imageUri;
+ }
+
+ public StatusCode getPostResult() {
+ return postResult;
+ }
+
+ public String getImageUri() {
+ return imageUri;
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/connector/LogResult.java b/main/src/cgeo/geocaching/connector/LogResult.java
new file mode 100644
index 0000000..62111a4
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/LogResult.java
@@ -0,0 +1,23 @@
+package cgeo.geocaching.connector;
+
+import cgeo.geocaching.enumerations.StatusCode;
+
+public class LogResult {
+
+ private final StatusCode postLogResult;
+ private final String logId;
+
+ public LogResult(StatusCode postLogResult, String logId) {
+ this.postLogResult = postLogResult;
+ this.logId = logId;
+ }
+
+ public StatusCode getPostLogResult() {
+ return postLogResult;
+ }
+
+ public String getLogId() {
+ return logId;
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/connector/NoLoggingManager.java b/main/src/cgeo/geocaching/connector/NoLoggingManager.java
new file mode 100644
index 0000000..04a73c1
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/NoLoggingManager.java
@@ -0,0 +1,46 @@
+package cgeo.geocaching.connector;
+
+import cgeo.geocaching.Geocache;
+import cgeo.geocaching.TrackableLog;
+import cgeo.geocaching.enumerations.LogType;
+import cgeo.geocaching.enumerations.StatusCode;
+
+import android.net.Uri;
+
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.List;
+
+public class NoLoggingManager implements ILoggingManager {
+
+ @Override
+ public void init() {
+ // nothing to do
+ }
+
+ @Override
+ public LogResult postLog(Geocache cache, LogType logType, Calendar date, String log, String logPassword, List<TrackableLog> trackableLogs) {
+ return new LogResult(StatusCode.LOG_POST_ERROR, "");
+ }
+
+ @Override
+ public ImageResult postLogImage(String logId, String imageCaption, String imageDescription, Uri imageUri) {
+ return new ImageResult(StatusCode.LOG_POST_ERROR, "");
+ }
+
+ @Override
+ public boolean hasLoaderError() {
+ return true;
+ }
+
+ @Override
+ public List<TrackableLog> getTrackables() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List<LogType> getPossibleLogTypes() {
+ return Collections.emptyList();
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/connector/UnknownConnector.java b/main/src/cgeo/geocaching/connector/UnknownConnector.java
index b6fc29a..e9fecb9 100644
--- a/main/src/cgeo/geocaching/connector/UnknownConnector.java
+++ b/main/src/cgeo/geocaching/connector/UnknownConnector.java
@@ -1,7 +1,7 @@
package cgeo.geocaching.connector;
-import cgeo.geocaching.ICache;
import cgeo.geocaching.Geocache;
+import cgeo.geocaching.ICache;
import org.apache.commons.lang3.StringUtils;
@@ -36,4 +36,5 @@ public class UnknownConnector extends AbstractConnector {
protected String getCacheUrlPrefix() {
return null;
}
+
}
diff --git a/main/src/cgeo/geocaching/connector/WaymarkingConnector.java b/main/src/cgeo/geocaching/connector/WaymarkingConnector.java
new file mode 100644
index 0000000..f184f6e
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/WaymarkingConnector.java
@@ -0,0 +1,40 @@
+package cgeo.geocaching.connector;
+
+import cgeo.geocaching.Geocache;
+import cgeo.geocaching.ICache;
+
+import org.apache.commons.lang3.StringUtils;
+
+public class WaymarkingConnector extends AbstractConnector {
+
+ @Override
+ public String getName() {
+ return "Waymarking";
+ }
+
+ @Override
+ public String getCacheUrl(Geocache cache) {
+ return getCacheUrlPrefix() + cache.getGeocode();
+ }
+
+ @Override
+ public String getHost() {
+ return "www.waymarking.com";
+ }
+
+ @Override
+ public boolean isOwner(ICache cache) {
+ // this connector has no user management
+ return false;
+ }
+
+ @Override
+ protected String getCacheUrlPrefix() {
+ return "http://" + getHost() + "/waymarks/";
+ }
+
+ @Override
+ public boolean canHandle(String geocode) {
+ return StringUtils.startsWith(geocode, "WM");
+ }
+}
diff --git a/main/src/cgeo/geocaching/connector/capability/ILogin.java b/main/src/cgeo/geocaching/connector/capability/ILogin.java
new file mode 100644
index 0000000..4a839c8
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/capability/ILogin.java
@@ -0,0 +1,57 @@
+package cgeo.geocaching.connector.capability;
+
+import cgeo.geocaching.connector.IConnector;
+
+import android.content.Context;
+import android.os.Handler;
+
+public interface ILogin extends IConnector {
+
+
+ /**
+ * Contacts the server the connector belongs to
+ * and verifies/establishes authentication and
+ * retrieves information about the current user
+ * (Name, found caches) if applicable.
+ *
+ * @param handler
+ * Handler to receive status feedback
+ * @param fromActivity
+ * Calling activity context
+ * @return true in case of success, false in case of failure
+ */
+ boolean login(Handler handler, Context fromActivity);
+
+ /**
+ * Returns the status of the last {@link}login() request
+ *
+ * @return
+ */
+ boolean isLoggedIn();
+
+ /**
+ * User-centered string describing the current login/connection status
+ *
+ * @return
+ */
+ String getLoginStatusString();
+
+ /**
+ * Name the user has in this connector or empty string if not applicable
+ * It might be necessary to execute login before this information is valid.
+ *
+ * @return
+ */
+ String getUserName();
+
+ /**
+ * Number of caches the user has found in this connector
+ * Normally retrieved/updated with (@see login).
+ * Might be out dated as changes on the connectors site
+ * are generally not notified.
+ *
+ * @return
+ */
+ int getCachesFound();
+
+}
diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java
index 50bf096..44d6e8f 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java
@@ -4,30 +4,46 @@ import cgeo.geocaching.Geocache;
import cgeo.geocaching.ICache;
import cgeo.geocaching.R;
import cgeo.geocaching.SearchResult;
-import cgeo.geocaching.Settings;
import cgeo.geocaching.cgData;
+import cgeo.geocaching.cgeoapplication;
import cgeo.geocaching.connector.AbstractConnector;
+import cgeo.geocaching.connector.ILoggingManager;
+import cgeo.geocaching.connector.capability.ILogin;
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;
+import cgeo.geocaching.settings.SettingsActivity;
+import cgeo.geocaching.settings.Settings;
import cgeo.geocaching.utils.CancellableHandler;
import cgeo.geocaching.utils.Log;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
+import android.app.Activity;
+import android.content.Context;
+import android.os.Handler;
+
import java.util.regex.Pattern;
-public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort {
+public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort, ILogin {
private static final String CACHE_URL_SHORT = "http://coord.info/";
// Double slash is used to force open in browser
private static final String CACHE_URL_LONG = "http://www.geocaching.com//seek/cache_details.aspx?wp=";
- private static final Pattern gpxZipFilePattern = Pattern.compile("\\d{7,}(_.+)?\\.zip", Pattern.CASE_INSENSITIVE);
+ /**
+ * Pocket queries downloaded from the website use a numeric prefix. The pocket query creator Android app adds a
+ * verbatim "pocketquery" prefix.
+ */
+ private static final Pattern GPX_ZIP_FILE_PATTERN = Pattern.compile("((\\d{7,})|(pocketquery))" + "(_.+)?" + "\\.zip", Pattern.CASE_INSENSITIVE);
+
+ /**
+ * Pattern for GC codes
+ */
+ private final static Pattern PATTERN_GC_CODE = Pattern.compile("GC[0-9A-Z]+", Pattern.CASE_INSENSITIVE);
private GCConnector() {
// singleton
@@ -49,7 +65,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
if (geocode == null) {
return false;
}
- return GCConstants.PATTERN_GC_CODE.matcher(geocode).matches() || GCConstants.PATTERN_TB_CODE.matcher(geocode).matches();
+ return GCConnector.PATTERN_GC_CODE.matcher(geocode).matches();
}
@Override
@@ -63,6 +79,11 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
}
@Override
+ public boolean supportsPersonalNote() {
+ return true;
+ }
+
+ @Override
public boolean supportsOwnCoordinates() {
return true;
}
@@ -78,8 +99,23 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
}
@Override
+ public boolean supportsLogImages() {
+ return true;
+ }
+
+ @Override
+ public ILoggingManager getLoggingManager(Activity activity, Geocache cache) {
+ return new GCLoggingManager(activity, cache);
+ }
+
+ @Override
+ public boolean canLog(Geocache cache) {
+ return StringUtils.isNotBlank(cache.getCacheId());
+ }
+
+ @Override
public String getName() {
- return "GeoCaching.com";
+ return "geocaching.com";
}
@Override
@@ -135,7 +171,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
@Override
public boolean isZippedGPXFile(final String fileName) {
- return gpxZipFilePattern.matcher(fileName).matches();
+ return GPX_ZIP_FILE_PATTERN.matcher(fileName).matches();
}
@Override
@@ -149,7 +185,8 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
}
- public static boolean addToWatchlist(Geocache cache) {
+ @Override
+ public boolean addToWatchlist(Geocache cache) {
final boolean added = GCParser.addToWatchlist(cache);
if (added) {
cgData.saveChangedCache(cache);
@@ -157,7 +194,8 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
return added;
}
- public static boolean removeFromWatchlist(Geocache cache) {
+ @Override
+ public boolean removeFromWatchlist(Geocache cache) {
final boolean removed = GCParser.removeFromWatchlist(cache);
if (removed) {
cgData.saveChangedCache(cache);
@@ -218,6 +256,15 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
}
@Override
+ public boolean uploadPersonalNote(Geocache cache) {
+ final boolean uploaded = GCParser.uploadPersonalNote(cache);
+ if (uploaded) {
+ cgData.saveChangedCache(cache);
+ }
+ return uploaded;
+ }
+
+ @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;
@@ -234,12 +281,57 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
}
@Override
- public CacheRealm getCacheRealm() {
- return CacheRealm.GC;
+ public boolean isActivated() {
+ return Settings.isGCConnectorActive();
}
@Override
- public boolean isActivated() {
- return true;
+ public int getCacheMapMarkerId(boolean disabled) {
+ if (disabled) {
+ return R.drawable.marker_disabled;
+ }
+ return R.drawable.marker;
+ }
+
+ @Override
+ public boolean login(Handler handler, Context fromActivity) {
+ // login
+ final StatusCode status = Login.login();
+
+ if (status == StatusCode.NO_ERROR) {
+ cgeoapplication.getInstance().firstRun = false;
+ Login.detectGcCustomDate();
+ }
+
+ if (cgeoapplication.getInstance().showLoginToast && handler != null) {
+ handler.sendMessage(handler.obtainMessage(0, status));
+ cgeoapplication.getInstance().showLoginToast = false;
+
+ // invoke settings activity to insert login details
+ if (status == StatusCode.NO_LOGIN_INFO_STORED && fromActivity != null) {
+ SettingsActivity.startWithServicesPage(fromActivity);
+ }
+ }
+ return status == StatusCode.NO_ERROR;
+ }
+
+ @Override
+ public String getUserName() {
+ return Login.getActualUserName();
+ }
+
+ @Override
+ public int getCachesFound() {
+ return Login.getActualCachesFound();
+ }
+
+ @Override
+ public String getLoginStatusString() {
+ return Login.getActualStatus();
+ }
+
+ @Override
+ public boolean isLoggedIn() {
+ return Login.isActualLoginStatus();
}
}
diff --git a/main/src/cgeo/geocaching/connector/gc/GCConstants.java b/main/src/cgeo/geocaching/connector/gc/GCConstants.java
index b66acb7..d908b47 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCConstants.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCConstants.java
@@ -36,7 +36,7 @@ public final class GCConstants {
public final static Pattern PATTERN_LATLON = Pattern.compile("<span id=\"uxLatLon\"[^>]*>(.*?)</span>");
public final static Pattern PATTERN_LATLON_ORIG = Pattern.compile("\\{\"isUserDefined\":true[^}]+?\"oldLatLngDisplay\":\"([^\"]+)\"\\}");
public final static Pattern PATTERN_LOCATION = Pattern.compile(Pattern.quote("<span id=\"ctl00_ContentBody_Location\">In ") + "(?:<a href=[^>]*>)?(.*?)<");
- public final static Pattern PATTERN_PERSONALNOTE = Pattern.compile("<p id=\"cache_note\"[^>]*>(.*?)</p>");
+ public final static Pattern PATTERN_PERSONALNOTE = Pattern.compile("<p id=\"cache_note\"[^>]*>(.*?)</p>", Pattern.DOTALL);
public final static Pattern PATTERN_NAME = Pattern.compile("<span id=\"ctl00_ContentBody_CacheName\">(.*?)</span>");
public final static Pattern PATTERN_DIFFICULTY = Pattern.compile("<span id=\"ctl00_ContentBody_uxLegendScale\"[^>]*>[^<]*<img src=\"[^\"]*/images/stars/stars([0-9_]+)\\.gif\"");
public final static Pattern PATTERN_TERRAIN = Pattern.compile("<span id=\"ctl00_ContentBody_Localize[\\d]+\"[^>]*>[^<]*<img src=\"[^\"]*/images/stars/stars([0-9_]+)\\.gif\"");
@@ -69,7 +69,7 @@ public final class GCConstants {
public static final String MEMBER_STATUS_RENEW = "<a id=\"ctl00_hlRenew";
public static final String MEMBER_STATUS_PM = "Premium Member";
/** Use replaceAll("[,.]","") on the resulting string before converting to an int */
- public static final Pattern PATTERN_CACHES_FOUND = Pattern.compile("<strong[^>]*>.*?([\\d,.]+) Caches Found");
+ public static final Pattern PATTERN_CACHES_FOUND = Pattern.compile("<strong[^>]*>.*?([\\d,.]+) Caches Found", Pattern.DOTALL);
public static final Pattern PATTERN_AVATAR_IMAGE_PROFILE_PAGE = Pattern.compile("<img src=\"(http://img.geocaching.com/user/avatar/[0-9a-f-]+\\.jpg)\"[^>]*\\salt=\"Avatar\"");
public static final Pattern PATTERN_LOGIN_NAME_LOGIN_PAGE = Pattern.compile("ctl00_ContentBody_lbUsername\">.*<strong>(.*)</strong>");
public static final Pattern PATTERN_CUSTOMDATE = Pattern.compile("<option selected=\"selected\" value=\"([ /Mdy-]+)\">");
@@ -84,6 +84,7 @@ public final class GCConstants {
public final static String ERROR_TB_DOES_NOT_EXIST = "does not exist in the system";
public final static String ERROR_TB_ELEMENT_EXCEPTION = "ElementNotFound Exception";
public final static String ERROR_TB_ARITHMETIC_OVERFLOW = "operation resulted in an overflow";
+ public final static String ERROR_TB_NOT_ACTIVATED = "hasn't been activated";
/**
* some parts of the webpage don't correctly encode the name, therefore this pattern must be checked with a
* trackable name that needs HTML encoding
@@ -121,9 +122,9 @@ public final class GCConstants {
public final static Pattern PATTERN_SEARCH_DIFFICULTY_TERRAIN = Pattern.compile("<span class=\"small\">([0-5]([\\.,]5)?)/([0-5]([\\.,]5)?)</span><br />");
public final static Pattern PATTERN_SEARCH_CONTAINER = Pattern.compile("<img src=\"/images/icons/container/([^\\.]+)\\.gif\"");
public final static Pattern PATTERN_SEARCH_GEOCODE = Pattern.compile("\\|\\W*(GC[0-9A-Z]+)[^\\|]*\\|");
- public final static Pattern PATTERN_SEARCH_ID = Pattern.compile("name=\"CID\"[^v]*value=\"([0-9]+)\"");
- public final static Pattern PATTERN_SEARCH_FAVORITE = Pattern.compile("<span id=\"ctl00_ContentBody_dlResults_ctl[0-9]+_uxFavoritesValue\" title=\"[^\"]*\" class=\"favorite-rank\">([0-9]+)</span>");
- public final static Pattern PATTERN_SEARCH_TOTALCOUNT = Pattern.compile("<td class=\"PageBuilderWidget\"><span>Total Records[^<]*<b>(\\d+)<\\/b>");
+ public final static Pattern PATTERN_SEARCH_ID = Pattern.compile("name=\"CID\"[^v]*value=\"(\\d+)\"");
+ public final static Pattern PATTERN_SEARCH_FAVORITE = Pattern.compile("favorite-rank\">([0-9,.]+)</span>");
+ public final static Pattern PATTERN_SEARCH_TOTALCOUNT = Pattern.compile("<span>Total Records\\D*(\\d+)<");
public final static Pattern PATTERN_SEARCH_RECAPTCHA = Pattern.compile("<script[^>]*src=\"[^\"]*/recaptcha/api/challenge\\?k=([^\"]+)\"[^>]*>");
public final static Pattern PATTERN_SEARCH_RECAPTCHACHALLENGE = Pattern.compile("challenge : '([^']+)'");
@@ -152,17 +153,11 @@ public final class GCConstants {
public final static Pattern PATTERN_MAINTENANCE = Pattern.compile("<span id=\"ctl00_ContentBody_LogBookPanel1_lbConfirm\"[^>]*>([^<]*<font[^>]*>)?([^<]+)(</font>[^<]*)?</span>", Pattern.CASE_INSENSITIVE);
public final static Pattern PATTERN_OK1 = Pattern.compile("<h2[^>]*>[^<]*<span id=\"ctl00_ContentBody_lbHeading\"[^>]*>[^<]*</span>[^<]*</h2>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
public final static Pattern PATTERN_OK2 = Pattern.compile("<div id=[\"|']ctl00_ContentBody_LogBookPanel1_ViewLogPanel[\"|']>", Pattern.CASE_INSENSITIVE);
- public final static Pattern PATTERN_OK_IMAGEUPLOAD = Pattern.compile("<div id=[\"|']ctl00_ContentBody_ImageUploadControl1_uxUploadDonePanel[\"|']>", Pattern.CASE_INSENSITIVE);
+ public final static Pattern PATTERN_IMAGE_UPLOAD_URL = Pattern.compile("title=\"Click for Larger Image\"\\s*src=\"(.*?)\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
public final static Pattern PATTERN_VIEWSTATEFIELDCOUNT = Pattern.compile("id=\"__VIEWSTATEFIELDCOUNT\"[^(value)]+value=\"(\\d+)\"[^>]+>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
public final static Pattern PATTERN_VIEWSTATES = Pattern.compile("id=\"__VIEWSTATE(\\d*)\"[^(value)]+value=\"([^\"]+)\"[^>]+>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
public final static Pattern PATTERN_USERTOKEN = Pattern.compile("userToken\\s*=\\s*'([^']+)'");
- /**
- * Patterns for GC and TB codes
- */
- public final static Pattern PATTERN_GC_CODE = Pattern.compile("GC[0-9A-Z]+", Pattern.CASE_INSENSITIVE);
- public final static Pattern PATTERN_TB_CODE = Pattern.compile("TB[0-9A-Z]+", Pattern.CASE_INSENSITIVE);
-
/** Live Map since 14.02.2012 */
public final static Pattern PATTERN_USERSESSION = Pattern.compile("UserSession\\('([^']+)'");
public final static Pattern PATTERN_SESSIONTOKEN = Pattern.compile("sessionToken:'([^']+)'");
@@ -194,7 +189,7 @@ public final class GCConstants {
*/
public static long gccodeToGCId(final String gccode) {
long base = GC_BASE31;
- String geocodeWO = gccode.substring(2).toUpperCase(Locale.US);
+ final 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;
diff --git a/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java
new file mode 100644
index 0000000..2aa5c75
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java
@@ -0,0 +1,132 @@
+package cgeo.geocaching.connector.gc;
+
+import cgeo.geocaching.Geocache;
+import cgeo.geocaching.LogCacheActivity;
+import cgeo.geocaching.R;
+import cgeo.geocaching.settings.Settings;
+import cgeo.geocaching.TrackableLog;
+import cgeo.geocaching.activity.ActivityMixin;
+import cgeo.geocaching.connector.ILoggingManager;
+import cgeo.geocaching.connector.ImageResult;
+import cgeo.geocaching.connector.LogResult;
+import cgeo.geocaching.enumerations.LogType;
+import cgeo.geocaching.enumerations.StatusCode;
+import cgeo.geocaching.loaders.UrlLoader;
+import cgeo.geocaching.network.Parameters;
+import cgeo.geocaching.utils.Log;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+
+import android.app.Activity;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.Loader;
+
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.List;
+
+public class GCLoggingManager implements ILoggingManager, LoaderManager.LoaderCallbacks<String> {
+
+ private final LogCacheActivity activity;
+ private final Geocache cache;
+
+ private String[] viewstates;
+ private List<TrackableLog> trackables;
+ private List<LogType> possibleLogTypes;
+ private boolean hasLoaderError = true;
+
+ public GCLoggingManager(Activity activity, Geocache cache) {
+ this.activity = (LogCacheActivity) activity;
+ this.cache = cache;
+ }
+
+ @Override
+ public Loader<String> onCreateLoader(int arg0, Bundle arg1) {
+ if (!Settings.isLogin()) { // allow offline logging
+ ActivityMixin.showToast(activity, activity.getResources().getString(R.string.err_login));
+ return null;
+ }
+ return new UrlLoader(activity.getBaseContext(), "http://www.geocaching.com/seek/log.aspx", new Parameters("ID", cache.getCacheId()));
+ }
+
+ @Override
+ public void onLoadFinished(Loader<String> arg0, String page) {
+
+ if (page == null) {
+ hasLoaderError = true;
+ } else {
+
+ viewstates = Login.getViewstates(page);
+ trackables = GCParser.parseTrackableLog(page);
+ possibleLogTypes = GCParser.parseTypes(page);
+
+ hasLoaderError = possibleLogTypes.isEmpty();
+ }
+
+ activity.onLoadFinished();
+ }
+
+ @Override
+ public void onLoaderReset(Loader<String> arg0) {
+ // nothing to do
+ }
+
+ @Override
+ public void init() {
+ activity.getSupportLoaderManager().initLoader(0, null, this);
+ }
+
+ @Override
+ public LogResult postLog(Geocache cache, LogType logType, Calendar date, String log, String logPassword, List<TrackableLog> trackableLogs) {
+
+ try {
+ final ImmutablePair<StatusCode, String> postResult = GCParser.postLog(cache.getGeocode(), cache.getCacheId(), viewstates, logType,
+ date.get(Calendar.YEAR), (date.get(Calendar.MONTH) + 1), date.get(Calendar.DATE),
+ log, trackableLogs);
+
+ return new LogResult(postResult.left, postResult.right);
+ } catch (Exception e) {
+ Log.e("GCLoggingManager.postLog", e);
+ }
+
+ return new LogResult(StatusCode.LOG_POST_ERROR, "");
+ }
+
+ @Override
+ public ImageResult postLogImage(String logId, String imageCaption, String imageDescription, Uri imageUri) {
+
+ if (StringUtils.isNotBlank(imageUri.getPath())) {
+
+ ImmutablePair<StatusCode, String> imageResult = GCParser.uploadLogImage(logId, imageCaption, imageDescription, imageUri);
+
+ return new ImageResult(imageResult.left, imageResult.right);
+ }
+
+ return new ImageResult(StatusCode.LOGIMAGE_POST_ERROR, "");
+ }
+
+ @Override
+ public boolean hasLoaderError() {
+ return hasLoaderError;
+ }
+
+ @Override
+ public List<TrackableLog> getTrackables() {
+ if (hasLoaderError) {
+ return Collections.emptyList();
+ }
+ return trackables;
+ }
+
+ @Override
+ public List<LogType> getPossibleLogTypes() {
+ if (hasLoaderError) {
+ return Collections.emptyList();
+ }
+ return possibleLogTypes;
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/connector/gc/GCMap.java b/main/src/cgeo/geocaching/connector/gc/GCMap.java
index 49f61ef..4bc55fe 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCMap.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCMap.java
@@ -2,7 +2,7 @@ package cgeo.geocaching.connector.gc;
import cgeo.geocaching.Geocache;
import cgeo.geocaching.SearchResult;
-import cgeo.geocaching.Settings;
+import cgeo.geocaching.settings.Settings;
import cgeo.geocaching.cgData;
import cgeo.geocaching.cgeoapplication;
import cgeo.geocaching.enumerations.CacheSize;
@@ -92,7 +92,7 @@ public class GCMap {
JSONObject ownerObj = dataObject.getJSONObject("owner");
cache.setOwnerDisplayName(ownerObj.getString("text"));
- result.addCache(cache);
+ result.addAndPutInCache(cache);
}
} catch (JSONException e) {
@@ -231,13 +231,13 @@ public class GCMap {
exclude = true;
}
if (!exclude) {
- searchResult.addCache(cache);
+ searchResult.addAndPutInCache(cache);
}
}
Log.d("Retrieved " + searchResult.getCount() + " caches for tile " + tile.toString());
} catch (Exception e) {
- Log.e("GCBase.parseMapJSON", e);
+ Log.e("GCMap.parseMapJSON", e);
}
return searchResult;
@@ -283,7 +283,7 @@ public class GCMap {
* @return
*/
private static SearchResult searchByViewport(final Viewport viewport, final String[] tokens, Strategy strategy) {
- Log.d("GCBase.searchByViewport" + viewport.toString());
+ Log.d("GCMap.searchByViewport" + viewport.toString());
final SearchResult searchResult = new SearchResult();
searchResult.setUrl(GCConstants.URL_LIVE_MAP + "?ll=" + viewport.getCenter().getLatitude() + "," + viewport.getCenter().getLongitude());
@@ -330,14 +330,14 @@ public class GCMap {
String data = Tile.requestMapInfo(GCConstants.URL_MAP_INFO, params, GCConstants.URL_LIVE_MAP);
if (StringUtils.isEmpty(data)) {
- Log.w("GCBase.searchByViewport: No data from server for tile (" + tile.getX() + "/" + tile.getY() + ")");
+ Log.w("GCMap.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())) {
- Log.e("GCBase.searchByViewport: No cache parsed for viewport " + viewport);
+ Log.e("GCMap.searchByViewport: No cache parsed for viewport " + viewport);
}
else {
- searchResult.addGeocodes(search.getGeocodes());
+ searchResult.addSearchResult(search);
}
Tile.Cache.add(tile);
}
@@ -349,20 +349,21 @@ public class GCMap {
}
}
+
+ // Check for vanished found caches
+ if (tiles.iterator().next().getZoomlevel() >= Tile.ZOOMLEVEL_MIN_PERSONALIZED) {
+ searchResult.addFilteredGeocodes(cgData.getCachedMissingFromSearch(searchResult, tiles, GCConnector.getInstance(), Tile.ZOOMLEVEL_MIN_PERSONALIZED - 1));
+ }
}
- if (strategy.flags.contains(StrategyFlag.SEARCH_NEARBY)) {
+ if (strategy.flags.contains(StrategyFlag.SEARCH_NEARBY) && Settings.isPremiumMember()) {
final Geopoint center = viewport.getCenter();
if ((lastSearchViewport == null) || !lastSearchViewport.contains(center)) {
//FIXME We don't have a RecaptchaReceiver!?
SearchResult search = GCParser.searchByCoords(center, Settings.getCacheType(), false, null);
if (search != null && !search.isEmpty()) {
final Set<String> geocodes = search.getGeocodes();
- if (Settings.isPremiumMember()) {
- lastSearchViewport = cgData.getBounds(geocodes);
- } else {
- lastSearchViewport = new Viewport(center, center);
- }
+ lastSearchViewport = cgData.getBounds(geocodes);
searchResult.addGeocodes(geocodes);
}
}
diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java
index 6b456fd..9ecb51b 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCParser.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java
@@ -5,7 +5,7 @@ import cgeo.geocaching.Image;
import cgeo.geocaching.LogEntry;
import cgeo.geocaching.R;
import cgeo.geocaching.SearchResult;
-import cgeo.geocaching.Settings;
+import cgeo.geocaching.settings.Settings;
import cgeo.geocaching.Trackable;
import cgeo.geocaching.TrackableLog;
import cgeo.geocaching.Waypoint;
@@ -28,10 +28,10 @@ import cgeo.geocaching.loaders.RecaptchaReceiver;
import cgeo.geocaching.network.Network;
import cgeo.geocaching.network.Parameters;
import cgeo.geocaching.ui.DirectionImage;
-import cgeo.geocaching.utils.BaseUtils;
import cgeo.geocaching.utils.CancellableHandler;
import cgeo.geocaching.utils.Log;
import cgeo.geocaching.utils.MatcherWrapper;
+import cgeo.geocaching.utils.TextUtils;
import ch.boye.httpclientandroidlib.HttpResponse;
@@ -79,14 +79,14 @@ public abstract class GCParser {
// recaptcha
String recaptchaChallenge = null;
if (showCaptcha) {
- String recaptchaJsParam = BaseUtils.getMatch(page, GCConstants.PATTERN_SEARCH_RECAPTCHA, false, null);
+ final String recaptchaJsParam = TextUtils.getMatch(page, GCConstants.PATTERN_SEARCH_RECAPTCHA, false, null);
if (recaptchaJsParam != null) {
final Parameters params = new Parameters("k", recaptchaJsParam.trim());
final String recaptchaJs = Network.getResponseData(Network.getRequest("http://www.google.com/recaptcha/api/challenge", params));
if (StringUtils.isNotBlank(recaptchaJs)) {
- recaptchaChallenge = BaseUtils.getMatch(recaptchaJs, GCConstants.PATTERN_SEARCH_RECAPTCHACHALLENGE, true, 1, null, true);
+ recaptchaChallenge = TextUtils.getMatch(recaptchaJs, GCConstants.PATTERN_SEARCH_RECAPTCHACHALLENGE, true, 1, null, true);
}
}
if (thread != null && StringUtils.isNotBlank(recaptchaChallenge)) {
@@ -109,7 +109,7 @@ public abstract class GCParser {
page = page.substring(startPos); // cut on <table
startPos = page.indexOf('>');
- int endPos = page.indexOf("ctl00_ContentBody_UnitTxt");
+ final int endPos = page.indexOf("ctl00_ContentBody_UnitTxt");
if (startPos == -1 || endPos == -1) {
Log.e("GCParser.parseSearch: ID \"ctl00_ContentBody_UnitTxt\" not found on page");
return null;
@@ -120,9 +120,10 @@ public abstract class GCParser {
final String[] rows = page.split("<tr class=");
final int rows_count = rows.length;
+ int excludedCaches = 0;
for (int z = 1; z < rows_count; z++) {
final Geocache cache = new Geocache();
- String row = rows[z];
+ final String row = rows[z];
// check for cache type presence
if (!row.contains("images/wpttypes")) {
@@ -150,33 +151,35 @@ public abstract class GCParser {
}
}
}
- } catch (Exception e) {
+ } catch (final Exception e) {
// failed to parse GUID and/or Disabled
Log.w("GCParser.parseSearch: Failed to parse GUID and/or Disabled data");
}
if (Settings.isExcludeDisabledCaches() && (cache.isDisabled() || cache.isArchived())) {
// skip disabled and archived caches
+ excludedCaches++;
continue;
}
- cache.setGeocode(BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_GEOCODE, true, 1, cache.getGeocode(), true));
+ cache.setGeocode(TextUtils.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.setType(CacheType.getByPattern(TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_TYPE, true, 1, null, true)));
// cache direction - image
if (Settings.getLoadDirImg()) {
- final String direction = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION_DISTANCE, false, 1, null, false);
+ final String direction = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION_DISTANCE, false, 1, null, false);
if (direction != null) {
cache.setDirectionImg(direction);
}
}
// cache distance - estimated distance for basic members
- final String distance = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION_DISTANCE, false, 2, null, false);
+ final String distance = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION_DISTANCE, false, 2, null, false);
if (distance != null) {
- cache.setDistance(DistanceParser.parseDistance(distance, Settings.isUseMetricUnits()));
+ cache.setDistance(DistanceParser.parseDistance(distance,
+ !Settings.isUseImperialUnits()));
}
// difficulty/terrain
@@ -193,7 +196,7 @@ public abstract class GCParser {
}
// size
- final String container = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_CONTAINER, false, 1, null, false);
+ final String container = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_CONTAINER, false, 1, null, false);
cache.setSize(CacheSize.getById(container));
// cache inventory
@@ -203,7 +206,7 @@ public abstract class GCParser {
if (matcherTbs.groupCount() > 0) {
try {
cache.setInventoryItems(Integer.parseInt(matcherTbs.group(1)));
- } catch (NumberFormatException e) {
+ } catch (final NumberFormatException e) {
Log.e("Error parsing trackables count", e);
}
inventoryPre = matcherTbs.group(2);
@@ -229,7 +232,7 @@ public abstract class GCParser {
cache.setFound(row.contains("/images/icons/16/found.png"));
// id
- String result = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_ID, null);
+ String result = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_ID, null);
if (null != result) {
cache.setCacheId(result);
cids.add(cache.getCacheId());
@@ -237,24 +240,24 @@ public abstract class GCParser {
// favorite count
try {
- result = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_FAVORITE, false, 1, null, true);
+ result = getNumberString(TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_FAVORITE, false, 1, null, true));
if (null != result) {
cache.setFavoritePoints(Integer.parseInt(result));
}
- } catch (NumberFormatException e) {
- Log.w("GCParser.parseSearch: Failed to parse favourite count");
+ } catch (final NumberFormatException e) {
+ Log.w("GCParser.parseSearch: Failed to parse favorite count");
}
- searchResult.addCache(cache);
+ searchResult.addAndPutInCache(cache);
}
// total caches found
try {
- String result = BaseUtils.getMatch(page, GCConstants.PATTERN_SEARCH_TOTALCOUNT, false, 1, null, true);
+ final String result = TextUtils.getMatch(page, GCConstants.PATTERN_SEARCH_TOTALCOUNT, false, 1, null, true);
if (null != result) {
- searchResult.setTotal(Integer.parseInt(result));
+ searchResult.setTotal(Integer.parseInt(result) - excludedCaches);
}
- } catch (NumberFormatException e) {
+ } catch (final NumberFormatException e) {
Log.w("GCParser.parseSearch: Failed to parse cache count");
}
@@ -284,7 +287,7 @@ public abstract class GCParser {
params.put("__VIEWSTATEFIELDCOUNT", String.valueOf(searchResult.viewstates.length));
}
}
- for (String cid : cids) {
+ for (final String cid : cids) {
params.put("CID", cid);
}
@@ -308,7 +311,7 @@ public abstract class GCParser {
LocParser.parseLoc(searchResult, coordinates);
- } catch (Exception e) {
+ } catch (final Exception e) {
Log.e("GCParser.parseSearch.CIDs", e);
}
}
@@ -316,7 +319,7 @@ public abstract class GCParser {
// get direction images
if (Settings.getLoadDirImg()) {
final Set<Geocache> caches = searchResult.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB);
- for (Geocache cache : caches) {
+ for (final Geocache cache : caches) {
if (cache.getCoords() == null && StringUtils.isNotEmpty(cache.getDirectionImg())) {
DirectionImage.getDrawable(cache.getDirectionImg());
}
@@ -327,15 +330,17 @@ public abstract class GCParser {
}
private static Float parseStars(final String value) {
- float floatValue = Float.parseFloat(StringUtils.replaceChars(value, ',', '.'));
+ final float floatValue = Float.parseFloat(StringUtils.replaceChars(value, ',', '.'));
return floatValue >= 0.5 && floatValue <= 5.0 ? floatValue : null;
}
static SearchResult parseCache(final String page, final CancellableHandler handler) {
final SearchResult searchResult = parseCacheFromText(page, handler);
+ // attention: parseCacheFromText already stores implicitely through searchResult.addCache
if (searchResult != null && !searchResult.getGeocodes().isEmpty()) {
final Geocache cache = searchResult.getFirstCacheFromResult(LoadFlags.LOAD_CACHE_OR_DB);
getExtraOnlineInfo(cache, page, handler);
+ // too late: it is already stored through parseCacheFromText
cache.setDetailedUpdatedNow();
if (CancellableHandler.isCancelled(handler)) {
return null;
@@ -352,61 +357,70 @@ public abstract class GCParser {
return searchResult;
}
- static SearchResult parseCacheFromText(final String page, final CancellableHandler handler) {
+ static SearchResult parseCacheFromText(final String pageIn, final CancellableHandler handler) {
CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_details);
- if (StringUtils.isBlank(page)) {
+ if (StringUtils.isBlank(pageIn)) {
Log.e("GCParser.parseCache: No page given");
return null;
}
final SearchResult searchResult = new SearchResult();
- if (page.contains(GCConstants.STRING_UNPUBLISHED_OTHER) || page.contains(GCConstants.STRING_UNPUBLISHED_OWNER) || page.contains(GCConstants.STRING_UNPUBLISHED_FROM_SEARCH)) {
+ if (pageIn.contains(GCConstants.STRING_UNPUBLISHED_OTHER) || pageIn.contains(GCConstants.STRING_UNPUBLISHED_OWNER) || pageIn.contains(GCConstants.STRING_UNPUBLISHED_FROM_SEARCH)) {
searchResult.setError(StatusCode.UNPUBLISHED_CACHE);
return searchResult;
}
- if (page.contains(GCConstants.STRING_PREMIUMONLY_1) || page.contains(GCConstants.STRING_PREMIUMONLY_2)) {
+ if (pageIn.contains(GCConstants.STRING_PREMIUMONLY_1) || pageIn.contains(GCConstants.STRING_PREMIUMONLY_2)) {
searchResult.setError(StatusCode.PREMIUM_ONLY);
return searchResult;
}
- final String cacheName = Html.fromHtml(BaseUtils.getMatch(page, GCConstants.PATTERN_NAME, true, "")).toString();
+ final String cacheName = Html.fromHtml(TextUtils.getMatch(pageIn, GCConstants.PATTERN_NAME, true, "")).toString();
if (GCConstants.STRING_UNKNOWN_ERROR.equalsIgnoreCase(cacheName)) {
searchResult.setError(StatusCode.UNKNOWN_ERROR);
return searchResult;
}
+ // first handle the content with line breaks, then trim everything for easier matching and reduced memory consumption in parsed fields
+ String personalNoteWithLineBreaks = "";
+ MatcherWrapper matcher = new MatcherWrapper(GCConstants.PATTERN_PERSONALNOTE, pageIn);
+ if (matcher.find()) {
+ personalNoteWithLineBreaks = matcher.group(1).trim();
+ }
+
+ final String page = TextUtils.replaceWhitespace(pageIn);
+
final Geocache cache = new Geocache();
cache.setDisabled(page.contains(GCConstants.STRING_DISABLED));
cache.setArchived(page.contains(GCConstants.STRING_ARCHIVED));
- cache.setPremiumMembersOnly(BaseUtils.matches(page, GCConstants.PATTERN_PREMIUMMEMBERS));
+ cache.setPremiumMembersOnly(TextUtils.matches(page, GCConstants.PATTERN_PREMIUMMEMBERS));
- cache.setFavorite(BaseUtils.matches(page, GCConstants.PATTERN_FAVORITE));
+ cache.setFavorite(TextUtils.matches(page, GCConstants.PATTERN_FAVORITE));
// cache geocode
- cache.setGeocode(BaseUtils.getMatch(page, GCConstants.PATTERN_GEOCODE, true, cache.getGeocode()));
+ cache.setGeocode(TextUtils.getMatch(page, GCConstants.PATTERN_GEOCODE, true, cache.getGeocode()));
// cache id
- cache.setCacheId(BaseUtils.getMatch(page, GCConstants.PATTERN_CACHEID, true, cache.getCacheId()));
+ cache.setCacheId(TextUtils.getMatch(page, GCConstants.PATTERN_CACHEID, true, cache.getCacheId()));
// cache guid
- cache.setGuid(BaseUtils.getMatch(page, GCConstants.PATTERN_GUID, true, cache.getGuid()));
+ cache.setGuid(TextUtils.getMatch(page, GCConstants.PATTERN_GUID, true, cache.getGuid()));
// name
cache.setName(cacheName);
// owner real name
- cache.setOwnerUserId(Network.decode(BaseUtils.getMatch(page, GCConstants.PATTERN_OWNER_USERID, true, cache.getOwnerUserId())));
+ cache.setOwnerUserId(Network.decode(TextUtils.getMatch(page, GCConstants.PATTERN_OWNER_USERID, true, cache.getOwnerUserId())));
cache.setUserModifiedCoords(false);
String tableInside = page;
- int pos = tableInside.indexOf(GCConstants.STRING_CACHEDETAILS);
+ final int pos = tableInside.indexOf(GCConstants.STRING_CACHEDETAILS);
if (pos == -1) {
Log.e("GCParser.parseCache: ID \"cacheDetails\" not found on page");
return null;
@@ -416,96 +430,96 @@ public abstract class GCParser {
if (StringUtils.isNotBlank(tableInside)) {
// cache terrain
- String result = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_TERRAIN, true, null);
+ String result = TextUtils.getMatch(tableInside, GCConstants.PATTERN_TERRAIN, true, null);
if (result != null) {
try {
cache.setTerrain(Float.parseFloat(StringUtils.replaceChars(result, '_', '.')));
- } catch (NumberFormatException e) {
+ } catch (final NumberFormatException e) {
Log.e("Error parsing terrain value", e);
}
}
// cache difficulty
- result = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_DIFFICULTY, true, null);
+ result = TextUtils.getMatch(tableInside, GCConstants.PATTERN_DIFFICULTY, true, null);
if (result != null) {
try {
cache.setDifficulty(Float.parseFloat(StringUtils.replaceChars(result, '_', '.')));
- } catch (NumberFormatException e) {
+ } catch (final NumberFormatException e) {
Log.e("Error parsing difficulty value", e);
}
}
// owner
- cache.setOwnerDisplayName(StringEscapeUtils.unescapeHtml4(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_OWNER_DISPLAYNAME, true, cache.getOwnerDisplayName())));
+ cache.setOwnerDisplayName(StringEscapeUtils.unescapeHtml4(TextUtils.getMatch(tableInside, GCConstants.PATTERN_OWNER_DISPLAYNAME, true, cache.getOwnerDisplayName())));
// hidden
try {
- String hiddenString = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDEN, true, null);
+ String hiddenString = TextUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDEN, true, null);
if (StringUtils.isNotBlank(hiddenString)) {
cache.setHidden(Login.parseGcCustomDate(hiddenString));
}
if (cache.getHiddenDate() == null) {
// event date
- hiddenString = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDENEVENT, true, null);
+ hiddenString = TextUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDENEVENT, true, null);
if (StringUtils.isNotBlank(hiddenString)) {
cache.setHidden(Login.parseGcCustomDate(hiddenString));
}
}
- } catch (ParseException e) {
+ } catch (final ParseException e) {
// failed to parse cache hidden date
Log.w("GCParser.parseCache: Failed to parse cache hidden (event) date");
}
- // favourite
+ // favorite
try {
- cache.setFavoritePoints(Integer.parseInt(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_FAVORITECOUNT, true, "0")));
- } catch (NumberFormatException e) {
- Log.e("Error parsing favourite count", e);
+ cache.setFavoritePoints(Integer.parseInt(TextUtils.getMatch(tableInside, GCConstants.PATTERN_FAVORITECOUNT, true, "0")));
+ } catch (final NumberFormatException e) {
+ Log.e("Error parsing favorite count", e);
}
// cache size
- cache.setSize(CacheSize.getById(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_SIZE, true, CacheSize.NOT_CHOSEN.id)));
+ cache.setSize(CacheSize.getById(TextUtils.getMatch(tableInside, GCConstants.PATTERN_SIZE, true, CacheSize.NOT_CHOSEN.id)));
}
// cache found
- cache.setFound(BaseUtils.matches(page, GCConstants.PATTERN_FOUND) || BaseUtils.matches(page, GCConstants.PATTERN_FOUND_ALTERNATIVE));
+ cache.setFound(TextUtils.matches(page, GCConstants.PATTERN_FOUND) || TextUtils.matches(page, GCConstants.PATTERN_FOUND_ALTERNATIVE));
// cache found date
try {
- final String foundDateString = BaseUtils.getMatch(page, GCConstants.PATTERN_FOUND_DATE, true, null);
+ final String foundDateString = TextUtils.getMatch(page, GCConstants.PATTERN_FOUND_DATE, true, null);
if (StringUtils.isNotBlank(foundDateString)) {
cache.setVisitedDate(Login.parseGcCustomDate(foundDateString).getTime());
}
- } catch (ParseException e) {
+ } catch (final ParseException e) {
// failed to parse cache found date
Log.w("GCParser.parseCache: Failed to parse cache found date");
}
// cache type
- cache.setType(CacheType.getByPattern(BaseUtils.getMatch(page, GCConstants.PATTERN_TYPE, true, cache.getType().id)));
+ cache.setType(CacheType.getByPattern(TextUtils.getMatch(page, GCConstants.PATTERN_TYPE, true, cache.getType().id)));
// on watchlist
- cache.setOnWatchlist(BaseUtils.matches(page, GCConstants.PATTERN_WATCHLIST));
+ cache.setOnWatchlist(TextUtils.matches(page, GCConstants.PATTERN_WATCHLIST));
// latitude and longitude. Can only be retrieved if user is logged in
- String latlon = BaseUtils.getMatch(page, GCConstants.PATTERN_LATLON, true, "");
+ String latlon = TextUtils.getMatch(page, GCConstants.PATTERN_LATLON, true, "");
if (StringUtils.isNotEmpty(latlon)) {
try {
cache.setCoords(new Geopoint(latlon));
cache.setReliableLatLon(true);
- } catch (Geopoint.GeopointException e) {
+ } catch (final Geopoint.GeopointException e) {
Log.w("GCParser.parseCache: Failed to parse cache coordinates", e);
}
}
// cache location
- cache.setLocation(BaseUtils.getMatch(page, GCConstants.PATTERN_LOCATION, true, ""));
+ cache.setLocation(TextUtils.getMatch(page, GCConstants.PATTERN_LOCATION, true, ""));
// cache hint
- String result = BaseUtils.getMatch(page, GCConstants.PATTERN_HINT, false, null);
+ final String result = TextUtils.getMatch(page, GCConstants.PATTERN_HINT, false, null);
if (result != null) {
// replace linebreak and paragraph tags
- String hint = GCConstants.PATTERN_LINEBREAK.matcher(result).replaceAll("\n");
+ final String hint = GCConstants.PATTERN_LINEBREAK.matcher(result).replaceAll("\n");
if (hint != null) {
cache.setHint(StringUtils.replace(hint, "</p>", "").trim());
}
@@ -514,17 +528,17 @@ public abstract class GCParser {
cache.checkFields();
// cache personal note
- cache.setPersonalNote(BaseUtils.getMatch(page, GCConstants.PATTERN_PERSONALNOTE, true, cache.getPersonalNote()));
+ cache.setPersonalNote(personalNoteWithLineBreaks);
// cache short description
- cache.setShortDescription(BaseUtils.getMatch(page, GCConstants.PATTERN_SHORTDESC, true, ""));
+ cache.setShortDescription(TextUtils.getMatch(page, GCConstants.PATTERN_SHORTDESC, true, ""));
// cache description
- cache.setDescription(BaseUtils.getMatch(page, GCConstants.PATTERN_DESC, true, ""));
+ cache.setDescription(TextUtils.getMatch(page, GCConstants.PATTERN_DESC, true, ""));
// cache attributes
try {
- final String attributesPre = BaseUtils.getMatch(page, GCConstants.PATTERN_ATTRIBUTES, true, null);
+ final String attributesPre = TextUtils.getMatch(page, GCConstants.PATTERN_ATTRIBUTES, true, null);
if (null != attributesPre) {
final MatcherWrapper matcherAttributesInside = new MatcherWrapper(GCConstants.PATTERN_ATTRIBUTESINSIDE, attributesPre);
@@ -537,8 +551,8 @@ public abstract class GCParser {
// if the image name can be recognized, use the image name as attribute
final String imageName = matcherAttributesInside.group(1).trim();
if (StringUtils.isNotEmpty(imageName)) {
- int start = imageName.lastIndexOf('/');
- int end = imageName.lastIndexOf('.');
+ final int start = imageName.lastIndexOf('/');
+ final int end = imageName.lastIndexOf('.');
if (start >= 0 && end >= 0) {
attribute = imageName.substring(start + 1, end).replace('-', '_').toLowerCase(Locale.US);
}
@@ -548,7 +562,7 @@ public abstract class GCParser {
}
cache.setAttributes(attributes);
}
- } catch (Exception e) {
+ } catch (final Exception e) {
// failed to parse cache attributes
Log.w("GCParser.parseCache: Failed to parse cache attributes");
}
@@ -565,7 +579,7 @@ public abstract class GCParser {
while (matcherSpoilersInside.find()) {
// the original spoiler URL (include .../display/... contains a low-resolution image
// if we shorten the URL we get the original-resolution image
- String url = matcherSpoilersInside.group(1).replace("/display", "");
+ final String url = matcherSpoilersInside.group(1).replace("/display", "");
String title = null;
if (matcherSpoilersInside.group(3) != null) {
@@ -577,7 +591,7 @@ public abstract class GCParser {
}
cache.addSpoiler(new Image(url, title, description));
}
- } catch (Exception e) {
+ } catch (final Exception e) {
// failed to parse cache spoilers
Log.w("GCParser.parseCache: Failed to parse cache spoilers");
}
@@ -611,20 +625,20 @@ public abstract class GCParser {
}
}
}
- } catch (Exception e) {
+ } catch (final Exception e) {
// failed to parse cache inventory
Log.w("GCParser.parseCache: Failed to parse cache inventory (2)");
}
// cache logs counts
try {
- final String countlogs = BaseUtils.getMatch(page, GCConstants.PATTERN_COUNTLOGS, true, null);
+ final String countlogs = TextUtils.getMatch(page, GCConstants.PATTERN_COUNTLOGS, true, null);
if (null != countlogs) {
final MatcherWrapper matcherLog = new MatcherWrapper(GCConstants.PATTERN_COUNTLOG, countlogs);
while (matcherLog.find()) {
- String typeStr = matcherLog.group(1);
- String countStr = matcherLog.group(2).replaceAll("[.,]", "");
+ final String typeStr = matcherLog.group(1);
+ final String countStr = getNumberString(matcherLog.group(2));
if (StringUtils.isNotBlank(typeStr)
&& LogType.UNKNOWN != LogType.getByIconName(typeStr)
@@ -633,7 +647,7 @@ public abstract class GCParser {
}
}
}
- } catch (Exception e) {
+ } catch (final Exception e) {
// failed to parse logs
Log.w("GCParser.parseCache: Failed to parse cache log count");
}
@@ -643,7 +657,7 @@ public abstract class GCParser {
// add waypoint for original coordinates in case of user-modified listing-coordinates
try {
- final String originalCoords = BaseUtils.getMatch(page, GCConstants.PATTERN_LATLON_ORIG, false, null);
+ final String originalCoords = TextUtils.getMatch(page, GCConstants.PATTERN_LATLON_ORIG, false, null);
if (null != originalCoords) {
final Waypoint waypoint = new Waypoint(cgeoapplication.getInstance().getString(R.string.cache_coordinates_original), WaypointType.ORIGINAL, false);
@@ -651,7 +665,7 @@ public abstract class GCParser {
cache.addOrChangeWaypoint(waypoint, false);
cache.setUserModifiedCoords(true);
}
- } catch (Geopoint.GeopointException e) {
+ } catch (final Geopoint.GeopointException e) {
}
int wpBegin = page.indexOf("<table class=\"Table\" id=\"ctl00_ContentBody_Waypoints\">");
@@ -685,21 +699,21 @@ public abstract class GCParser {
// waypoint name
// res is null during the unit tests
- final String name = BaseUtils.getMatch(wp[6], GCConstants.PATTERN_WPNAME, true, 1, cgeoapplication.getInstance().getString(R.string.waypoint), true);
+ final String name = TextUtils.getMatch(wp[6], GCConstants.PATTERN_WPNAME, true, 1, cgeoapplication.getInstance().getString(R.string.waypoint), true);
// waypoint type
- final String resulttype = BaseUtils.getMatch(wp[3], GCConstants.PATTERN_WPTYPE, null);
+ final String resulttype = TextUtils.getMatch(wp[3], GCConstants.PATTERN_WPTYPE, null);
final Waypoint waypoint = new Waypoint(name, WaypointType.findById(resulttype), false);
// waypoint prefix
- waypoint.setPrefix(BaseUtils.getMatch(wp[4], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, true, 2, waypoint.getPrefix(), false));
+ waypoint.setPrefix(TextUtils.getMatch(wp[4], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, true, 2, waypoint.getPrefix(), false));
// waypoint lookup
- waypoint.setLookup(BaseUtils.getMatch(wp[5], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, true, 2, waypoint.getLookup(), false));
+ waypoint.setLookup(TextUtils.getMatch(wp[5], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, true, 2, waypoint.getLookup(), false));
// waypoint latitude and logitude
- latlon = Html.fromHtml(BaseUtils.getMatch(wp[7], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, false, 2, "", false)).toString().trim();
+ latlon = Html.fromHtml(TextUtils.getMatch(wp[7], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, false, 2, "", false)).toString().trim();
if (!StringUtils.startsWith(latlon, "???")) {
waypoint.setLatlon(latlon);
waypoint.setCoords(new Geopoint(latlon));
@@ -711,7 +725,7 @@ public abstract class GCParser {
}
// waypoint note
- waypoint.setNote(BaseUtils.getMatch(wp[3], GCConstants.PATTERN_WPNOTE, waypoint.getNote()));
+ waypoint.setNote(TextUtils.getMatch(wp[3], GCConstants.PATTERN_WPNOTE, waypoint.getNote()));
cache.addOrChangeWaypoint(waypoint, false);
}
@@ -729,10 +743,18 @@ public abstract class GCParser {
return searchResult;
}
- searchResult.addCache(cache);
+ cache.setDetailedUpdatedNow();
+ searchResult.addAndPutInCache(cache);
return searchResult;
}
+ private static String getNumberString(final String numberWithPunctuation) {
+ if (numberWithPunctuation == null) {
+ return null;
+ }
+ return numberWithPunctuation.replaceAll("[.,]", "");
+ }
+
public static SearchResult searchByNextPage(final SearchResult search, boolean showCaptcha, RecaptchaReceiver recaptchaReceiver) {
if (search == null) {
return search;
@@ -782,7 +804,7 @@ public abstract class GCParser {
// save to application
search.setError(searchResult.getError());
search.setViewstates(searchResult.viewstates);
- for (String geocode : searchResult.getGeocodes()) {
+ for (final String geocode : searchResult.getGeocodes()) {
search.addGeocode(geocode);
}
return search;
@@ -859,7 +881,7 @@ public abstract class GCParser {
}
private static boolean isSearchForMyCaches(final String userName) {
- if (userName.equalsIgnoreCase(Settings.getLogin().left)) {
+ if (userName.equalsIgnoreCase(Settings.getGcLogin().left)) {
Log.i("Overriding users choice because of self search, downloading all caches.");
return true;
}
@@ -893,7 +915,7 @@ public abstract class GCParser {
return null;
}
try {
- JSONObject response = Network.requestJSON("http://www.geocaching.com/api/geocode", new Parameters("q", address));
+ final JSONObject response = Network.requestJSON("http://www.geocaching.com/api/geocode", new Parameters("q", address));
if (response == null) {
return null;
}
@@ -903,12 +925,12 @@ public abstract class GCParser {
if (!response.has("data")) {
return null;
}
- JSONObject data = response.getJSONObject("data");
+ final JSONObject data = response.getJSONObject("data");
if (data == null) {
return null;
}
return searchByCoords(new Geopoint(data.getDouble("lat"), data.getDouble("lng")), cacheType, showCaptcha, recaptchaReceiver);
- } catch (JSONException e) {
+ } catch (final JSONException e) {
Log.w("GCParser.searchByAddress", e);
}
@@ -1005,7 +1027,7 @@ public abstract class GCParser {
final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/seek/log.aspx").encodedQuery("ID=" + cacheid).build().toString();
String page = Login.postRequestLogged(uri, params);
if (!Login.getLoginStatus(page)) {
- Log.e("GCParser.postLogTrackable: Can not log in geocaching");
+ Log.e("GCParser.postLog: Cannot log in geocaching");
return new ImmutablePair<StatusCode, String>(StatusCode.NOT_LOGGED_IN, "");
}
@@ -1033,7 +1055,7 @@ public abstract class GCParser {
if (trackables != null && !trackables.isEmpty()) { // we have some trackables to proceed
final StringBuilder hdnSelected = new StringBuilder();
- for (TrackableLog tb : trackables) {
+ for (final TrackableLog tb : trackables) {
final String action = Integer.toString(tb.id) + tb.action.action;
final StringBuilder paramText = new StringBuilder("ctl00$ContentBody$LogBookPanel1$uxTrackables$repTravelBugs$ctl");
@@ -1054,7 +1076,7 @@ public abstract class GCParser {
page = Network.getResponseData(Network.postRequest(uri, params));
}
- } catch (Exception e) {
+ } catch (final Exception e) {
Log.e("GCParser.postLog.confim", e);
}
@@ -1074,11 +1096,11 @@ public abstract class GCParser {
Login.setActualCachesFound(Login.getActualCachesFound() + 1);
}
- final String logID = BaseUtils.getMatch(page, GCConstants.PATTERN_LOG_IMAGE_UPLOAD, "");
+ final String logID = TextUtils.getMatch(page, GCConstants.PATTERN_LOG_IMAGE_UPLOAD, "");
return new ImmutablePair<StatusCode, String>(StatusCode.NO_ERROR, logID);
}
- } catch (Exception e) {
+ } catch (final Exception e) {
Log.e("GCParser.postLog.check", e);
}
@@ -1099,20 +1121,13 @@ public abstract class GCParser {
* the URI for the image to be uploaded
* @return status code to indicate success or failure
*/
- public static StatusCode uploadLogImage(final String logId, final String caption, final String description, final Uri imageUri) {
- final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/seek/upload.aspx").encodedQuery("LID=" + logId).build().toString();
+ public static ImmutablePair<StatusCode, String> uploadLogImage(final String logId, final String caption, final String description, final Uri imageUri) {
+ final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/seek/upload.aspx").build().toString();
- String page = Network.getResponseData(Network.getRequest(uri));
-
- if (!Login.getLoginStatus(page)) {
- // Login.isActualLoginStatus() was wrong, we are not logged in
- final StatusCode loginState = Login.login();
- if (loginState == StatusCode.NO_ERROR) {
- page = Network.getResponseData(Network.getRequest(uri));
- } else {
- Log.e("Image upload: No login (error: " + loginState + ')');
- return StatusCode.NOT_LOGGED_IN;
- }
+ final String page = Login.getRequestLogged(uri, new Parameters("LID=", logId));
+ if (StringUtils.isBlank(page)) {
+ Log.e("GCParser.uploadLogImage: No data from server");
+ return new ImmutablePair<StatusCode, String>(StatusCode.UNKNOWN_ERROR, null);
}
final String[] viewstates = Login.getViewstates(page);
@@ -1128,18 +1143,23 @@ public abstract class GCParser {
final File image = new File(imageUri.getPath());
final String response = Network.getResponseData(Network.postRequest(uri, uploadParams, "ctl00$ContentBody$ImageUploadControl1$uxFileUpload", "image/jpeg", image));
- MatcherWrapper matcherOK = new MatcherWrapper(GCConstants.PATTERN_OK_IMAGEUPLOAD, response);
+ final MatcherWrapper matcherUrl = new MatcherWrapper(GCConstants.PATTERN_IMAGE_UPLOAD_URL, response);
- if (matcherOK.find()) {
+ if (matcherUrl.find()) {
Log.i("Logimage successfully uploaded.");
-
- return StatusCode.NO_ERROR;
+ final String uploadedImageUrl = matcherUrl.group(1);
+ return ImmutablePair.of(StatusCode.NO_ERROR, uploadedImageUrl);
}
Log.e("GCParser.uploadLogIMage: Failed to upload image because of unknown error");
- return StatusCode.LOGIMAGE_POST_ERROR;
+ return ImmutablePair.of(StatusCode.LOGIMAGE_POST_ERROR, null);
}
+ /**
+ * Post a log to GC.com.
+ *
+ * @return status code of the upload and ID of the log
+ */
public static StatusCode postLogTrackable(final String tbid, final String trackingCode, final String[] viewstates,
final LogType logType, final int year, final int month, final int day, final String log) {
if (Login.isEmpty(viewstates)) {
@@ -1187,7 +1207,7 @@ public abstract class GCParser {
final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/track/log.aspx").encodedQuery("wid=" + tbid).build().toString();
final String page = Login.postRequestLogged(uri, params);
if (!Login.getLoginStatus(page)) {
- Log.e("GCParser.postLogTrackable: Can not log in geocaching");
+ Log.e("GCParser.postLogTrackable: Cannot log in geocaching");
return StatusCode.NOT_LOGGED_IN;
}
@@ -1198,7 +1218,7 @@ public abstract class GCParser {
Log.i("Log successfully posted to trackable #" + trackingCode);
return StatusCode.NO_ERROR;
}
- } catch (Exception e) {
+ } catch (final Exception e) {
Log.e("GCParser.postLogTrackable.check", e);
}
@@ -1215,14 +1235,14 @@ public abstract class GCParser {
*/
static boolean addToWatchlist(final Geocache cache) {
final String uri = "http://www.geocaching.com/my/watchlist.aspx?w=" + cache.getCacheId();
- String page = Login.postRequestLogged(uri, null);
+ final String page = Login.postRequestLogged(uri, null);
if (StringUtils.isBlank(page)) {
Log.e("GCParser.addToWatchlist: No data from server");
return false; // error
}
- boolean guidOnPage = cache.isGuidContainedInPage(page);
+ final boolean guidOnPage = cache.isGuidContainedInPage(page);
if (guidOnPage) {
Log.i("GCParser.addToWatchlist: cache is on watchlist");
cache.setOnWatchlist(true);
@@ -1256,7 +1276,7 @@ public abstract class GCParser {
Login.transferViewstates(page, params);
page = Network.getResponseData(Network.postRequest(uri, params));
- boolean guidOnPage = cache.isGuidContainedInPage(page);
+ final boolean guidOnPage = cache.isGuidContainedInPage(page);
if (!guidOnPage) {
Log.i("GCParser.removeFromWatchlist: cache removed from watchlist");
cache.setOnWatchlist(false);
@@ -1294,14 +1314,14 @@ public abstract class GCParser {
private static boolean changeFavorite(final Geocache cache, final boolean add) {
final String page = requestHtmlPage(cache.getGeocode(), null, "n", "0");
- final String userToken = BaseUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, "");
+ final String userToken = TextUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, "");
if (StringUtils.isEmpty(userToken)) {
return false;
}
final String uri = "http://www.geocaching.com/datastore/favorites.svc/update?u=" + userToken + "&f=" + Boolean.toString(add);
- HttpResponse response = Network.postRequest(uri, null);
+ final HttpResponse response = Network.postRequest(uri, null);
if (response != null && response.getStatusLine().getStatusCode() == 200) {
Log.i("GCParser.changeFavorite: cache added/removed to/from favorites");
@@ -1330,7 +1350,7 @@ public abstract class GCParser {
* Parse a trackable HTML description into a Trackable object
*
* @param page
- * the HTML page to parse, already processed through {@link BaseUtils#replaceWhitespace}
+ * the HTML page to parse, already processed through {@link TextUtils#replaceWhitespace}
* @return the parsed trackable, or null if none could be parsed
*/
static Trackable parseTrackable(final String page, final String possibleTrackingcode) {
@@ -1346,20 +1366,20 @@ public abstract class GCParser {
final Trackable trackable = new Trackable();
// trackable geocode
- trackable.setGeocode(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GEOCODE, true, trackable.getGeocode()));
+ trackable.setGeocode(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GEOCODE, true, StringUtils.upperCase(possibleTrackingcode)));
// trackable id
- trackable.setGuid(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GUID, true, trackable.getGuid()));
+ trackable.setGuid(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GUID, true, trackable.getGuid()));
// trackable icon
- trackable.setIconUrl(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_ICON, true, trackable.getIconUrl()));
+ trackable.setIconUrl(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_ICON, true, trackable.getIconUrl()));
// trackable name
- trackable.setName(Html.fromHtml(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_NAME, true, "")).toString());
+ trackable.setName(Html.fromHtml(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_NAME, true, "")).toString());
// trackable type
if (StringUtils.isNotBlank(trackable.getName())) {
- trackable.setType(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_TYPE, true, trackable.getType()));
+ trackable.setType(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_TYPE, true, trackable.getType()));
}
// trackable owner name
@@ -1369,13 +1389,13 @@ public abstract class GCParser {
trackable.setOwnerGuid(matcherOwner.group(1));
trackable.setOwner(matcherOwner.group(2).trim());
}
- } catch (Exception e) {
+ } catch (final Exception e) {
// failed to parse trackable owner name
Log.w("GCParser.parseTrackable: Failed to parse trackable owner name");
}
// trackable origin
- trackable.setOrigin(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_ORIGIN, true, trackable.getOrigin()));
+ trackable.setOrigin(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_ORIGIN, true, trackable.getOrigin()));
// trackable spotted
try {
@@ -1393,39 +1413,44 @@ public abstract class GCParser {
trackable.setSpottedType(Trackable.SPOTTED_USER);
}
- if (BaseUtils.matches(page, GCConstants.PATTERN_TRACKABLE_SPOTTEDUNKNOWN)) {
+ if (TextUtils.matches(page, GCConstants.PATTERN_TRACKABLE_SPOTTEDUNKNOWN)) {
trackable.setSpottedType(Trackable.SPOTTED_UNKNOWN);
}
- if (BaseUtils.matches(page, GCConstants.PATTERN_TRACKABLE_SPOTTEDOWNER)) {
+ if (TextUtils.matches(page, GCConstants.PATTERN_TRACKABLE_SPOTTEDOWNER)) {
trackable.setSpottedType(Trackable.SPOTTED_OWNER);
}
- } catch (Exception e) {
+ } catch (final Exception e) {
// failed to parse trackable last known place
Log.w("GCParser.parseTrackable: Failed to parse trackable last known place");
}
// released date - can be missing on the page
try {
- String releaseString = BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_RELEASES, false, null);
+ final String releaseString = TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_RELEASES, false, null);
if (releaseString != null) {
trackable.setReleased(dateTbIn1.parse(releaseString));
if (trackable.getReleased() == null) {
trackable.setReleased(dateTbIn2.parse(releaseString));
}
}
- } catch (ParseException e1) {
+ } catch (final ParseException e1) {
trackable.setReleased(null);
}
// trackable distance
- final String distance = BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_DISTANCE, false, null);
+ final String distance = TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_DISTANCE, false, null);
if (null != distance) {
- trackable.setDistance(DistanceParser.parseDistance(distance, Settings.isUseMetricUnits()));
+ try {
+ trackable.setDistance(DistanceParser.parseDistance(distance,
+ !Settings.isUseImperialUnits()));
+ } catch (final NumberFormatException e) {
+ Log.e("GCParser.parseTrackable: Failed to parse distance", e);
+ }
}
// trackable goal
- trackable.setGoal(convertLinks(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GOAL, true, trackable.getGoal())));
+ trackable.setGoal(convertLinks(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GOAL, true, trackable.getGoal())));
// trackable details & image
try {
@@ -1441,10 +1466,13 @@ public abstract class GCParser {
trackable.setDetails(convertLinks(details));
}
}
- } catch (Exception e) {
+ } catch (final Exception e) {
// failed to parse trackable details & image
Log.w("GCParser.parseTrackable: Failed to parse trackable details & image");
}
+ if (StringUtils.isEmpty(trackable.getDetails()) && page.contains(GCConstants.ERROR_TB_NOT_ACTIVATED)) {
+ trackable.setDetails(cgeoapplication.getInstance().getString(R.string.trackable_not_activated));
+ }
// trackable logs
try {
@@ -1462,7 +1490,7 @@ public abstract class GCParser {
long date = 0;
try {
date = Login.parseGcCustomDate(matcherLogs.group(2)).getTime();
- } catch (ParseException e) {
+ } catch (final ParseException e) {
}
final LogEntry logDone = new LogEntry(
@@ -1490,7 +1518,7 @@ public abstract class GCParser {
trackable.getLogs().add(logDone);
}
- } catch (Exception e) {
+ } catch (final Exception e) {
// failed to parse logs
Log.w("GCParser.parseCache: Failed to parse cache logs", e);
}
@@ -1560,10 +1588,10 @@ public abstract class GCParser {
}
} else {
// extract embedded JSON data from page
- rawResponse = BaseUtils.getMatch(page, GCConstants.PATTERN_LOGBOOK, "");
+ rawResponse = TextUtils.getMatch(page, GCConstants.PATTERN_LOGBOOK, "");
}
- List<LogEntry> logs = new ArrayList<LogEntry>();
+ final List<LogEntry> logs = new ArrayList<LogEntry>();
try {
final JSONObject resp = new JSONObject(rawResponse);
@@ -1584,7 +1612,7 @@ public abstract class GCParser {
long date = 0;
try {
date = Login.parseGcCustomDate(entry.getString("Visited")).getTime();
- } catch (ParseException e) {
+ } catch (final ParseException e) {
Log.e("GCParser.loadLogsFromDetails: failed to parse log date.");
}
@@ -1612,7 +1640,7 @@ public abstract class GCParser {
logs.add(logDone);
}
- } catch (JSONException e) {
+ } catch (final JSONException e) {
// failed to parse logs
Log.w("GCParser.loadLogsFromDetails: Failed to parse cache logs", e);
}
@@ -1628,30 +1656,26 @@ public abstract class GCParser {
final List<LogType> types = new ArrayList<LogType>();
final MatcherWrapper typeBoxMatcher = new MatcherWrapper(GCConstants.PATTERN_TYPEBOX, page);
- String typesText = null;
- if (typeBoxMatcher.find()) {
- if (typeBoxMatcher.groupCount() > 0) {
- typesText = typeBoxMatcher.group(1);
- }
- }
-
- if (typesText != null) {
-
+ if (typeBoxMatcher.find() && typeBoxMatcher.groupCount() > 0) {
+ final String typesText = typeBoxMatcher.group(1);
final MatcherWrapper typeMatcher = new MatcherWrapper(GCConstants.PATTERN_TYPE2, typesText);
while (typeMatcher.find()) {
if (typeMatcher.groupCount() > 1) {
try {
- int type = Integer.parseInt(typeMatcher.group(2));
+ final int type = Integer.parseInt(typeMatcher.group(2));
if (type > 0) {
types.add(LogType.getById(type));
}
- } catch (NumberFormatException e) {
+ } catch (final NumberFormatException e) {
Log.e("Error parsing log types", e);
}
}
}
}
+ // we don't support this log type
+ types.remove(LogType.UPDATE_COORDINATES);
+
return types;
}
@@ -1690,7 +1714,7 @@ public abstract class GCParser {
Log.i("Trackable in inventory (#" + entry.ctl + "/" + entry.id + "): " + entry.trackCode + " - " + entry.name);
trackableLogs.add(entry);
}
- } catch (NumberFormatException e) {
+ } catch (final NumberFormatException e) {
Log.e("GCParser.parseTrackableLog", e);
}
}
@@ -1719,10 +1743,10 @@ public abstract class GCParser {
//cache.setLogs(loadLogsFromDetails(page, cache, false));
if (Settings.isFriendLogsWanted()) {
CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_logs);
- List<LogEntry> allLogs = cache.getLogs();
- List<LogEntry> friendLogs = loadLogsFromDetails(page, cache, true, false);
+ final List<LogEntry> allLogs = cache.getLogs();
+ final List<LogEntry> friendLogs = loadLogsFromDetails(page, cache, true, false);
if (friendLogs != null) {
- for (LogEntry log : friendLogs) {
+ for (final LogEntry log : friendLogs) {
if (allLogs.contains(log)) {
allLogs.get(allLogs.indexOf(log)).friend = true;
} else {
@@ -1732,16 +1756,6 @@ public abstract class GCParser {
}
}
- if (Settings.isElevationWanted()) {
- if (CancellableHandler.isCancelled(handler)) {
- return;
- }
- CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_elevation);
- if (cache.getCoords() != null) {
- cache.setElevation(cache.getCoords().getElevation());
- }
- }
-
if (Settings.isRatingWanted()) {
if (CancellableHandler.isCancelled(handler)) {
return;
@@ -1766,7 +1780,7 @@ public abstract class GCParser {
public static boolean editModifiedCoordinates(Geocache cache, Geopoint wpt) {
final String page = requestHtmlPage(cache.getGeocode(), null, "n", "0");
- final String userToken = BaseUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, "");
+ final String userToken = TextUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, "");
if (StringUtils.isEmpty(userToken)) {
return false;
}
@@ -1785,7 +1799,7 @@ public abstract class GCParser {
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);
+ final HttpResponse response = Network.postJsonRequest(uriPrefix + uriSuffix, jo);
Log.i("Sending to " + uriPrefix + uriSuffix + " :" + jo.toString());
if (response != null && response.getStatusLine().getStatusCode() == 200) {
@@ -1793,11 +1807,42 @@ public abstract class GCParser {
return true;
}
- } catch (JSONException e) {
+ } catch (final JSONException e) {
Log.e("Unknown exception with json wrap code", e);
}
Log.e("GCParser.deleteModifiedCoordinates - cannot delete modified coords");
return false;
}
+ public static boolean uploadPersonalNote(Geocache cache) {
+ final String page = requestHtmlPage(cache.getGeocode(), null, "n", "0");
+ final String userToken = TextUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, "");
+ if (StringUtils.isEmpty(userToken)) {
+ return false;
+ }
+
+ try {
+ final JSONObject jo = new JSONObject()
+ .put("dto", (new JSONObject()
+ .put("et", cache.getPersonalNote())
+ .put("ut", userToken)));
+
+ final String uriSuffix = "SetUserCacheNote";
+
+ final String uriPrefix = "http://www.geocaching.com/seek/cache_details.aspx/";
+ final HttpResponse response = Network.postJsonRequest(uriPrefix + uriSuffix, jo);
+ Log.i("Sending to " + uriPrefix + uriSuffix + " :" + jo.toString());
+
+ if (response != null && response.getStatusLine().getStatusCode() == 200) {
+ Log.i("GCParser.uploadPersonalNote - uploaded to GC.com");
+ return true;
+ }
+
+ } catch (final JSONException e) {
+ Log.e("Unknown exception with json wrap code", e);
+ }
+ Log.e("GCParser.uploadPersonalNote - cannot upload personal note");
+ return false;
+ }
+
}
diff --git a/main/src/cgeo/geocaching/connector/gc/IconDecoder.java b/main/src/cgeo/geocaching/connector/gc/IconDecoder.java
index d3a2959..ed44392 100644
--- a/main/src/cgeo/geocaching/connector/gc/IconDecoder.java
+++ b/main/src/cgeo/geocaching/connector/gc/IconDecoder.java
@@ -1,7 +1,7 @@
package cgeo.geocaching.connector.gc;
import cgeo.geocaching.Geocache;
-import cgeo.geocaching.Settings;
+import cgeo.geocaching.settings.Settings;
import cgeo.geocaching.enumerations.CacheType;
import android.graphics.Bitmap;
@@ -21,7 +21,7 @@ public abstract class IconDecoder {
private static final int CT_MEGAEVENT = 7;
private static final int CT_CITO = 8;
private static final int CT_WEBCAM = 9;
- private static final int CT_WHEREIGO = 10;
+ private static final int CT_WHERIGO = 10;
private static final int CT_VIRTUAL = 11;
private static final int CT_LETTERBOX = 12;
@@ -116,7 +116,7 @@ public abstract class IconDecoder {
case CT_WEBCAM:
cache.setType(CacheType.WEBCAM);
return true;
- case CT_WHEREIGO:
+ case CT_WHERIGO:
cache.setType(CacheType.WHERIGO);
return true;
case CT_VIRTUAL:
@@ -395,12 +395,12 @@ public abstract class IconDecoder {
if (g < 71) {
return CT_MYSTERY;
}
- return r < 153 ? CT_WHEREIGO : CT_WEBCAM;
+ return r < 153 ? CT_WHERIGO : CT_WEBCAM;
}
if (b < 167) {
return r < 157 ? CT_TRADITIONAL : CT_WEBCAM;
}
- return CT_WHEREIGO;
+ return CT_WHERIGO;
}
if (g < 199) {
if (r < 142) {
@@ -450,7 +450,7 @@ public abstract class IconDecoder {
if (b < 252) {
if (r < 243) {
if (r < 225) {
- return CT_WHEREIGO;
+ return CT_WHERIGO;
}
if (b < 232) {
if (g < 228) {
@@ -459,14 +459,14 @@ public abstract class IconDecoder {
return r < 231 ? CT_VIRTUAL : CT_TRADITIONAL;
}
if (r < 236) {
- return CT_WHEREIGO;
+ return CT_WHERIGO;
}
- return r < 240 ? CT_WEBCAM : CT_WHEREIGO;
+ return r < 240 ? CT_WEBCAM : CT_WHERIGO;
}
if (g < 247) {
return r < 245 ? CT_WEBCAM : CT_FOUND;
}
- return CT_WHEREIGO;
+ return CT_WHERIGO;
}
return CT_LETTERBOX;
}
diff --git a/main/src/cgeo/geocaching/connector/gc/Login.java b/main/src/cgeo/geocaching/connector/gc/Login.java
index 7351311..4b4c93f 100644
--- a/main/src/cgeo/geocaching/connector/gc/Login.java
+++ b/main/src/cgeo/geocaching/connector/gc/Login.java
@@ -1,16 +1,16 @@
package cgeo.geocaching.connector.gc;
import cgeo.geocaching.R;
-import cgeo.geocaching.Settings;
+import cgeo.geocaching.settings.Settings;
import cgeo.geocaching.cgeoapplication;
import cgeo.geocaching.enumerations.StatusCode;
import cgeo.geocaching.network.Cookies;
import cgeo.geocaching.network.HtmlImage;
import cgeo.geocaching.network.Network;
import cgeo.geocaching.network.Parameters;
-import cgeo.geocaching.utils.BaseUtils;
import cgeo.geocaching.utils.Log;
import cgeo.geocaching.utils.MatcherWrapper;
+import cgeo.geocaching.utils.TextUtils;
import ch.boye.httpclientandroidlib.HttpResponse;
@@ -52,9 +52,9 @@ public abstract class Login {
"dd/MM/yyyy"
};
- Map<String, SimpleDateFormat> map = new HashMap<String, SimpleDateFormat>();
+ final Map<String, SimpleDateFormat> map = new HashMap<String, SimpleDateFormat>();
- for (String format : formats) {
+ for (final String format : formats) {
map.put(format, new SimpleDateFormat(format, Locale.ENGLISH));
}
@@ -66,7 +66,7 @@ public abstract class Login {
}
private static StatusCode login(boolean retry) {
- final ImmutablePair<String, String> login = Settings.getLogin();
+ final ImmutablePair<String, String> login = Settings.getGcLogin();
if (login == null || StringUtils.isEmpty(login.left) || StringUtils.isEmpty(login.right)) {
Login.setActualStatus(cgeoapplication.getInstance().getString(R.string.err_login));
@@ -77,7 +77,7 @@ public abstract class Login {
Login.setActualStatus(cgeoapplication.getInstance().getString(R.string.init_login_popup_working));
HttpResponse loginResponse = Network.getRequest("https://www.geocaching.com/login/default.aspx");
String loginData = Network.getResponseData(loginResponse);
- if (loginResponse != null && loginResponse.getStatusLine().getStatusCode() == 503 && BaseUtils.matches(loginData, GCConstants.PATTERN_MAINTENANCE)) {
+ if (loginResponse != null && loginResponse.getStatusLine().getStatusCode() == 503 && TextUtils.matches(loginData, GCConstants.PATTERN_MAINTENANCE)) {
return StatusCode.MAINTENANCE;
}
@@ -147,9 +147,9 @@ public abstract class Login {
}
public static StatusCode logout() {
- HttpResponse logoutResponse = Network.getRequest("https://www.geocaching.com/login/default.aspx?RESET=Y&redir=http%3a%2f%2fwww.geocaching.com%2fdefault.aspx%3f");
- String logoutData = Network.getResponseData(logoutResponse);
- if (logoutResponse != null && logoutResponse.getStatusLine().getStatusCode() == 503 && BaseUtils.matches(logoutData, GCConstants.PATTERN_MAINTENANCE)) {
+ final HttpResponse logoutResponse = Network.getRequest("https://www.geocaching.com/login/default.aspx?RESET=Y&redir=http%3a%2f%2fwww.geocaching.com%2fdefault.aspx%3f");
+ final String logoutData = Network.getResponseData(logoutResponse);
+ if (logoutResponse != null && logoutResponse.getStatusLine().getStatusCode() == 503 && TextUtils.matches(logoutData, GCConstants.PATTERN_MAINTENANCE)) {
return StatusCode.MAINTENANCE;
}
@@ -205,17 +205,17 @@ public abstract class Login {
setActualStatus(cgeoapplication.getInstance().getString(R.string.init_login_popup_ok));
// on every page except login page
- setActualLoginStatus(BaseUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME));
+ setActualLoginStatus(TextUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME));
if (isActualLoginStatus()) {
- setActualUserName(BaseUtils.getMatch(page, GCConstants.PATTERN_LOGIN_NAME, true, "???"));
+ setActualUserName(TextUtils.getMatch(page, GCConstants.PATTERN_LOGIN_NAME, true, "???"));
int cachesCount = 0;
try {
- cachesCount = Integer.parseInt(BaseUtils.getMatch(page, GCConstants.PATTERN_CACHES_FOUND, true, "0").replaceAll("[,.]", ""));
- } catch (NumberFormatException e) {
+ cachesCount = Integer.parseInt(TextUtils.getMatch(page, GCConstants.PATTERN_CACHES_FOUND, true, "0").replaceAll("[,.]", ""));
+ } catch (final NumberFormatException e) {
Log.e("getLoginStatus: bad cache count", e);
}
setActualCachesFound(cachesCount);
- Settings.setMemberStatus(BaseUtils.getMatch(page, GCConstants.PATTERN_MEMBER_STATUS, true, null));
+ Settings.setMemberStatus(TextUtils.getMatch(page, GCConstants.PATTERN_MEMBER_STATUS, true, null));
if ( page.contains(GCConstants.MEMBER_STATUS_RENEW) ) {
Settings.setMemberStatus(GCConstants.MEMBER_STATUS_PM);
}
@@ -223,7 +223,7 @@ public abstract class Login {
}
// login page
- setActualLoginStatus(BaseUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME_LOGIN_PAGE));
+ setActualLoginStatus(TextUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME_LOGIN_PAGE));
if (isActualLoginStatus()) {
setActualUserName(Settings.getUsername());
// number of caches found is not part of this page
@@ -260,23 +260,23 @@ public abstract class Login {
public static BitmapDrawable downloadAvatarAndGetMemberStatus() {
try {
- final String profile = BaseUtils.replaceWhitespace(Network.getResponseData(Network.getRequest("http://www.geocaching.com/my/")));
+ final String profile = TextUtils.replaceWhitespace(Network.getResponseData(Network.getRequest("http://www.geocaching.com/my/")));
- Settings.setMemberStatus(BaseUtils.getMatch(profile, GCConstants.PATTERN_MEMBER_STATUS, true, null));
+ Settings.setMemberStatus(TextUtils.getMatch(profile, GCConstants.PATTERN_MEMBER_STATUS, true, null));
if (profile.contains(GCConstants.MEMBER_STATUS_RENEW)) {
Settings.setMemberStatus(GCConstants.MEMBER_STATUS_PM);
}
- setActualCachesFound(Integer.parseInt(BaseUtils.getMatch(profile, GCConstants.PATTERN_CACHES_FOUND, true, "-1").replaceAll("[,.]", "")));
+ setActualCachesFound(Integer.parseInt(TextUtils.getMatch(profile, GCConstants.PATTERN_CACHES_FOUND, true, "-1").replaceAll("[,.]", "")));
- final String avatarURL = BaseUtils.getMatch(profile, GCConstants.PATTERN_AVATAR_IMAGE_PROFILE_PAGE, false, null);
+ final String avatarURL = TextUtils.getMatch(profile, GCConstants.PATTERN_AVATAR_IMAGE_PROFILE_PAGE, false, null);
if (null != avatarURL) {
final HtmlImage imgGetter = new HtmlImage("", false, 0, false);
return imgGetter.getDrawable(avatarURL);
}
// No match? There may be no avatar set by user.
Log.d("No avatar set for user");
- } catch (Exception e) {
+ } catch (final Exception e) {
Log.w("Error when retrieving user avatar", e);
}
return null;
@@ -294,7 +294,7 @@ public abstract class Login {
return;
}
- String customDate = BaseUtils.getMatch(result, GCConstants.PATTERN_CUSTOMDATE, true, null);
+ final String customDate = TextUtils.getMatch(result, GCConstants.PATTERN_CUSTOMDATE, true, null);
if (null != customDate) {
Settings.setGcCustomDate(customDate);
}
@@ -310,14 +310,14 @@ public abstract class Login {
if (gcCustomDateFormats.containsKey(format)) {
try {
return gcCustomDateFormats.get(format).parse(trimmed);
- } catch (ParseException e) {
+ } catch (final ParseException e) {
}
}
- for (SimpleDateFormat sdf : gcCustomDateFormats.values()) {
+ for (final SimpleDateFormat sdf : gcCustomDateFormats.values()) {
try {
return sdf.parse(trimmed);
- } catch (ParseException e) {
+ } catch (final ParseException e) {
}
}
@@ -347,7 +347,7 @@ public abstract class Login {
return true;
}
- for (String s : a) {
+ for (final String s : a) {
if (StringUtils.isNotEmpty(s)) {
return false;
}
@@ -373,24 +373,24 @@ public abstract class Login {
if (matcherViewstateCount.find()) {
try {
count = Integer.parseInt(matcherViewstateCount.group(1));
- } catch (NumberFormatException e) {
+ } catch (final NumberFormatException e) {
Log.e("getViewStates", e);
}
}
- String[] viewstates = new String[count];
+ final String[] viewstates = new String[count];
// Get the viewstates
final MatcherWrapper matcherViewstates = new MatcherWrapper(GCConstants.PATTERN_VIEWSTATES, page);
while (matcherViewstates.find()) {
- String sno = matcherViewstates.group(1); // number of viewstate
+ final String sno = matcherViewstates.group(1); // number of viewstate
int no;
if (StringUtils.isEmpty(sno)) {
no = 0;
} else {
try {
no = Integer.parseInt(sno);
- } catch (NumberFormatException e) {
+ } catch (final NumberFormatException e) {
Log.e("getViewStates", e);
no = 0;
}
@@ -436,17 +436,17 @@ public abstract class Login {
* @return
*/
public static String postRequestLogged(final String uri, final Parameters params) {
- HttpResponse response = Network.postRequest(uri, params);
- String data = Network.getResponseData(response);
+ final String data = Network.getResponseData(Network.postRequest(uri, params));
- if (!getLoginStatus(data)) {
- if (login() == StatusCode.NO_ERROR) {
- response = Network.postRequest(uri, params);
- data = Network.getResponseData(response);
- } else {
- Log.i("Working as guest.");
- }
+ if (getLoginStatus(data)) {
+ return data;
+ }
+
+ if (login() == StatusCode.NO_ERROR) {
+ return Network.getResponseData(Network.postRequest(uri, params));
}
+
+ Log.i("Working as guest.");
return data;
}
@@ -458,26 +458,37 @@ public abstract class Login {
* @return
*/
public static String getRequestLogged(final String uri, final Parameters params) {
- final String data = Network.getResponseData(Network.getRequest(uri, params));
+ final String data = Network.getResponseData(Network.getRequest(uri, params), canRemoveWhitespace(uri));
if (getLoginStatus(data)) {
return data;
}
if (login() == StatusCode.NO_ERROR) {
- return Network.getResponseData(Network.getRequest(uri, params));
+ return Network.getResponseData(Network.getRequest(uri, params), canRemoveWhitespace(uri));
}
Log.w("Working as guest.");
return data;
}
+ /**
+ * Unfortunately the cache details page contains user generated whitespace in the personal note, therefore we cannot
+ * remove the white space from cache details pages.
+ *
+ * @param uri
+ * @return
+ */
+ private static boolean canRemoveWhitespace(final String uri) {
+ return !StringUtils.contains(uri, "cache_details");
+ }
+
/** Get user session & session token from the Live Map. Needed for following requests */
public static String[] getMapTokens() {
final HttpResponse response = Network.getRequest(GCConstants.URL_LIVE_MAP);
final String data = Network.getResponseData(response);
- final String userSession = BaseUtils.getMatch(data, GCConstants.PATTERN_USERSESSION, "");
- final String sessionToken = BaseUtils.getMatch(data, GCConstants.PATTERN_SESSIONTOKEN, "");
+ final String userSession = TextUtils.getMatch(data, GCConstants.PATTERN_USERSESSION, "");
+ final String sessionToken = TextUtils.getMatch(data, GCConstants.PATTERN_SESSIONTOKEN, "");
return new String[] { userSession, sessionToken };
}
}
diff --git a/main/src/cgeo/geocaching/connector/gc/SearchHandler.java b/main/src/cgeo/geocaching/connector/gc/SearchHandler.java
index 840cad1..45832e4 100644
--- a/main/src/cgeo/geocaching/connector/gc/SearchHandler.java
+++ b/main/src/cgeo/geocaching/connector/gc/SearchHandler.java
@@ -102,7 +102,7 @@ public class SearchHandler extends Handler {
imgHandler.sendEmptyMessage(0);
} catch (IOException e) {
- Log.e("Failed to download reCAPTCHA image");
+ Log.e("Failed to download reCAPTCHA image", e);
}
}
}
diff --git a/main/src/cgeo/geocaching/connector/gc/Tile.java b/main/src/cgeo/geocaching/connector/gc/Tile.java
index 0e5ffe7..ec90036 100644
--- a/main/src/cgeo/geocaching/connector/gc/Tile.java
+++ b/main/src/cgeo/geocaching/connector/gc/Tile.java
@@ -37,6 +37,7 @@ public class Tile {
public static final int TILE_SIZE = 256;
public static final int ZOOMLEVEL_MAX = 18;
public static final int ZOOMLEVEL_MIN = 0;
+ public static final int ZOOMLEVEL_MIN_PERSONALIZED = 12;
static final int[] NUMBER_OF_TILES = new int[ZOOMLEVEL_MAX - ZOOMLEVEL_MIN + 1];
static final int[] NUMBER_OF_PIXELS = new int[ZOOMLEVEL_MAX - ZOOMLEVEL_MIN + 1];
@@ -243,7 +244,7 @@ public class Tile {
try {
return response != null ? BitmapFactory.decodeStream(response.getEntity().getContent()) : null;
} catch (IOException e) {
- Log.e("cgBase.requestMapTile() " + e.getMessage());
+ Log.e("Tile.requestMapTile() ", e);
}
return null;
}
@@ -252,6 +253,10 @@ public class Tile {
return viewPort.contains(point);
}
+ public Viewport getViewport() {
+ return viewPort;
+ }
+
/**
* Calculate needed tiles for the given viewport to cover it with
* max 2x2 tiles
diff --git a/main/src/cgeo/geocaching/connector/oc/AttributeParser.java b/main/src/cgeo/geocaching/connector/oc/AttributeParser.java
new file mode 100644
index 0000000..63bee77
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/oc/AttributeParser.java
@@ -0,0 +1,327 @@
+// This is a generated file, do not change manually!
+
+package cgeo.geocaching.connector.oc;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class AttributeParser {
+
+ private final static Map<String, Integer> attrMapDe;
+ private final static Map<String, Integer> attrMapPl;
+
+ static {
+ attrMapDe = new HashMap<String, Integer>();
+ attrMapPl = new HashMap<String, Integer>();
+
+ // last header line
+ attrMapDe.put("Listed at Opencaching only", 6);
+ attrMapDe.put("Dostępna tylko na Opencaching", 6);
+ attrMapDe.put("Nur bei Opencaching logbar", 6);
+ attrMapDe.put("Solo loggeable en Opencaching", 6);
+ attrMapDe.put("Loggabile solo su Opencaching", 6);
+ attrMapPl.put("Near a Survey Marker", 54);
+ attrMapPl.put("W pobliżu punktu geodezyjnego", 54);
+ attrMapPl.put("Whereigo Cache", 55);
+ attrMapPl.put("Whereigo Cache", 55);
+ attrMapPl.put("Whereigo Cache", 55);
+ attrMapDe.put("Letterbox Cache", 8);
+ attrMapPl.put("Letterbox Cache", 56);
+ attrMapDe.put("Skrzynka typu Letterbox", 8);
+ attrMapPl.put("Skrzynka typu Letterbox", 56);
+ attrMapDe.put("Letterbox (benötigt Stempel)", 8);
+ attrMapPl.put("Letterbox (benötigt Stempel)", 56);
+ attrMapDe.put("Letterbox (necesita un estampador)", 8);
+ attrMapPl.put("Letterbox (necesita un estampador)", 56);
+ attrMapDe.put("Letterbox (richiede un timbro)", 8);
+ attrMapPl.put("Letterbox (richiede un timbro)", 56);
+ attrMapPl.put("GeoHotel", 43);
+ attrMapPl.put("GeoHotel", 43);
+ attrMapPl.put("GeoHotel", 43);
+ attrMapPl.put("Magnetic cache", 49);
+ attrMapPl.put("Przyczepiona magnesem", 49);
+ attrMapPl.put("magnetischer Cache", 49);
+ attrMapPl.put("Description contains an audio file", 50);
+ attrMapPl.put("Opis zawiera plik audio", 50);
+ attrMapPl.put("Offset cache", 51);
+ attrMapPl.put("Offset cache", 51);
+ attrMapPl.put("Peilungscache", 51);
+ attrMapPl.put("Garmin's wireless beacon", 52);
+ attrMapPl.put("Beacon - Garmin Chirp", 52);
+ attrMapPl.put("Funksignal – Garmin Chirp", 52);
+ attrMapPl.put("Dead Drop USB cache", 53);
+ attrMapPl.put("Dead Drop USB skrzynka", 53);
+ attrMapDe.put("Has a moving target", 31);
+ attrMapDe.put("bewegliches Ziel", 31);
+ attrMapDe.put("Objetivo en movimiento", 31);
+ attrMapDe.put("Oggetto in movimento", 31);
+ attrMapDe.put("Webcam Cache", 32);
+ attrMapDe.put("Webcam Cache", 32);
+ attrMapDe.put("Webcam Cache", 32);
+ attrMapDe.put("Webcam Cache", 32);
+ attrMapDe.put("Other cache type", 57);
+ attrMapDe.put("sonstiger Cachetyp", 57);
+ attrMapDe.put("Otro tipo de cache", 57);
+ attrMapDe.put("Altro tipo di cache", 57);
+ attrMapDe.put("Investigation required", 54);
+ attrMapDe.put("Recherche", 54);
+ attrMapDe.put("Investigación", 54);
+ attrMapDe.put("Ricerca", 54);
+ attrMapDe.put("Puzzle / Mystery", 55);
+ attrMapDe.put("Rätsel", 55);
+ attrMapDe.put("Puzzle / Misterio", 55);
+ attrMapDe.put("Puzzle / Mystery", 55);
+ attrMapDe.put("Arithmetical problem", 56);
+ attrMapDe.put("Rechenaufgabe", 56);
+ attrMapDe.put("Problema matemático", 56);
+ attrMapDe.put("Problema matematico", 56);
+ attrMapDe.put("Ask owner for start conditions", 58);
+ attrMapDe.put("Startbedingungen beim Owner erfragen", 58);
+ attrMapDe.put("Ask owner for start conditions", 58);
+ attrMapDe.put("Ask owner for start conditions", 58);
+ attrMapPl.put("Wheelchair accessible", 44);
+ attrMapPl.put("Dostępna dla niepełnosprawnych", 44);
+ attrMapPl.put("rollstuhltauglich", 44);
+ attrMapDe.put("Near the parking area", 24);
+ attrMapDe.put("nahe beim Auto", 24);
+ attrMapDe.put("Cerca de un Parking", 24);
+ attrMapDe.put("Vicino all'area di parcheggio", 24);
+ attrMapPl.put("Access only by walk", 84);
+ attrMapPl.put("Dostępna tylko pieszo", 84);
+ attrMapDe.put("Long walk", 25);
+ attrMapDe.put("längere Wanderung", 25);
+ attrMapDe.put("Larga caminata", 25);
+ attrMapDe.put("Lunga camminata", 25);
+ attrMapDe.put("Swamp, marsh or wading", 26);
+ attrMapDe.put("sumpfig/matschiges Gelände / waten", 26);
+ attrMapDe.put("Pantano / terreno fangoso", 26);
+ attrMapDe.put("Palude o marcita", 26);
+ attrMapDe.put("Hilly area", 27);
+ attrMapDe.put("hügeliges Gelände", 27);
+ attrMapDe.put("Terreno montañoso", 27);
+ attrMapDe.put("Area collinare", 27);
+ attrMapDe.put("Some climbing (no gear needed)", 28);
+ attrMapDe.put("leichtes Klettern (ohne Ausrüstung)", 28);
+ attrMapDe.put("fácil de subir (sin equipo)", 28);
+ attrMapDe.put("Arrampicata (attrezzatura non necessaria)", 28);
+ attrMapDe.put("Swimming required", 29);
+ attrMapDe.put("Schwimmen erforderlich", 29);
+ attrMapDe.put("Requiere nadar", 29);
+ attrMapDe.put("Nuoto necessario", 29);
+ attrMapDe.put("Access or parking fee", 36);
+ attrMapDe.put("Zugangs- bzw. Parkentgelt", 36);
+ attrMapDe.put("Acceso o parking pagando", 36);
+ attrMapDe.put("Tassa di ingresso o di parcheggio", 36);
+ attrMapPl.put("Bikes allowed", 85);
+ attrMapPl.put("Dostępna rowerem", 85);
+ attrMapPl.put("Hidden in natural surroundings (forests, mountains, etc.)", 60);
+ attrMapPl.put("Umiejscowiona na łonie natury (lasy, góry, itp.)", 60);
+ attrMapPl.put("Historic site", 61);
+ attrMapPl.put("Miejsce historyczne", 61);
+ attrMapDe.put("Point of interest", 30);
+ attrMapDe.put("interessanter Ort", 30);
+ attrMapDe.put("Punto de interes", 30);
+ attrMapDe.put("Punto di interesse", 30);
+ attrMapDe.put("Hidden wihin enclosed rooms (caves, buildings etc.)", 33);
+ attrMapDe.put("in geschlossenen Räumen (Höhle, Gebäude, etc.)", 33);
+ attrMapDe.put("en espacios confinados (cuevas, edificios, etc)", 33);
+ attrMapDe.put("All'interno di stanze chiuse (caverne, edifici, ecc.)", 33);
+ attrMapDe.put("Hidden under water", 34);
+ attrMapDe.put("Im Wasser versteckt", 34);
+ attrMapDe.put("En el agua", 34);
+ attrMapDe.put("Nell'acqua", 34);
+ attrMapDe.put("Parking area nearby", 18);
+ attrMapDe.put("Parkplatz in der Nähe", 18);
+ attrMapDe.put("Parking cercano", 18);
+ attrMapDe.put("Parcheggio nei pressi", 18);
+ attrMapDe.put("Public transportation", 19);
+ attrMapDe.put("erreichbar mit ÖVM", 19);
+ attrMapDe.put("Transporte Público", 19);
+ attrMapDe.put("Trasporto pubblico", 19);
+ attrMapDe.put("Drinking water nearby", 20);
+ attrMapDe.put("Trinkwasser in der Nähe", 20);
+ attrMapDe.put("Agua potable en las cercanias", 20);
+ attrMapDe.put("Acqua potabile nei pressi", 20);
+ attrMapDe.put("Public restrooms nearby", 21);
+ attrMapDe.put("öffentliche Toilette in der Nähe", 21);
+ attrMapDe.put("Aseos públicos cercanos", 21);
+ attrMapDe.put("Bagni pubblici nei pressi", 21);
+ attrMapDe.put("Public phone nearby", 22);
+ attrMapDe.put("Telefon in der Nähe", 22);
+ attrMapDe.put("Teléfono Público en las cercanias", 22);
+ attrMapDe.put("Telefono pubblico nei pressi", 22);
+ attrMapDe.put("First aid available", 23);
+ attrMapDe.put("Erste Hilfe verfügbar", 23);
+ attrMapDe.put("Disponible socorro rapido", 23);
+ attrMapDe.put("Disponibile pronto soccorso", 23);
+ attrMapDe.put("Available 24/7", 38);
+ attrMapDe.put("rund um die Uhr machbar", 38);
+ attrMapDe.put("Disponible las 24 horas", 38);
+ attrMapDe.put("Disponibile 24 ore", 38);
+ attrMapDe.put("Not 24/7", 39);
+ attrMapPl.put("Not 24/7", 80);
+ attrMapDe.put("Dostępna w określonych godzinach", 39);
+ attrMapPl.put("Dostępna w określonych godzinach", 80);
+ attrMapDe.put("nur zu bestimmten Uhrzeiten", 39);
+ attrMapPl.put("nur zu bestimmten Uhrzeiten", 80);
+ attrMapDe.put("Sólo disponible a ciertas horas", 39);
+ attrMapPl.put("Sólo disponible a ciertas horas", 80);
+ attrMapDe.put("Disponibile solo in certi orari", 39);
+ attrMapPl.put("Disponibile solo in certi orari", 80);
+ attrMapDe.put("Not recommended at night", 40);
+ attrMapDe.put("nur tagüber", 40);
+ attrMapDe.put("solo por el día", 40);
+ attrMapDe.put("solo di giorno", 40);
+ attrMapPl.put("Recommended at night", 91);
+ attrMapPl.put("Zalecane szukanie nocÄ…", 91);
+ attrMapPl.put("am besten nachts findbar", 91);
+ attrMapDe.put("Only at night", 1);
+ attrMapDe.put("nur bei Nacht", 1);
+ attrMapDe.put("Sólo por la noche", 1);
+ attrMapDe.put("Solo di notte", 1);
+ attrMapDe.put("All seasons", 42);
+ attrMapDe.put("ganzjähig zugänglich", 42);
+ attrMapDe.put("Todas las temporadas", 42);
+ attrMapDe.put("Tutte le stagioni", 42);
+ attrMapDe.put("Only available during specified seasons", 60);
+ attrMapDe.put("Nur zu bestimmten Zeiten im Jahr", 60);
+ attrMapDe.put("Sólo disponible durante las estaciones especificadas", 60);
+ attrMapDe.put("Disponibile solo in certe stagioni", 60);
+ attrMapDe.put("Breeding season / protected nature", 43);
+ attrMapDe.put("Brutsaison / Naturschutz", 43);
+ attrMapDe.put("Temporada de reproducción / protección de la naturaleza", 43);
+ attrMapDe.put("Stagione di riproduzione / natura protetta", 43);
+ attrMapDe.put("Available during winter", 44);
+ attrMapDe.put("schneesicheres Versteck", 44);
+ attrMapDe.put("Nieve en el escondite", 44);
+ attrMapDe.put("Luogo a prova di neve", 44);
+ attrMapDe.put("Not at high water level", 41);
+ attrMapDe.put("nicht bei Hochwasser oder Flut", 41);
+ attrMapDe.put("Compass required", 47);
+ attrMapPl.put("Compass required", 47);
+ attrMapDe.put("Potrzebny kompas", 47);
+ attrMapPl.put("Potrzebny kompas", 47);
+ attrMapDe.put("Kompass", 47);
+ attrMapPl.put("Kompass", 47);
+ attrMapDe.put("Brújula", 47);
+ attrMapPl.put("Brújula", 47);
+ attrMapDe.put("Bussola", 47);
+ attrMapPl.put("Bussola", 47);
+ attrMapPl.put("Take something to write", 48);
+ attrMapPl.put("Weź coś do pisania", 48);
+ attrMapPl.put("You may need a shovel", 81);
+ attrMapPl.put("Potrzebna łopatka", 81);
+ attrMapDe.put("Flashlight required", 48);
+ attrMapPl.put("Flashlight required", 82);
+ attrMapDe.put("Potrzebna latarka", 48);
+ attrMapPl.put("Potrzebna latarka", 82);
+ attrMapDe.put("Taschenlampe", 48);
+ attrMapPl.put("Taschenlampe", 82);
+ attrMapDe.put("Linterna", 48);
+ attrMapPl.put("Linterna", 82);
+ attrMapDe.put("Lampada tascabile", 48);
+ attrMapPl.put("Lampada tascabile", 82);
+ attrMapDe.put("Climbing gear required", 49);
+ attrMapDe.put("Kletterzeug", 49);
+ attrMapDe.put("Equipo de escalada", 49);
+ attrMapDe.put("Attrezzatura per arrampicata", 49);
+ attrMapDe.put("Cave equipment required", 50);
+ attrMapDe.put("Höhlenzeug", 50);
+ attrMapDe.put("Equipación para cuevas", 50);
+ attrMapDe.put("Attrezzatura per grotta", 50);
+ attrMapDe.put("Diving equipment required", 51);
+ attrMapDe.put("Taucherausrüstung", 51);
+ attrMapDe.put("Diving equipment", 51);
+ attrMapDe.put("Equipo de buceo", 51);
+ attrMapDe.put("Special tools required", 46);
+ attrMapPl.put("Special tools required", 83);
+ attrMapDe.put("Wymagany dodatkowy sprzęt", 46);
+ attrMapPl.put("Wymagany dodatkowy sprzęt", 83);
+ attrMapDe.put("spezielle Ausrüstung", 46);
+ attrMapPl.put("spezielle Ausrüstung", 83);
+ attrMapDe.put("Equipamiento especial", 46);
+ attrMapPl.put("Equipamiento especial", 83);
+ attrMapDe.put("Equipaggiamento speciale", 46);
+ attrMapPl.put("Equipaggiamento speciale", 83);
+ attrMapDe.put("Requires a boat", 52);
+ attrMapPl.put("Requires a boat", 86);
+ attrMapDe.put("Wymaga sprzętu pływającego", 52);
+ attrMapPl.put("Wymaga sprzętu pływającego", 86);
+ attrMapDe.put("Wasserfahrzeug", 52);
+ attrMapPl.put("Wasserfahrzeug", 86);
+ attrMapDe.put("Barca", 52);
+ attrMapPl.put("Barca", 86);
+ attrMapDe.put("Barca", 52);
+ attrMapPl.put("Barca", 86);
+ attrMapDe.put("No GPS required", 35);
+ attrMapDe.put("ohne GPS findbar", 35);
+ attrMapDe.put("Sin GPS", 35);
+ attrMapDe.put("Senza GPS", 35);
+ attrMapDe.put("Dangerous area", 9);
+ attrMapPl.put("Dangerous area", 90);
+ attrMapDe.put("Skrzynka niebezpieczna", 9);
+ attrMapPl.put("Skrzynka niebezpieczna", 90);
+ attrMapDe.put("gefährliches Gebiet", 9);
+ attrMapPl.put("gefährliches Gebiet", 90);
+ attrMapDe.put("Zona Peligrosa", 9);
+ attrMapPl.put("Zona Peligrosa", 90);
+ attrMapDe.put("Area pericolosa", 9);
+ attrMapPl.put("Area pericolosa", 90);
+ attrMapDe.put("Active railway nearby", 10);
+ attrMapDe.put("aktive Eisenbahnlinie in der Nähe", 10);
+ attrMapDe.put("Cerca del ferrocarril activo", 10);
+ attrMapDe.put("Ferrovia attiva nei pressi", 10);
+ attrMapDe.put("Cliff / Rocks", 11);
+ attrMapDe.put("Klippen / Felsen", 11);
+ attrMapDe.put("Acantilado / Rocas", 11);
+ attrMapDe.put("Scogliera / Rocce", 11);
+ attrMapDe.put("Hunting", 12);
+ attrMapDe.put("Jagdgebiet", 12);
+ attrMapDe.put("Zona de Caza", 12);
+ attrMapDe.put("Caccia", 12);
+ attrMapDe.put("Thorns", 13);
+ attrMapDe.put("Dornen", 13);
+ attrMapDe.put("Espinas", 13);
+ attrMapDe.put("Spine", 13);
+ attrMapDe.put("Ticks", 14);
+ attrMapDe.put("Zecken", 14);
+ attrMapDe.put("Garrapatas", 14);
+ attrMapDe.put("Zecche", 14);
+ attrMapDe.put("Abandoned mines", 15);
+ attrMapDe.put("Folgen des Bergbaus", 15);
+ attrMapDe.put("Mina abandonada", 15);
+ attrMapDe.put("Miniere abbandonate", 15);
+ attrMapDe.put("Poisonous plants", 16);
+ attrMapDe.put("giftige Pflanzen", 16);
+ attrMapDe.put("Planta venenosa", 16);
+ attrMapDe.put("Piante velenose", 16);
+ attrMapDe.put("Dangerous animals", 17);
+ attrMapDe.put("giftige/gefährliche Tiere", 17);
+ attrMapDe.put("Animales Peligrosos", 17);
+ attrMapDe.put("Animali pericolosi", 17);
+ attrMapPl.put("Quick cache", 40);
+ attrMapPl.put("Szybka skrzynka", 40);
+ attrMapDe.put("Overnight stay necessary", 37);
+ attrMapDe.put("Übernachtung erforderlich", 37);
+ attrMapDe.put("Necesario pernoctar", 37);
+ attrMapDe.put("Necessario pernottamento", 37);
+ attrMapPl.put("Take your children", 41);
+ attrMapPl.put("Można zabrać dzieci", 41);
+ attrMapDe.put("Suited for children (10-12 yo)", 59);
+ attrMapDe.put("kindgerecht (10-12 Jahre)", 59);
+ attrMapDe.put("Apto para niños (10-12 años)", 59);
+ attrMapDe.put("Suited for children (10-12 anni)", 59);
+ // first trailer line
+
+ }
+
+ public static int getOcDeId(final String name) {
+
+ int result = 0;
+
+ if (attrMapDe.containsKey(name)) {
+ result = attrMapDe.get(name);
+ }
+ return result;
+ }
+}
diff --git a/main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java b/main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java
deleted file mode 100644
index 621032f..0000000
--- a/main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java
+++ /dev/null
@@ -1,754 +0,0 @@
-package cgeo.geocaching.connector.oc;
-
-import cgeo.geocaching.Geocache;
-import cgeo.geocaching.Image;
-import cgeo.geocaching.LogEntry;
-import cgeo.geocaching.R;
-import cgeo.geocaching.Settings;
-import cgeo.geocaching.cgeoapplication;
-import cgeo.geocaching.connector.ConnectorFactory;
-import cgeo.geocaching.connector.IConnector;
-import cgeo.geocaching.connector.gc.GCConnector;
-import cgeo.geocaching.enumerations.CacheAttribute;
-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.content.res.Resources;
-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.Matcher;
-import java.util.regex.Pattern;
-
-public class OC11XMLParser {
-
- private static final String[] MARKUP = new String[] { "p", "span" };
- private static Pattern STRIP_DATE = Pattern.compile("\\+0([0-9]){1}\\:00");
- private static Pattern LOCAL_URL = Pattern.compile("href=\"(.*)\"");
- private static final int CACHE_PARSE_LIMIT = 250;
- private static final Resources res = cgeoapplication.getInstance().getResources();
-
- private static ImageHolder imageHolder = null;
-
- private static class CacheHolder {
- public Geocache cache;
- public String latitude;
- public String longitude;
- }
-
- private static class CacheLog {
- public String id;
- public String cacheId;
- public LogEntry logEntry;
- }
-
- private static class CacheDescription {
- public String cacheId;
- public String shortDesc;
- public String desc;
- public String hint;
- }
-
- private static class ImageHolder {
- public String url;
- public String objectId;
- protected String title;
- protected boolean isSpoiler = false;
- }
-
- 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) {
- try {
- 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;
- }
- } catch (NumberFormatException e) {
- Log.e("OC11XMLParser.getCacheSize", e);
- }
- return CacheSize.NOT_CHOSEN;
- }
-
- private static CacheType getCacheType(final String typeId) {
- try {
- 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.VIRTUAL;
- case 10: // Driv./Drive-In
- return CacheType.TRADITIONAL;
- default:
- return CacheType.UNKNOWN;
- }
- } catch (NumberFormatException e) {
- Log.e("OC11XMLParser.getCacheType", e);
- }
- 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;
- default:
- return LogType.UNKNOWN;
- }
- }
-
- private static void setCacheStatus(final int statusId, final Geocache 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 Geocache(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;
- }
-
- private static int attributeId;
-
- public static Collection<Geocache> parseCaches(final InputStream stream) throws IOException {
- // parse and return caches without filtering
- return parseCaches(stream, true);
- }
-
- public static Collection<Geocache> parseCachesFiltered(final InputStream stream) throws IOException {
- // parse caches and filter result
- return parseCaches(stream, false);
- }
-
- private static Collection<Geocache> parseCaches(final InputStream stream, boolean ignoreFiltersIn) throws IOException {
-
- final Map<String, Geocache> caches = new HashMap<String, Geocache>();
- final Map<String, LogEntry> logs = new HashMap<String, LogEntry>();
-
- 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");
-
- final boolean ignoreFilters = ignoreFiltersIn;
-
- // cache
- cacheNode.setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attributes) {
- resetCache(cacheHolder);
- }
-
- });
-
- cacheNode.setEndElementListener(new EndElementListener() {
-
- @Override
- public void end() {
- Geocache cache = cacheHolder.cache;
- Geopoint coords = new Geopoint(cacheHolder.latitude, cacheHolder.longitude);
- cache.setCoords(coords);
- if (caches.size() < CACHE_PARSE_LIMIT && isValid(cache) && (ignoreFilters || !isExcluded(cache))) {
- cache.setDetailedUpdatedNow();
- caches.put(cache.getCacheId(), cache);
- }
- }
-
- private boolean isExcluded(Geocache cache) {
- if (cache.isArchived()) {
- return true;
- }
- if (cache.isDisabled() && Settings.isExcludeDisabledCaches()) {
- return true;
- }
- if ((cache.isFound() || cache.isOwner()) && Settings.isExcludeMyCaches()) {
- return true;
- }
- return !Settings.getCacheType().contains(cache);
- }
-
- private boolean isValid(Geocache cache) {
- return StringUtils.isNotBlank(cache.getGeocode()) && !cache.getCoords().equals(Geopoint.ZERO);
- }
- });
-
- // 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(res.getString(R.string.cache_listed_on, GCConnector.getInstance().getName()) + ": <a href=\"http://coord.info/" + gccode + "\">" + gccode + "</a><br /><br />");
- }
- }
- }
- });
-
- // 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();
- try {
- cacheHolder.cache.setDifficulty(Float.valueOf(content));
- } catch (NumberFormatException e) {
- Log.e("OC11XMLParser: unknown difficulty " + content, e);
- }
- }
- });
-
- // cache.terrain
- cacheNode.getChild("terrain").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- final String content = body.trim();
- try {
- cacheHolder.cache.setTerrain(Float.valueOf(content));
- } catch (NumberFormatException e) {
- Log.e("OC11XMLParser: unknown terrain " + content, e);
- }
- }
- });
-
- // cache.datehidden
- cacheNode.getChild("datehidden").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- final String content = body.trim();
- cacheHolder.cache.setHidden(parseFullDate(content));
- }
- });
-
- // cache.userid
- final Element useridNode = cacheNode.getChild("userid");
-
- useridNode.setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attributes) {
- if (attributes.getIndex("id") > -1) {
- cacheHolder.cache.setOwnerUserId(attributes.getValue("id"));
- }
- }
- });
-
- useridNode.setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- cacheHolder.cache.setOwnerDisplayName(body);
- }
- });
-
- // cache.attributes.attribute
- final Element attributeNode = cacheNode.getChild("attributes").getChild("attribute");
-
- attributeNode.setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attributes) {
- if (attributes.getIndex("id") > -1) {
- try {
- attributeId = Integer.parseInt(attributes.getValue("id"));
- } catch (NumberFormatException e) {
- Log.w(String.format("Failed to parse attribute id of cache '%s'.", cacheHolder.cache.getGeocode()));
- }
- }
- }
- });
-
- attributeNode.setEndTextElementListener(new EndTextElementListener() {
- @Override
- public void end(String body) {
- CacheAttribute attribute = CacheAttribute.getByOcId(attributeId);
- if (attribute != null) {
- // semantic of attributes on opencaching is always "yes"
- cacheHolder.cache.getAttributes().add(attribute.getAttributeName(true));
- }
- else {
- 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 Geocache cache = caches.get(descHolder.cacheId);
- if (cache != null) {
- cache.setShortDescription(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 = linkify(stripMarkup(content));
- }
- });
-
- // cachedesc.desc
- cacheDesc.getChild("desc").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- final String content = body.trim();
- descHolder.desc = linkify(stripMarkup(content));
- }
- });
-
- // cachedesc.hint
- cacheDesc.getChild("hint").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- descHolder.hint = body.trim();
- }
- });
-
- // 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 Geocache cache = caches.get(logHolder.cacheId);
- if (cache != null && logHolder.logEntry.type != LogType.UNKNOWN) {
- logs.put(logHolder.id, logHolder.logEntry);
- cache.getLogs().add(0, logHolder.logEntry);
- if ((logHolder.logEntry.type == LogType.FOUND_IT || logHolder.logEntry.type == LogType.ATTENDED)
- && StringUtils.equalsIgnoreCase(logHolder.logEntry.author, Settings.getOCConnectorUserName())) {
- cache.setFound(true);
- cache.setVisitedDate(logHolder.logEntry.date);
- }
- }
- }
- });
-
- // cachelog.id
- cacheLog.getChild("id").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- logHolder.id = StringUtils.trim(body);
- }
- });
-
- // 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 (NullPointerException e) {
- Log.w("Failed to parse log date", e);
- }
- }
- });
-
- // cachelog.logtype
- cacheLog.getChild("logtype").setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attrs) {
- if (attrs.getIndex("id") > -1) {
- final String id = attrs.getValue("id");
- try {
- final int typeId = Integer.parseInt(id);
- logHolder.logEntry.type = getLogType(typeId);
- } catch (NumberFormatException e) {
- Log.e("OC11XMLParser, unknown logtype " + id, e);
- }
- }
- }
- });
-
- // 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 = stripMarkup(logText);
- }
- });
-
- // pictures
- final Element picture = root.getChild("picture");
-
- picture.setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attrs) {
- imageHolder = new ImageHolder();
- }
- });
-
- picture.setEndElementListener(new EndElementListener() {
-
- @Override
- public void end() {
- if (imageHolder.isSpoiler) {
- final Geocache cache = caches.get(imageHolder.objectId);
- if (cache != null) {
- Image spoiler = new Image(imageHolder.url, imageHolder.title);
- cache.addSpoiler(spoiler);
- }
- }
- else {
- final LogEntry log = logs.get(imageHolder.objectId);
- if (log != null) {
- log.addLogImage(new Image(imageHolder.url, imageHolder.title));
- }
- }
- }
- });
-
- // picture.object
- picture.getChild("object").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- imageHolder.objectId = StringUtils.trim(body);
- }
- });
-
- // picture.title
- picture.getChild("title").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- imageHolder.title = StringUtils.trim(body);
- }
- });
-
- // picture.url
- picture.getChild("url").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- imageHolder.url = StringUtils.trim(body);
- }
- });
-
- // picture.attributes
- picture.getChild("attributes").setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attributes) {
- if (attributes.getIndex("spoiler") > -1) {
- String spoiler = attributes.getValue("spoiler");
- imageHolder.isSpoiler = ("1".equals(spoiler));
- }
- }
- });
-
- 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);
- return null;
- }
- }
-
- /**
- * Converts local links to absolute links targeting the OC website.
- */
- private static String linkify(String input) {
- String result = input;
- Matcher matcher = LOCAL_URL.matcher(result);
- while (matcher.find()) {
- String url = matcher.group(1);
- if (!url.contains(":/")) {
- IConnector ocConnector = ConnectorFactory.getConnector("OCXXX");
- String prefix = "http://" + ocConnector.getHost() + "/";
- result = StringUtils.replace(result, url, prefix + url);
- matcher = LOCAL_URL.matcher(result);
- }
- }
- return result;
- }
-
- /**
- * Removes unneeded markup. Log texts are typically encapsulated in paragraph tags which lead to more empty space on
- * rendering.
- */
- protected static String stripMarkup(String input) {
- if (!StringUtils.startsWith(input, "<")) {
- return input;
- }
- String result = input.trim();
- for (String tagName : MARKUP) {
- final String startTag = "<" + tagName + ">";
- if (StringUtils.startsWith(result, startTag)) {
- final String endTag = "</" + tagName + ">";
- if (StringUtils.endsWith(result, endTag)) {
- String inner = result.substring(startTag.length(), result.length() - endTag.length()).trim();
- String nested = stripMarkup(inner);
- if (!nested.contains(startTag)) {
- result = nested;
- }
- }
- }
- }
- return result;
- }
-} \ No newline at end of file
diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java
index 69cf8a4..4f365ec 100644
--- a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java
+++ b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java
@@ -7,13 +7,31 @@ import cgeo.geocaching.network.Parameters;
import cgeo.geocaching.utils.CancellableHandler;
import cgeo.geocaching.utils.CryptUtils;
+import org.apache.commons.lang3.StringUtils;
+
public class OCApiConnector extends OCConnector implements ISearchByGeocode {
+ // Levels of Okapi we support
+ // oldapi is around rev 500
+ // current is from rev 798 onwards
+ public enum ApiSupport {
+ oldapi,
+ current
+ }
+
+ // Levels of OAuth-Authentication we support
+ public enum OAuthLevel {
+ Level1,
+ Level3
+ }
+
private final String cK;
+ private final ApiSupport apiSupport;
- public OCApiConnector(String name, String host, String prefix, String cK) {
+ public OCApiConnector(String name, String host, String prefix, String cK, ApiSupport apiSupport) {
super(name, host, prefix);
this.cK = cK;
+ this.apiSupport = apiSupport;
}
public void addAuthentication(final Parameters params) {
@@ -23,7 +41,7 @@ public class OCApiConnector extends OCConnector implements ISearchByGeocode {
@Override
public String getLicenseText(final Geocache cache) {
// NOT TO BE TRANSLATED
- return "<a href=\"" + getCacheUrl(cache) + "\">" + getName() + "</a> data licensed under the Creative Commons BY-SA 3.0 License";
+ return "© " + cache.getOwnerDisplayName() + ", <a href=\"" + getCacheUrl(cache) + "\">" + getName() + "</a>, CC-BY-NC-ND, alle Logeinträge © jeweiliger Autor";
}
@Override
@@ -40,4 +58,23 @@ public class OCApiConnector extends OCConnector implements ISearchByGeocode {
// currently always active, but only for details download
return true;
}
+
+ @SuppressWarnings("static-method")
+ public OAuthLevel getSupportedAuthLevel() {
+ return OAuthLevel.Level1;
+ }
+
+ public String getCK() {
+ return CryptUtils.rot13(cK);
+ }
+
+ @SuppressWarnings("static-method")
+ public String getCS() {
+ return StringUtils.EMPTY;
+ }
+
+ public ApiSupport getApiSupport() {
+ return apiSupport;
+ }
+
}
diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java
new file mode 100644
index 0000000..5ca2d28
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java
@@ -0,0 +1,139 @@
+package cgeo.geocaching.connector.oc;
+
+import cgeo.geocaching.Geocache;
+import cgeo.geocaching.SearchResult;
+import cgeo.geocaching.settings.Settings;
+import cgeo.geocaching.cgData;
+import cgeo.geocaching.cgeoapplication;
+import cgeo.geocaching.connector.ILoggingManager;
+import cgeo.geocaching.connector.capability.ILogin;
+import cgeo.geocaching.connector.capability.ISearchByCenter;
+import cgeo.geocaching.connector.capability.ISearchByViewPort;
+import cgeo.geocaching.connector.oc.UserInfo.UserInfoStatus;
+import cgeo.geocaching.geopoint.Geopoint;
+import cgeo.geocaching.geopoint.Viewport;
+import cgeo.geocaching.utils.CryptUtils;
+
+import org.apache.commons.lang3.StringUtils;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Handler;
+
+public class OCApiLiveConnector extends OCApiConnector implements ISearchByCenter, ISearchByViewPort, ILogin {
+
+ private String cS;
+ private UserInfo userInfo = new UserInfo(StringUtils.EMPTY, 0, UserInfoStatus.NOT_RETRIEVED);
+
+ public OCApiLiveConnector(String name, String host, String prefix, int cKResId, int cSResId, ApiSupport apiSupport) {
+ super(name, host, prefix, CryptUtils.rot13(cgeoapplication.getInstance().getResources().getString(cKResId)), apiSupport);
+
+ cS = CryptUtils.rot13(cgeoapplication.getInstance().getResources().getString(cSResId));
+ }
+
+ @Override
+ public boolean isActivated() {
+ return Settings.isOCConnectorActive();
+ }
+
+ @Override
+ public SearchResult searchByViewport(Viewport viewport, String[] tokens) {
+ return new SearchResult(OkapiClient.getCachesBBox(viewport, this));
+ }
+
+ @Override
+ public SearchResult searchByCenter(Geopoint center) {
+
+ return new SearchResult(OkapiClient.getCachesAround(center, this));
+ }
+
+ @Override
+ public OAuthLevel getSupportedAuthLevel() {
+ // TODO the tokens must be available connector specific
+ if (StringUtils.isNotBlank(Settings.getOCDETokenPublic()) && StringUtils.isNotBlank(Settings.getOCDETokenSecret())) {
+ return OAuthLevel.Level3;
+ }
+ return OAuthLevel.Level1;
+ }
+
+ @Override
+ public String getCS() {
+ return CryptUtils.rot13(cS);
+ }
+
+ @Override
+ public boolean supportsWatchList() {
+ return true;
+ }
+
+ @Override
+ public boolean addToWatchlist(Geocache cache) {
+ final boolean added = OkapiClient.setWatchState(cache, true, this);
+
+ if (added) {
+ cgData.saveChangedCache(cache);
+ }
+
+ return added;
+ }
+
+ @Override
+ public boolean removeFromWatchlist(Geocache cache) {
+ final boolean removed = OkapiClient.setWatchState(cache, false, this);
+
+ if (removed) {
+ cgData.saveChangedCache(cache);
+ }
+
+ return removed;
+ }
+
+ @Override
+ public boolean supportsLogging() {
+ return true;
+ }
+
+ @Override
+ public ILoggingManager getLoggingManager(Activity activity, Geocache cache) {
+ return new OkapiLoggingManager(activity, this, cache);
+ }
+
+ @Override
+ public boolean canLog(Geocache cache) {
+ return true;
+ }
+
+ public boolean supportsPersonalization() {
+ return getSupportedAuthLevel() == OAuthLevel.Level3;
+ }
+
+ @Override
+ public boolean login(Handler handler, Context fromActivity) {
+ if (supportsPersonalization()) {
+ userInfo = OkapiClient.getUserInfo(this);
+ } else {
+ userInfo = new UserInfo(StringUtils.EMPTY, 0, UserInfoStatus.NOT_SUPPORTED);
+ }
+ return userInfo.getStatus() == UserInfoStatus.SUCCESSFUL;
+ }
+
+ @Override
+ public String getUserName() {
+ return userInfo.getName();
+ }
+
+ @Override
+ public int getCachesFound() {
+ return userInfo.getFinds();
+ }
+
+ @Override
+ public String getLoginStatusString() {
+ return cgeoapplication.getInstance().getString(userInfo.getStatus().resId);
+ }
+
+ @Override
+ public boolean isLoggedIn() {
+ return userInfo.getStatus() == UserInfoStatus.SUCCESSFUL;
+ }
+}
diff --git a/main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java b/main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java
new file mode 100644
index 0000000..08d796e
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java
@@ -0,0 +1,107 @@
+package cgeo.geocaching.connector.oc;
+
+import cgeo.geocaching.R;
+import cgeo.geocaching.settings.Settings;
+import cgeo.geocaching.cgeoapplication;
+import cgeo.geocaching.network.OAuthAuthorizationActivity;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+
+public class OCAuthorizationActivity extends OAuthAuthorizationActivity {
+
+ public OCAuthorizationActivity() {
+ super("www.opencaching.de",
+ "/okapi/services/oauth/request_token",
+ "/okapi/services/oauth/authorize",
+ "/okapi/services/oauth/access_token",
+ false,
+ cgeoapplication.getInstance().getResources().getString(R.string.oc_de_okapi_consumer_key),
+ cgeoapplication.getInstance().getResources().getString(R.string.oc_de_okapi_consumer_secret));
+ }
+
+ @Override
+ protected ImmutablePair<String, String> getTempToken() {
+ return Settings.getTempOCDEToken();
+ }
+
+ @Override
+ protected void setTempTokens(String tokenPublic, String tokenSecret) {
+ Settings.setOCDETempTokens(tokenPublic, tokenSecret);
+ }
+
+ @Override
+ protected void setTokens(String tokenPublic, String tokenSecret, boolean enable) {
+ Settings.setOCDETokens(tokenPublic, tokenSecret, enable);
+ }
+
+ @Override
+ protected String getAuthTitle() {
+ return res.getString(R.string.auth_ocde);
+ }
+
+ @Override
+ protected String getAuthAgain() {
+ return res.getString(R.string.auth_again_oc);
+ }
+
+ @Override
+ protected String getErrAuthInitialize() {
+ return res.getString(R.string.err_auth_initialize);
+ }
+
+ @Override
+ protected String getAuthStart() {
+ return res.getString(R.string.auth_start_oc);
+ }
+
+ @Override
+ protected String getAuthDialogCompleted() {
+ return res.getString(R.string.auth_dialog_completed_oc, getAuthTitle());
+ }
+
+ @Override
+ protected String getErrAuthProcess() {
+ return res.getString(R.string.err_auth_process);
+ }
+
+ @Override
+ protected String getAuthDialogWait() {
+ return res.getString(R.string.auth_dialog_wait_oc, getAuthTitle());
+ }
+
+ @Override
+ protected String getAuthDialogPinTitle() {
+ return res.getString(R.string.auth_dialog_pin_title_oc);
+ }
+
+ @Override
+ protected String getAuthDialogPinMessage() {
+ return res.getString(R.string.auth_dialog_pin_message_oc, getAuthTitle());
+ }
+
+ @Override
+ protected String getAboutAuth1() {
+ return res.getString(R.string.about_auth_1_oc, getAuthTitle());
+ }
+
+ @Override
+ protected String getAboutAuth2() {
+ return res.getString(R.string.about_auth_2_oc, getAuthTitle(), getAuthTitle());
+ }
+
+ @Override
+ protected String getAuthAuthorize() {
+ return res.getString(R.string.auth_authorize_oc);
+ }
+
+ @Override
+ protected String getAuthPinHint() {
+ return res.getString(R.string.auth_pin_hint_oc, getAuthTitle());
+ }
+
+ @Override
+ protected String getAuthFinish() {
+ return res.getString(R.string.auth_finish_oc);
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/connector/oc/OCConnector.java b/main/src/cgeo/geocaching/connector/oc/OCConnector.java
index 62dfb4c..29cdd10 100644
--- a/main/src/cgeo/geocaching/connector/oc/OCConnector.java
+++ b/main/src/cgeo/geocaching/connector/oc/OCConnector.java
@@ -2,8 +2,8 @@ package cgeo.geocaching.connector.oc;
import cgeo.geocaching.Geocache;
import cgeo.geocaching.ICache;
+import cgeo.geocaching.R;
import cgeo.geocaching.connector.AbstractConnector;
-import cgeo.geocaching.enumerations.CacheRealm;
import java.util.regex.Pattern;
@@ -12,7 +12,7 @@ public class OCConnector extends AbstractConnector {
private final String host;
private final String name;
private final Pattern codePattern;
- private static final Pattern gpxZipFilePattern = Pattern.compile("oc[a-z]{2,3}\\d{5,}\\.zip", Pattern.CASE_INSENSITIVE);
+ private static final Pattern GPX_ZIP_FILE_PATTERN = Pattern.compile("oc[a-z]{2,3}\\d{5,}\\.zip", Pattern.CASE_INSENSITIVE);
public OCConnector(final String name, final String host, final String prefix) {
this.name = name;
@@ -45,7 +45,7 @@ public class OCConnector extends AbstractConnector {
@Override
public boolean isZippedGPXFile(String fileName) {
- return gpxZipFilePattern.matcher(fileName).matches();
+ return GPX_ZIP_FILE_PATTERN.matcher(fileName).matches();
}
@Override
@@ -59,8 +59,11 @@ public class OCConnector extends AbstractConnector {
}
@Override
- public CacheRealm getCacheRealm() {
- return CacheRealm.OC;
+ public int getCacheMapMarkerId(boolean disabled) {
+ if (disabled) {
+ return R.drawable.marker_disabled_oc;
+ }
+ return R.drawable.marker_oc;
}
}
diff --git a/main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java b/main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java
deleted file mode 100644
index 43fdcfc..0000000
--- a/main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package cgeo.geocaching.connector.oc;
-
-import cgeo.geocaching.Geocache;
-import cgeo.geocaching.ICache;
-import cgeo.geocaching.SearchResult;
-import cgeo.geocaching.Settings;
-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.ui.Formatter;
-import cgeo.geocaching.utils.CancellableHandler;
-
-import org.apache.commons.lang3.StringUtils;
-
-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 Geocache 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();
- }
-
- @Override
- public boolean isOwner(ICache cache) {
- return StringUtils.equalsIgnoreCase(cache.getOwnerDisplayName(), Settings.getOCConnectorUserName());
- }
-
- @Override
- public String getLicenseText(Geocache cache) {
- // not to be translated
- return "© " + cache.getOwnerDisplayName() + ", " + "<a href=\"" + getCacheUrl(cache) + "\">www.opencaching.de</a>, CC-BY-NC-ND, Stand: " + Formatter.formatFullDate(cache.getUpdated());
- }
-
-}
diff --git a/main/src/cgeo/geocaching/connector/oc/OCXMLClient.java b/main/src/cgeo/geocaching/connector/oc/OCXMLClient.java
deleted file mode 100644
index 6767b48..0000000
--- a/main/src/cgeo/geocaching/connector/oc/OCXMLClient.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package cgeo.geocaching.connector.oc;
-
-import cgeo.geocaching.Geocache;
-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.lang3.StringUtils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Collection;
-import java.util.Collections;
-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 Geocache getCache(final String geoCode) {
- try {
- final Parameters params = getOCXmlQueryParameters(true, true, true);
- params.put("wp", geoCode);
- final InputStream data = request(ConnectorFactory.getConnector(geoCode), SERVICE_CACHE, params);
-
- if (data == null) {
- return null;
- }
-
- Collection<Geocache> caches = OC11XMLParser.parseCaches(new GZIPInputStream(data));
- if (caches.iterator().hasNext()) {
- Geocache cache = caches.iterator().next();
- cgData.saveCache(cache, LoadFlags.SAVE_ALL);
- return cache;
- }
- return null;
- } catch (IOException e) {
- Log.e("Error parsing cache '" + geoCode + "'", e);
- return null;
- }
- }
-
- public static Collection<Geocache> getCachesAround(final Geopoint center, final double distance) {
- try {
- final Parameters params = getOCXmlQueryParameters(false, 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 Collections.emptyList();
- }
-
- return OC11XMLParser.parseCachesFiltered(new GZIPInputStream(data));
- } catch (IOException e) {
- Log.e("Error parsing nearby search result", e);
- return Collections.emptyList();
- }
- }
-
- 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 boolean withImages) {
- return new Parameters("modifiedsince", "20000101000000",
- "user", "0",
- "cache", "1",
- "cachedesc", withDescription ? "1" : "0",
- "cachelog", withLogs ? "1" : "0",
- "picture", withImages ? "1" : "0",
- "removedobject", "0",
- "session", "0",
- "doctype", "0",
- "charset", "utf-8",
- "zip", "gzip",
- "picturefromcachelog", withImages ? "1" : "0");
- }
-}
diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
index 0673605..6959adf 100644
--- a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
+++ b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
@@ -3,17 +3,31 @@ package cgeo.geocaching.connector.oc;
import cgeo.geocaching.Geocache;
import cgeo.geocaching.Image;
import cgeo.geocaching.LogEntry;
+import cgeo.geocaching.R;
+import cgeo.geocaching.Waypoint;
import cgeo.geocaching.cgData;
+import cgeo.geocaching.cgeoapplication;
import cgeo.geocaching.connector.ConnectorFactory;
import cgeo.geocaching.connector.IConnector;
+import cgeo.geocaching.connector.LogResult;
+import cgeo.geocaching.connector.gc.GCConnector;
+import cgeo.geocaching.connector.oc.OCApiConnector.ApiSupport;
+import cgeo.geocaching.connector.oc.OCApiConnector.OAuthLevel;
+import cgeo.geocaching.connector.oc.UserInfo.UserInfoStatus;
+import cgeo.geocaching.enumerations.CacheAttribute;
import cgeo.geocaching.enumerations.CacheSize;
import cgeo.geocaching.enumerations.CacheType;
import cgeo.geocaching.enumerations.LoadFlags.SaveFlag;
import cgeo.geocaching.enumerations.LogType;
+import cgeo.geocaching.enumerations.StatusCode;
+import cgeo.geocaching.enumerations.WaypointType;
import cgeo.geocaching.geopoint.Geopoint;
import cgeo.geocaching.geopoint.GeopointFormatter;
+import cgeo.geocaching.geopoint.Viewport;
import cgeo.geocaching.network.Network;
+import cgeo.geocaching.network.OAuth;
import cgeo.geocaching.network.Parameters;
+import cgeo.geocaching.settings.Settings;
import cgeo.geocaching.utils.Log;
import org.apache.commons.lang3.StringUtils;
@@ -22,17 +36,42 @@ import org.json.JSONException;
import org.json.JSONObject;
import android.net.Uri;
-import android.text.Html;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
-final public class OkapiClient {
+final class OkapiClient {
+
+ private static final char SEPARATOR = '|';
+ private static final String SEPARATOR_STRING = Character.toString(SEPARATOR);
+ private static final SimpleDateFormat LOG_DATE_FORMAT;
+ static {
+ LOG_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ", Locale.US);
+ LOG_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+ private static final SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault());
+
+ private static final String CACHE_ATTRNAMES = "attrnames";
+ private static final String WPT_LOCATION = "location";
+ private static final String WPT_DESCRIPTION = "description";
+ private static final String WPT_TYPE = "type";
+ private static final String WPT_NAME = "name";
+ private static final String CACHE_IS_WATCHED = "is_watched";
+ private static final String CACHE_WPTS = "alt_wpts";
+ private static final String CACHE_STATUS_ARCHIVED = "Archived";
+ private static final String CACHE_STATUS_DISABLED = "Temporarily unavailable";
+ private static final String CACHE_IS_FOUND = "is_found";
private static final String CACHE_SIZE = "size";
private static final String CACHE_VOTES = "rating_votes";
private static final String CACHE_NOTFOUNDS = "notfounds";
@@ -55,6 +94,8 @@ final public class OkapiClient {
private static final String CACHE_LOCATION = "location";
private static final String CACHE_NAME = "name";
private static final String CACHE_CODE = "code";
+ private static final String CACHE_REQ_PASSWORD = "req_passwd";
+ private static final String CACHE_MY_NOTES = "my_notes";
private static final String LOG_TYPE = "type";
private static final String LOG_COMMENT = "comment";
@@ -62,15 +103,35 @@ final public class OkapiClient {
private static final String LOG_USER = "user";
private static final String USER_USERNAME = "username";
+ private static final String USER_CACHES_FOUND = "caches_found";
+ private static final String USER_INFO_FIELDS = "username|caches_found";
+
+ // the several realms of possible fields for cache retrieval:
+ // Core: for livemap requests (L3 - only with level 3 auth)
+ // Additional: additional fields for full cache (L3 - only for level 3 auth, current - only for connectors with current api)
+ private static final String SERVICE_CACHE_CORE_FIELDS = "code|name|location|type|status|difficulty|terrain|size";
+ private static final String SERVICE_CACHE_CORE_L3_FIELDS = "is_found";
+ private static final String SERVICE_CACHE_ADDITIONAL_FIELDS = "owner|founds|notfounds|rating|rating_votes|recommendations|description|hint|images|latest_logs|date_hidden|alt_wpts|attrnames|req_passwd";
+ private static final String SERVICE_CACHE_ADDITIONAL_CURRENT_FIELDS = "gc_code|attribution_note";
+ private static final String SERVICE_CACHE_ADDITIONAL_L3_FIELDS = "is_watched|my_notes";
+
+ private static final String METHOD_SEARCH_NEAREST = "services/caches/search/nearest";
+ private static final String METHOD_SEARCH_BBOX = "services/caches/search/bbox";
+ private static final String METHOD_RETRIEVE_CACHES = "services/caches/geocaches";
+
+ public static Geocache getCache(final String geoCode) {
+ final Parameters params = new Parameters("cache_code", geoCode);
+ final IConnector connector = ConnectorFactory.getConnector(geoCode);
+ if (!(connector instanceof OCApiConnector)) {
+ return null;
+ }
- private static final String SERVICE_CACHE = "/okapi/services/caches/geocache";
- private static final String SERVICE_CACHE_FIELDS = "code|name|location|type|status|owner|founds|notfounds|size|difficulty|terrain|rating|rating_votes|recommendations|description|hint|images|latest_logs|date_hidden";
+ final OCApiConnector ocapiConn = (OCApiConnector) connector;
- private static final String SERVICE_NEAREST = "/okapi/services/caches/search/nearest";
+ params.add("fields", getFullFields(ocapiConn));
+ params.add("attribution_append", "none");
- public static Geocache getCache(final String geoCode) {
- final Parameters params = new Parameters("cache_code", geoCode, "fields", SERVICE_CACHE_FIELDS);
- final JSONObject data = request(ConnectorFactory.getConnector(geoCode), SERVICE_CACHE, params);
+ final JSONObject data = request(ocapiConn, OkapiService.SERVICE_CACHE, params);
if (data == null) {
return null;
@@ -79,57 +140,144 @@ final public class OkapiClient {
return parseCache(data);
}
- public static List<Geocache> getCachesAround(final Geopoint center, IConnector connector) {
- String centerString = GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, center) + "|" + GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, center);
- final Parameters params = new Parameters("center", centerString);
- final JSONObject data = request(connector, SERVICE_NEAREST, params);
+ public static List<Geocache> getCachesAround(final Geopoint center, final OCApiConnector connector) {
+ final String centerString = GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, center) + SEPARATOR + GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, center);
+ final Parameters params = new Parameters("search_method", METHOD_SEARCH_NEAREST);
+ final Map<String, String> valueMap = new LinkedHashMap<String, String>();
+ valueMap.put("center", centerString);
+ valueMap.put("limit", "20");
+
+ return requestCaches(connector, params, valueMap);
+ }
+
+ private static List<Geocache> requestCaches(final OCApiConnector connector, final Parameters params, final Map<String, String> valueMap) {
+ addFilterParams(valueMap, connector);
+ params.add("search_params", new JSONObject(valueMap).toString());
+ addRetrieveParams(params, connector);
+
+ final JSONObject data = request(connector, OkapiService.SERVICE_SEARCH_AND_RETRIEVE, params);
if (data == null) {
- return null;
+ return Collections.emptyList();
}
return parseCaches(data);
}
+ // Assumes level 3 OAuth
+ public static List<Geocache> getCachesBBox(final Viewport viewport, final OCApiConnector connector) {
+
+ if (viewport.getLatitudeSpan() == 0 || viewport.getLongitudeSpan() == 0) {
+ return Collections.emptyList();
+ }
+
+ final String bboxString = GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, viewport.bottomLeft)
+ + SEPARATOR + GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, viewport.bottomLeft)
+ + SEPARATOR + GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, viewport.topRight)
+ + SEPARATOR + GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, viewport.topRight);
+ final Parameters params = new Parameters("search_method", METHOD_SEARCH_BBOX);
+ final Map<String, String> valueMap = new LinkedHashMap<String, String>();
+ valueMap.put("bbox", bboxString);
+
+ return requestCaches(connector, params, valueMap);
+ }
+
+ public static boolean setWatchState(final Geocache cache, final boolean watched, final OCApiConnector connector) {
+ final Parameters params = new Parameters("cache_code", cache.getGeocode());
+ params.add("watched", watched ? "true" : "false");
+
+ final JSONObject data = request(connector, OkapiService.SERVICE_MARK_CACHE, params);
+
+ if (data == null) {
+ return false;
+ }
+
+ cache.setOnWatchlist(watched);
+
+ return true;
+ }
+
+ public static LogResult postLog(final Geocache cache, final LogType logType, final Calendar date, final String log, final String logPassword, final OCApiConnector connector) {
+ final Parameters params = new Parameters("cache_code", cache.getGeocode());
+ params.add("logtype", logType.oc_type);
+ params.add("comment", log);
+ params.add("comment_format", "plaintext");
+ params.add("when", LOG_DATE_FORMAT.format(date.getTime()));
+ if (logType.equals(LogType.NEEDS_MAINTENANCE)) {
+ params.add("needs_maintenance", "true");
+ }
+ if (logPassword != null) {
+ params.add("password", logPassword);
+ }
+
+ final JSONObject data = request(connector, OkapiService.SERVICE_SUBMIT_LOG, params);
+
+ if (data == null) {
+ return new LogResult(StatusCode.LOG_POST_ERROR, "");
+ }
+
+ try {
+ if (data.getBoolean("success")) {
+ return new LogResult(StatusCode.NO_ERROR, data.getString("log_uuid"));
+ }
+
+ return new LogResult(StatusCode.LOG_POST_ERROR, "");
+ } catch (final JSONException e) {
+ Log.e("OkapiClient.postLog", e);
+ }
+ return new LogResult(StatusCode.LOG_POST_ERROR, "");
+ }
+
private static List<Geocache> parseCaches(final JSONObject response) {
try {
- final JSONArray cachesResponse = response.getJSONArray("results");
+ // Check for empty result
+ final String result = response.getString("results");
+ if (StringUtils.isBlank(result) || StringUtils.equals(result, "[]")) {
+ return Collections.emptyList();
+ }
+
+ // Get and iterate result list
+ final JSONObject cachesResponse = response.getJSONObject("results");
if (cachesResponse != null) {
- ArrayList<String> geocodes = new ArrayList<String>(cachesResponse.length());
- for (int i = 0; i < cachesResponse.length(); i++) {
- String geocode = cachesResponse.getString(i);
- if (StringUtils.isNotBlank(geocode)) {
- geocodes.add(geocode);
- }
- }
- List<Geocache> caches = new ArrayList<Geocache>(geocodes.size());
- for (String geocode : geocodes) {
- Geocache cache = getCache(geocode);
+ final List<Geocache> caches = new ArrayList<Geocache>(cachesResponse.length());
+ @SuppressWarnings("unchecked")
+ final
+ Iterator<String> keys = cachesResponse.keys();
+ while (keys.hasNext()) {
+ final String key = keys.next();
+ final Geocache cache = parseSmallCache(cachesResponse.getJSONObject(key));
if (cache != null) {
caches.add(cache);
}
}
return caches;
}
- } catch (JSONException e) {
- Log.e("OkapiClient.parseCaches", e);
+ } catch (final JSONException e) {
+ Log.e("OkapiClient.parseCachesResult", e);
}
- return null;
+ return Collections.emptyList();
+ }
+
+ private static Geocache parseSmallCache(final JSONObject response) {
+ final Geocache cache = new Geocache();
+ cache.setReliableLatLon(true);
+ try {
+
+ parseCoreCache(response, cache);
+
+ cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_CACHE));
+ } catch (final JSONException e) {
+ Log.e("OkapiClient.parseSmallCache", e);
+ }
+ return cache;
}
private static Geocache parseCache(final JSONObject response) {
final Geocache cache = new Geocache();
cache.setReliableLatLon(true);
try {
- cache.setGeocode(response.getString(CACHE_CODE));
- cache.setName(response.getString(CACHE_NAME));
- // not used: names
- setLocation(cache, response.getString(CACHE_LOCATION));
- cache.setType(getCacheType(response.getString(CACHE_TYPE)));
- final String status = response.getString(CACHE_STATUS);
- cache.setDisabled(status.equalsIgnoreCase("Temporarily unavailable"));
- cache.setArchived(status.equalsIgnoreCase("Archived"));
+ parseCoreCache(response, cache);
// not used: url
final JSONObject owner = response.getJSONObject(CACHE_OWNER);
@@ -137,9 +285,7 @@ final public class OkapiClient {
cache.getLogCounts().put(LogType.FOUND_IT, response.getInt(CACHE_FOUNDS));
cache.getLogCounts().put(LogType.DIDNT_FIND_IT, response.getInt(CACHE_NOTFOUNDS));
- cache.setSize(getCacheSize(response));
- cache.setDifficulty((float) response.getDouble(CACHE_DIFFICULTY));
- cache.setTerrain((float) response.getDouble(CACHE_TERRAIN));
+
if (!response.isNull(CACHE_RATING)) {
cache.setRating((float) response.getDouble(CACHE_RATING));
}
@@ -147,14 +293,29 @@ final public class OkapiClient {
cache.setFavoritePoints(response.getInt(CACHE_RECOMMENDATIONS));
// not used: req_password
- cache.setDescription(response.getString(CACHE_DESCRIPTION));
- cache.setHint(Html.fromHtml(response.getString(CACHE_HINT)).toString());
+ // Prepend gc-link to description if available
+ final StringBuilder description = new StringBuilder(500);
+ if (!response.isNull("gc_code")) {
+ final String gccode = response.getString("gc_code");
+ description.append(cgeoapplication.getInstance().getResources()
+ .getString(R.string.cache_listed_on, GCConnector.getInstance().getName()))
+ .append(": <a href=\"http://coord.info/")
+ .append(gccode)
+ .append("\">")
+ .append(gccode)
+ .append("</a><br /><br />");
+ }
+ description.append(response.getString(CACHE_DESCRIPTION));
+ cache.setDescription(description.toString());
+
+ // currently the hint is delivered as HTML (contrary to OKAPI documentation), so we can store it directly
+ cache.setHint(response.getString(CACHE_HINT));
// not used: hints
final JSONArray images = response.getJSONArray(CACHE_IMAGES);
if (images != null) {
for (int i = 0; i < images.length(); i++) {
- JSONObject imageResponse = images.getJSONObject(i);
+ final 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());
@@ -163,20 +324,50 @@ final public class OkapiClient {
}
}
- // not used: attrnames
+ cache.setAttributes(parseAttributes(response.getJSONArray(CACHE_ATTRNAMES)));
cache.setLogs(parseLogs(response.getJSONArray(CACHE_LATEST_LOGS)));
cache.setHidden(parseDate(response.getString(CACHE_HIDDEN)));
+ //TODO: Store license per cache
+ //cache.setLicense(response.getString("attribution_note"));
+ cache.setWaypoints(parseWaypoints(response.getJSONArray(CACHE_WPTS)), false);
+ if (!response.isNull(CACHE_IS_WATCHED)) {
+ cache.setOnWatchlist(response.getBoolean(CACHE_IS_WATCHED));
+ }
+ if (!response.isNull(CACHE_MY_NOTES)) {
+ cache.setPersonalNote(response.getString(CACHE_MY_NOTES));
+ }
+ cache.setLogPasswordRequired(response.getBoolean(CACHE_REQ_PASSWORD));
cache.setDetailedUpdatedNow();
// save full detailed caches
cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB));
- } catch (JSONException e) {
+ } catch (final JSONException e) {
Log.e("OkapiClient.parseCache", e);
}
return cache;
}
- private static String absoluteUrl(String url, String geocode) {
+ private static void parseCoreCache(final JSONObject response, final Geocache cache) throws JSONException {
+ cache.setGeocode(response.getString(CACHE_CODE));
+ cache.setName(response.getString(CACHE_NAME));
+ // not used: names
+ setLocation(cache, response.getString(CACHE_LOCATION));
+ cache.setType(getCacheType(response.getString(CACHE_TYPE)));
+
+ final String status = response.getString(CACHE_STATUS);
+ cache.setDisabled(status.equalsIgnoreCase(CACHE_STATUS_DISABLED));
+ cache.setArchived(status.equalsIgnoreCase(CACHE_STATUS_ARCHIVED));
+
+ cache.setSize(getCacheSize(response));
+ cache.setDifficulty((float) response.getDouble(CACHE_DIFFICULTY));
+ cache.setTerrain((float) response.getDouble(CACHE_TERRAIN));
+
+ if (!response.isNull(CACHE_IS_FOUND)) {
+ cache.setFound(response.getBoolean(CACHE_IS_FOUND));
+ }
+ }
+
+ private static String absoluteUrl(final String url, final String geocode) {
final Uri uri = Uri.parse(url);
if (!uri.isAbsolute()) {
@@ -189,16 +380,16 @@ final public class OkapiClient {
return url;
}
- private static String parseUser(JSONObject user) throws JSONException {
+ private static String parseUser(final JSONObject user) throws JSONException {
return user.getString(USER_USERNAME);
}
- private static List<LogEntry> parseLogs(JSONArray logsJSON) {
+ private static List<LogEntry> parseLogs(final JSONArray logsJSON) {
List<LogEntry> result = null;
for (int i = 0; i < logsJSON.length(); i++) {
try {
- JSONObject logResponse = logsJSON.getJSONObject(i);
- LogEntry log = new LogEntry(
+ final JSONObject logResponse = logsJSON.getJSONObject(i);
+ final LogEntry log = new LogEntry(
parseUser(logResponse.getJSONObject(LOG_USER)),
parseDate(logResponse.getString(LOG_DATE)).getTime(),
parseLogType(logResponse.getString(LOG_TYPE)),
@@ -207,14 +398,38 @@ final public class OkapiClient {
result = new ArrayList<LogEntry>();
}
result.add(log);
- } catch (JSONException e) {
+ } catch (final JSONException e) {
Log.e("OkapiClient.parseLogs", e);
}
}
return result;
}
- private static LogType parseLogType(String logType) {
+ private static List<Waypoint> parseWaypoints(final JSONArray wptsJson) {
+ List<Waypoint> result = null;
+ for (int i = 0; i < wptsJson.length(); i++) {
+ try {
+ final JSONObject wptResponse = wptsJson.getJSONObject(i);
+ final Waypoint wpt = new Waypoint(wptResponse.getString(WPT_NAME),
+ parseWptType(wptResponse.getString(WPT_TYPE)),
+ false);
+ wpt.setNote(wptResponse.getString(WPT_DESCRIPTION));
+ final Geopoint pt = parseCoords(wptResponse.getString(WPT_LOCATION));
+ if (pt != null) {
+ wpt.setCoords(pt);
+ }
+ if (result == null) {
+ result = new ArrayList<Waypoint>();
+ }
+ result.add(wpt);
+ } catch (final JSONException e) {
+ Log.e("OkapiClient.parseWaypoints", e);
+ }
+ }
+ return result;
+ }
+
+ private static LogType parseLogType(final String logType) {
if ("Found it".equalsIgnoreCase(logType)) {
return LogType.FOUND_IT;
}
@@ -224,20 +439,74 @@ final public class OkapiClient {
return LogType.NOTE;
}
+ private static WaypointType parseWptType(final String wptType) {
+ if ("parking".equalsIgnoreCase(wptType)) {
+ return WaypointType.PARKING;
+ }
+ if ("path".equalsIgnoreCase(wptType)) {
+ return WaypointType.TRAILHEAD;
+ }
+ if ("stage".equalsIgnoreCase(wptType)) {
+ return WaypointType.STAGE;
+ }
+ if ("physical-stage".equalsIgnoreCase(wptType)) {
+ return WaypointType.STAGE;
+ }
+ if ("virtual-stage".equalsIgnoreCase(wptType)) {
+ return WaypointType.PUZZLE;
+ }
+ if ("final".equalsIgnoreCase(wptType)) {
+ return WaypointType.FINAL;
+ }
+ if ("poi".equalsIgnoreCase(wptType)) {
+ return WaypointType.TRAILHEAD;
+ }
+ return WaypointType.WAYPOINT;
+ }
+
private static Date parseDate(final String date) {
- final SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault());
final String strippedDate = date.replaceAll("\\+0([0-9]){1}\\:00", "+0$100");
try {
return ISO8601DATEFORMAT.parse(strippedDate);
- } catch (ParseException e) {
+ } catch (final ParseException e) {
Log.e("OkapiClient.parseDate", e);
}
return null;
}
+ private static Geopoint parseCoords(final String location) {
+ final String latitude = StringUtils.substringBefore(location, SEPARATOR_STRING);
+ final String longitude = StringUtils.substringAfter(location, SEPARATOR_STRING);
+ if (StringUtils.isNotBlank(latitude) && StringUtils.isNotBlank(longitude)) {
+ return new Geopoint(latitude, longitude);
+ }
+
+ return null;
+ }
+
+ private static List<String> parseAttributes(final JSONArray nameList) {
+
+ final List<String> result = new ArrayList<String>();
+
+ for (int i = 0; i < nameList.length(); i++) {
+ try {
+ final String name = nameList.getString(i);
+ final CacheAttribute attr = CacheAttribute.getByOcId(AttributeParser.getOcDeId(name));
+
+ if (attr != null) {
+ result.add(attr.rawName);
+ }
+ } catch (final JSONException e) {
+ Log.e("OkapiClient.parseAttributes", e);
+ }
+ }
+
+ return result;
+ }
+
private static void setLocation(final Geocache cache, final String location) {
- final String latitude = StringUtils.substringBefore(location, "|");
- final String longitude = StringUtils.substringAfter(location, "|");
+ final String latitude = StringUtils.substringBefore(location, SEPARATOR_STRING);
+ final String longitude = StringUtils.substringAfter(location, SEPARATOR_STRING);
cache.setCoords(new Geopoint(latitude, longitude));
}
@@ -248,7 +517,7 @@ final public class OkapiClient {
double size = 0;
try {
size = response.getDouble(CACHE_SIZE);
- } catch (JSONException e) {
+ } catch (final JSONException e) {
Log.e("OkapiClient.getCacheSize", e);
}
switch ((int) Math.round(size)) {
@@ -281,14 +550,57 @@ final public class OkapiClient {
if (cacheType.equalsIgnoreCase("Virtual")) {
return CacheType.VIRTUAL;
}
+ if (cacheType.equalsIgnoreCase("Event")) {
+ return CacheType.EVENT;
+ }
+ if (cacheType.equalsIgnoreCase("Webcam")) {
+ return CacheType.WEBCAM;
+ }
+ if (cacheType.equalsIgnoreCase("Math/Physics")) {
+ return CacheType.MYSTERY;
+ }
+ if (cacheType.equalsIgnoreCase("Drive-In")) {
+ return CacheType.TRADITIONAL;
+ }
return CacheType.UNKNOWN;
}
- private static JSONObject request(final IConnector connector, final String service, final Parameters params) {
+ private static String getCoreFields(final OCApiConnector connector) {
if (connector == null) {
- return null;
+ Log.e("OkapiClient.getCoreFields called with invalid connector");
+ return StringUtils.EMPTY;
}
- if (!(connector instanceof OCApiConnector)) {
+
+ if (connector.getSupportedAuthLevel() == OAuthLevel.Level3) {
+ return SERVICE_CACHE_CORE_FIELDS + SEPARATOR + SERVICE_CACHE_CORE_L3_FIELDS;
+ }
+
+ return SERVICE_CACHE_CORE_FIELDS;
+ }
+
+ private static String getFullFields(final OCApiConnector connector) {
+ if (connector == null) {
+ Log.e("OkapiClient.getFullFields called with invalid connector");
+ return StringUtils.EMPTY;
+ }
+
+ final StringBuilder res = new StringBuilder(500);
+
+ res.append(SERVICE_CACHE_CORE_FIELDS);
+ res.append(SEPARATOR).append(SERVICE_CACHE_ADDITIONAL_FIELDS);
+ if (connector.getSupportedAuthLevel() == OAuthLevel.Level3) {
+ res.append(SEPARATOR).append(SERVICE_CACHE_CORE_L3_FIELDS);
+ res.append(SEPARATOR).append(SERVICE_CACHE_ADDITIONAL_L3_FIELDS);
+ }
+ if (connector.getApiSupport() == ApiSupport.current) {
+ res.append(SEPARATOR).append(SERVICE_CACHE_ADDITIONAL_CURRENT_FIELDS);
+ }
+
+ return res.toString();
+ }
+
+ private static JSONObject request(final OCApiConnector connector, final OkapiService service, final Parameters params) {
+ if (connector == null) {
return null;
}
@@ -297,10 +609,15 @@ final public class OkapiClient {
return null;
}
- ((OCApiConnector) connector).addAuthentication(params);
params.add("langpref", getPreferredLanguage());
- final String uri = "http://" + host + service;
+ if (connector.getSupportedAuthLevel() == OAuthLevel.Level3) {
+ OAuth.signOAuth(host, service.methodName, "GET", false, params, Settings.getOCDETokenPublic(), Settings.getOCDETokenSecret(), connector.getCK(), connector.getCS());
+ } else {
+ connector.addAuthentication(params);
+ }
+
+ final String uri = "http://" + host + service.methodName;
return Network.requestJSON(uri, params);
}
@@ -311,4 +628,79 @@ final public class OkapiClient {
}
return "en";
}
+
+ private static void addFilterParams(final Map<String, String> valueMap, final OCApiConnector connector) {
+ if (!Settings.isExcludeDisabledCaches()) {
+ valueMap.put("status", "Available|Temporarily unavailable");
+ }
+ if (Settings.isExcludeMyCaches() && connector.getSupportedAuthLevel() == OAuthLevel.Level3) {
+ valueMap.put("exclude_my_own", "true");
+ valueMap.put("found_status", "notfound_only");
+ }
+ if (Settings.getCacheType() != CacheType.ALL) {
+ valueMap.put("type", getFilterFromType(Settings.getCacheType()));
+ }
+ }
+
+ private static void addRetrieveParams(final Parameters params, final OCApiConnector connector) {
+ params.add("retr_method", METHOD_RETRIEVE_CACHES);
+ params.add("retr_params", "{\"fields\": \"" + getCoreFields(connector) + "\"}");
+ params.add("wrap", "true");
+ }
+
+ private static String getFilterFromType(final CacheType cacheType) {
+ switch (cacheType) {
+ case EVENT:
+ return "Event";
+ case MULTI:
+ return "Multi";
+ case MYSTERY:
+ return "Quiz";
+ case TRADITIONAL:
+ return "Traditional";
+ case VIRTUAL:
+ return "Virtual";
+ case WEBCAM:
+ return "Webcam";
+ default:
+ return "";
+ }
+ }
+
+ public static UserInfo getUserInfo(final OCApiLiveConnector connector) {
+ final Parameters params = new Parameters("fields", USER_INFO_FIELDS);
+
+ final JSONObject data = request(connector, OkapiService.SERVICE_USER, params);
+
+ if (data == null) {
+ return new UserInfo(StringUtils.EMPTY, 0, UserInfoStatus.FAILED);
+ }
+
+ String name = StringUtils.EMPTY;
+ boolean successUserName = false;
+
+ if (!data.isNull(USER_USERNAME)) {
+ try {
+ name = data.getString(USER_USERNAME);
+ successUserName = true;
+ } catch (final JSONException e) {
+ Log.e("OkapiClient.getUserInfo - name", e);
+ }
+ }
+
+ int finds = 0;
+ boolean successFinds = false;
+
+ if (!data.isNull(USER_CACHES_FOUND)) {
+ try {
+ finds = data.getInt(USER_CACHES_FOUND);
+ successFinds = true;
+ } catch (final JSONException e) {
+ Log.e("OkapiClient.getUserInfo - finds", e);
+ }
+ }
+
+ return new UserInfo(name, finds, successUserName && successFinds ? UserInfoStatus.SUCCESSFUL : UserInfoStatus.FAILED);
+ }
+
}
diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java b/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java
new file mode 100644
index 0000000..22cb9dd
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java
@@ -0,0 +1,70 @@
+package cgeo.geocaching.connector.oc;
+
+import cgeo.geocaching.Geocache;
+import cgeo.geocaching.LogCacheActivity;
+import cgeo.geocaching.TrackableLog;
+import cgeo.geocaching.connector.ILoggingManager;
+import cgeo.geocaching.connector.ImageResult;
+import cgeo.geocaching.connector.LogResult;
+import cgeo.geocaching.enumerations.LogType;
+import cgeo.geocaching.enumerations.StatusCode;
+
+import android.app.Activity;
+import android.net.Uri;
+
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.List;
+
+public class OkapiLoggingManager implements ILoggingManager {
+
+ private final OCApiLiveConnector connector;
+ private final Geocache cache;
+ private LogCacheActivity activity;
+
+ private final static List<LogType> standardLogTypes = Arrays.asList(LogType.FOUND_IT, LogType.DIDNT_FIND_IT, LogType.NOTE, LogType.NEEDS_MAINTENANCE);
+ private final static List<LogType> eventLogTypes = Arrays.asList(LogType.WILL_ATTEND, LogType.ATTENDED, LogType.NOTE);
+
+ public OkapiLoggingManager(Activity activity, OCApiLiveConnector connector, Geocache cache) {
+ this.connector = connector;
+ this.cache = cache;
+ this.activity = (LogCacheActivity) activity;
+ }
+
+ @Override
+ public void init() {
+ activity.onLoadFinished();
+ }
+
+ @Override
+ public LogResult postLog(Geocache cache, LogType logType, Calendar date, String log, String logPassword, List<TrackableLog> trackableLogs) {
+ final LogResult result = OkapiClient.postLog(cache, logType, date, log, logPassword, connector);
+ connector.login(null, null);
+ return result;
+ }
+
+ @Override
+ public ImageResult postLogImage(String logId, String imageCaption, String imageDescription, Uri imageUri) {
+ return new ImageResult(StatusCode.LOG_POST_ERROR, "");
+ }
+
+ @Override
+ public boolean hasLoaderError() {
+ return false;
+ }
+
+ @Override
+ public List<TrackableLog> getTrackables() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List<LogType> getPossibleLogTypes() {
+ if (cache.isEventCache()) {
+ return eventLogTypes;
+ }
+
+ return standardLogTypes;
+ }
+}
diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiService.java b/main/src/cgeo/geocaching/connector/oc/OkapiService.java
new file mode 100644
index 0000000..ec09527
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/oc/OkapiService.java
@@ -0,0 +1,21 @@
+package cgeo.geocaching.connector.oc;
+
+import cgeo.geocaching.connector.oc.OCApiConnector.OAuthLevel;
+
+
+enum OkapiService {
+ SERVICE_CACHE("/okapi/services/caches/geocache", OAuthLevel.Level1),
+ SERVICE_SEARCH_AND_RETRIEVE("/okapi/services/caches/shortcuts/search_and_retrieve", OAuthLevel.Level1),
+ SERVICE_MARK_CACHE("/okapi/services/caches/mark", OAuthLevel.Level3),
+ SERVICE_SUBMIT_LOG("/okapi/services/logs/submit", OAuthLevel.Level3),
+ SERVICE_USER("/okapi/services/users/user", OAuthLevel.Level1);
+
+ final String methodName;
+ final OAuthLevel level;
+
+ OkapiService(final String methodName, final OAuthLevel level) {
+ this.methodName = methodName;
+ this.level = level;
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/connector/oc/UserInfo.java b/main/src/cgeo/geocaching/connector/oc/UserInfo.java
new file mode 100644
index 0000000..0dc0440
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/oc/UserInfo.java
@@ -0,0 +1,41 @@
+package cgeo.geocaching.connector.oc;
+
+import cgeo.geocaching.R;
+
+public class UserInfo {
+
+ public enum UserInfoStatus {
+ NOT_RETRIEVED(R.string.init_login_popup_working),
+ SUCCESSFUL(R.string.init_login_popup_ok),
+ FAILED(R.string.init_login_popup_failed),
+ NOT_SUPPORTED(R.string.init_login_popup_not_authorized);
+
+ public final int resId;
+
+ UserInfoStatus(int resId) {
+ this.resId = resId;
+ }
+ }
+
+ private final String name;
+ private final int finds;
+ private final UserInfoStatus status;
+
+ UserInfo(String name, int finds, UserInfoStatus status) {
+ this.name = name;
+ this.finds = finds;
+ this.status = status;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getFinds() {
+ return finds;
+ }
+
+ public UserInfoStatus getStatus() {
+ return status;
+ }
+}
diff --git a/main/src/cgeo/geocaching/connector/ox/OXConnector.java b/main/src/cgeo/geocaching/connector/ox/OXConnector.java
index eec07e3..af33bb6 100644
--- a/main/src/cgeo/geocaching/connector/ox/OXConnector.java
+++ b/main/src/cgeo/geocaching/connector/ox/OXConnector.java
@@ -3,7 +3,7 @@ package cgeo.geocaching.connector.ox;
import cgeo.geocaching.Geocache;
import cgeo.geocaching.ICache;
import cgeo.geocaching.SearchResult;
-import cgeo.geocaching.Settings;
+import cgeo.geocaching.settings.Settings;
import cgeo.geocaching.connector.AbstractConnector;
import cgeo.geocaching.connector.capability.ISearchByCenter;
import cgeo.geocaching.connector.capability.ISearchByGeocode;
diff --git a/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java b/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java
new file mode 100644
index 0000000..cd32d87
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java
@@ -0,0 +1,10 @@
+package cgeo.geocaching.connector.trackable;
+
+
+public abstract class AbstractTrackableConnector implements TrackableConnector {
+
+ @Override
+ public boolean isLoggable() {
+ return false;
+ }
+}
diff --git a/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java b/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java
new file mode 100644
index 0000000..8387076
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java
@@ -0,0 +1,42 @@
+package cgeo.geocaching.connector.trackable;
+
+import cgeo.geocaching.Trackable;
+import cgeo.geocaching.network.Network;
+import cgeo.geocaching.utils.Log;
+
+import java.util.regex.Pattern;
+
+public class GeokretyConnector extends AbstractTrackableConnector {
+
+ private static final Pattern PATTERN_GK_CODE = Pattern.compile("GK[0-9A-F]{4,}");
+
+ @Override
+ public boolean canHandleTrackable(String geocode) {
+ return geocode != null && PATTERN_GK_CODE.matcher(geocode).matches();
+ }
+
+ @Override
+ public String getUrl(Trackable trackable) {
+ return "http://geokrety.org/konkret.php?id=" + getId(trackable.getGeocode());
+ }
+
+ @Override
+ public Trackable searchTrackable(String geocode, String guid, String id) {
+ final String page = Network.getResponseData(Network.getRequest("http://geokrety.org/export2.php?gkid=" + getId(geocode)));
+ if (page == null) {
+ return null;
+ }
+ return GeokretyParser.parse(page);
+ }
+
+ private static int getId(String geocode) {
+ try {
+ final String hex = geocode.substring(2);
+ return Integer.parseInt(hex, 16);
+ } catch (final NumberFormatException e) {
+ Log.e("Trackable.getUrl", e);
+ }
+ return -1;
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java b/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java
new file mode 100644
index 0000000..66ca5f7
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java
@@ -0,0 +1,82 @@
+package cgeo.geocaching.connector.trackable;
+
+import cgeo.geocaching.R;
+import cgeo.geocaching.Trackable;
+import cgeo.geocaching.cgeoapplication;
+import cgeo.geocaching.utils.Log;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import android.sax.Element;
+import android.sax.EndTextElementListener;
+import android.sax.RootElement;
+import android.sax.StartElementListener;
+import android.util.Xml;
+
+public class GeokretyParser {
+
+ public static Trackable parse(final String page) {
+ final Trackable trackable = new Trackable();
+
+ final RootElement root = new RootElement("gkxml");
+ final Element geokret = root.getChild("geokrety").getChild("geokret");
+
+ geokret.setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String name) {
+ trackable.setName(name);
+ }
+ });
+
+ geokret.setStartElementListener(new StartElementListener() {
+
+ @Override
+ public void start(Attributes attributes) {
+ try {
+ if (attributes.getIndex("id") > -1) {
+ trackable.setGeocode(geocode(Integer.valueOf(attributes.getValue("id"))));
+ }
+ if (attributes.getIndex("dist") > -1) {
+ trackable.setDistance(Float.valueOf(attributes.getValue("dist")));
+ }
+ if (attributes.getIndex("type") > -1) {
+ trackable.setType(getType(Integer.valueOf(attributes.getValue("type"))));
+ }
+ } catch (final NumberFormatException e) {
+ Log.e("Parsing geokret", e);
+ }
+ }
+ });
+
+ try {
+ Xml.parse(page, root.getContentHandler());
+ return trackable;
+ } catch (final SAXException e) {
+ Log.w("Cannot parse geokrety", e);
+ }
+
+ return null;
+ }
+
+ protected static String getType(int type) {
+ switch (type) {
+ case 0:
+ return cgeoapplication.getInstance().getString(R.string.geokret_type_traditional);
+ case 1:
+ return cgeoapplication.getInstance().getString(R.string.geokret_type_book_or_media);
+ case 2:
+ return cgeoapplication.getInstance().getString(R.string.geokret_type_human);
+ case 3:
+ return cgeoapplication.getInstance().getString(R.string.geokret_type_coin);
+ case 4:
+ return cgeoapplication.getInstance().getString(R.string.geokret_type_post);
+ }
+ return null;
+ }
+
+ protected static String geocode(final int id) {
+ return String.format("GK%04X", id);
+ }
+}
diff --git a/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java b/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java
new file mode 100644
index 0000000..c09dc07
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java
@@ -0,0 +1,19 @@
+package cgeo.geocaching.connector.trackable;
+
+import cgeo.geocaching.Trackable;
+
+/**
+ * Methods to be implemented by any connector for handling trackables
+ *
+ */
+public interface TrackableConnector {
+
+ public boolean canHandleTrackable(final String geocode);
+
+ public String getUrl(final Trackable trackable);
+
+ public boolean isLoggable();
+
+ public Trackable searchTrackable(String geocode, String guid, String id);
+
+}
diff --git a/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java b/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java
new file mode 100644
index 0000000..0dac6cc
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java
@@ -0,0 +1,50 @@
+package cgeo.geocaching.connector.trackable;
+
+import cgeo.geocaching.Trackable;
+import cgeo.geocaching.connector.gc.GCParser;
+
+import java.util.regex.Pattern;
+
+public class TravelBugConnector extends AbstractTrackableConnector {
+
+ /**
+ * TB codes really start with TB1, there is no padding or minimum length
+ */
+ private final static Pattern PATTERN_TB_CODE = Pattern.compile("(TB[0-9A-Z]+)|([0-9A-Z]{6})", Pattern.CASE_INSENSITIVE);
+
+ @Override
+ public boolean canHandleTrackable(String geocode) {
+ return TravelBugConnector.PATTERN_TB_CODE.matcher(geocode).matches();
+ }
+
+ @Override
+ public String getUrl(Trackable trackable) {
+ return "http://www.geocaching.com//track/details.aspx?tracker=" + trackable.getGeocode();
+ }
+
+ @Override
+ public boolean isLoggable() {
+ return true;
+ }
+
+ @Override
+ public Trackable searchTrackable(String geocode, String guid, String id) {
+ return GCParser.searchTrackable(geocode, guid, id);
+ }
+
+ /**
+ * initialization on demand holder pattern
+ */
+ private static class Holder {
+ private static final TravelBugConnector INSTANCE = new TravelBugConnector();
+ }
+
+ private TravelBugConnector() {
+ // singleton
+ }
+
+ public static TravelBugConnector getInstance() {
+ return Holder.INSTANCE;
+ }
+
+} \ No newline at end of file
diff --git a/main/src/cgeo/geocaching/connector/trackable/UnknownTrackableConnector.java b/main/src/cgeo/geocaching/connector/trackable/UnknownTrackableConnector.java
new file mode 100644
index 0000000..0295927
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/trackable/UnknownTrackableConnector.java
@@ -0,0 +1,24 @@
+package cgeo.geocaching.connector.trackable;
+
+import cgeo.geocaching.Trackable;
+
+import org.apache.commons.lang3.StringUtils;
+
+public class UnknownTrackableConnector extends AbstractTrackableConnector {
+
+ @Override
+ public boolean canHandleTrackable(String geocode) {
+ return false;
+ }
+
+ @Override
+ public String getUrl(Trackable trackable) {
+ return StringUtils.EMPTY;
+ }
+
+ @Override
+ public Trackable searchTrackable(String geocode, String guid, String id) {
+ return null;
+ }
+
+}