aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--main/res/layout/detail.xml20
-rw-r--r--main/res/values/strings.xml2
-rw-r--r--main/src/cgeo/geocaching/cgBase.java67
-rw-r--r--main/src/cgeo/geocaching/cgCache.java8
-rw-r--r--main/src/cgeo/geocaching/cgHtmlImg.java5
-rw-r--r--main/src/cgeo/geocaching/cgeodetail.java23
-rw-r--r--main/src/cgeo/geocaching/connector/AbstractConnector.java25
-rw-r--r--main/src/cgeo/geocaching/connector/ConnectorFactory.java23
-rw-r--r--main/src/cgeo/geocaching/connector/GCConnector.java100
-rw-r--r--main/src/cgeo/geocaching/connector/IConnector.java65
-rw-r--r--main/src/cgeo/geocaching/connector/OCConnector.java21
-rw-r--r--main/src/cgeo/geocaching/connector/OXConnector.java12
-rw-r--r--main/src/cgeo/geocaching/connector/opencaching/ApiOpenCachingConnector.java52
-rw-r--r--main/src/cgeo/geocaching/connector/opencaching/OkapiClient.java260
-rw-r--r--main/src/cgeo/geocaching/connector/opencaching/OpenCachingConnector.java41
15 files changed, 632 insertions, 92 deletions
diff --git a/main/res/layout/detail.xml b/main/res/layout/detail.xml
index 2d0e773..7330875 100644
--- a/main/res/layout/detail.xml
+++ b/main/res/layout/detail.xml
@@ -279,6 +279,26 @@
android:orientation="vertical">
</LinearLayout>
</LinearLayout>
+ <LinearLayout android:id="@+id/license_box"
+ android:visibility="gone"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+ <RelativeLayout style="@style/separator_horizontal_layout" >
+ <View style="@style/separator_horizontal" />
+ <TextView style="@style/separator_horizontal_headline"
+ android:text="@string/cache_license" />
+ </RelativeLayout>
+ <TextView android:id="@+id/license"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left"
+ android:padding="3dip"
+ android:textSize="14dip"
+ android:textColor="?text_color"
+ android:linksClickable="true"
+ android:textColorLink="?text_color_link" />
+ </LinearLayout>
</LinearLayout>
</ScrollView>
</LinearLayout> \ No newline at end of file
diff --git a/main/res/values/strings.xml b/main/res/values/strings.xml
index 73cb8cf..8207a77 100644
--- a/main/res/values/strings.xml
+++ b/main/res/values/strings.xml
@@ -581,6 +581,7 @@
<string name="cache_export_fieldnote">Export as Field Notes</string>
<string name="cache_clear_history">Clear history</string>
<string name="cache_remove_from_history">Remove from history</string>
+ <string name="cache_license">License</string>
<!-- file list base -->
<string name="file_searching_in">Searching for files\nin</string>
@@ -951,6 +952,7 @@
<string name="changelog">\n
<b>next version</b>\n
· new: import of LOC files\n
+ · new: search for geo code supports OpenCaching.DE/.PL/.US\n
\n\n
<b>18.09.2011</b>\n
· fix: Android 3+ compatibility\n
diff --git a/main/src/cgeo/geocaching/cgBase.java b/main/src/cgeo/geocaching/cgBase.java
index 1e9eb7a..edccd2c 100644
--- a/main/src/cgeo/geocaching/cgBase.java
+++ b/main/src/cgeo/geocaching/cgBase.java
@@ -1,6 +1,7 @@
package cgeo.geocaching;
import cgeo.geocaching.activity.ActivityMixin;
+import cgeo.geocaching.connector.ConnectorFactory;
import cgeo.geocaching.enumerations.CacheSize;
import cgeo.geocaching.enumerations.CacheType;
import cgeo.geocaching.enumerations.WaypointType;
@@ -2583,71 +2584,7 @@ public class cgBase {
return search.getCurrentId();
}
- final String host = "www.geocaching.com";
- final String path = "/seek/cache_details.aspx";
- final String method = "GET";
- final Map<String, String> params = new HashMap<String, String>();
- if (StringUtils.isNotBlank(geocode)) {
- params.put("wp", geocode);
- } else if (StringUtils.isNotBlank(guid)) {
- params.put("guid", guid);
- }
- params.put("decrypt", "y");
-
- String page = requestLogged(false, host, path, method, params, false, false, false);
-
- if (StringUtils.isEmpty(page)) {
- if (app.isThere(geocode, guid, true, false)) {
- if (StringUtils.isBlank(geocode) && StringUtils.isNotBlank(guid)) {
- Log.i(cgSettings.tag, "Loading old cache from cache.");
-
- geocode = app.getGeocode(guid);
- }
-
- final List<cgCache> cacheList = new ArrayList<cgCache>();
- cacheList.add(app.getCacheByGeocode(geocode));
- search.addGeocode(geocode);
- search.error = null;
-
- app.addSearch(search, cacheList, false, reason);
-
- cacheList.clear();
-
- return search.getCurrentId();
- }
-
- Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No data from server");
- return null;
- }
-
- final cgCacheWrap caches = parseCache(page, reason);
- if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) {
- if (caches != null && StringUtils.isNotBlank(caches.error)) {
- search.error = caches.error;
- }
- if (caches != null && StringUtils.isNotBlank(caches.url)) {
- search.url = caches.url;
- }
-
- app.addSearch(search, null, true, reason);
-
- Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No cache parsed");
- return null;
- }
-
- if (app == null) {
- Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No application found");
- return null;
- }
-
- List<cgCache> cacheList = processSearchResults(search, caches, 0, 0, null);
-
- app.addSearch(search, cacheList, true, reason);
-
- page = null;
- cacheList.clear();
-
- return search.getCurrentId();
+ return ConnectorFactory.getConnector(geocode).searchByGeocode(this, geocode, guid, app, search, reason);
}
public UUID searchByOffline(Map<String, Object> parameters) {
diff --git a/main/src/cgeo/geocaching/cgCache.java b/main/src/cgeo/geocaching/cgCache.java
index 68aaf49..a9b41dc 100644
--- a/main/src/cgeo/geocaching/cgCache.java
+++ b/main/src/cgeo/geocaching/cgCache.java
@@ -458,4 +458,12 @@ public class cgCache implements ICache {
return name;
}
+ public boolean supportsUserActions() {
+ return getConnector().supportsUserActions();
+ }
+
+ public boolean supportsCachesAround() {
+ return getConnector().supportsCachesAround();
+ }
+
}
diff --git a/main/src/cgeo/geocaching/cgHtmlImg.java b/main/src/cgeo/geocaching/cgHtmlImg.java
index 40575d2..1196598 100644
--- a/main/src/cgeo/geocaching/cgHtmlImg.java
+++ b/main/src/cgeo/geocaching/cgHtmlImg.java
@@ -1,5 +1,7 @@
package cgeo.geocaching;
+import cgeo.geocaching.connector.ConnectorFactory;
+import cgeo.geocaching.connector.IConnector;
import cgeo.geocaching.utils.CryptUtils;
import org.apache.commons.lang3.StringUtils;
@@ -175,7 +177,8 @@ public class cgHtmlImg implements Html.ImageGetter {
uri = Uri.parse(url);
if (uri.isAbsolute() == false) {
- url = "http://www.geocaching.com" + url;
+ IConnector connector = ConnectorFactory.getConnector(geocode);
+ url = "http://" + connector.getHost() + url;
}
} catch (Exception e) {
Log.e(cgSettings.tag, "cgHtmlImg.getDrawable (parse URL): " + e.toString());
diff --git a/main/src/cgeo/geocaching/cgeodetail.java b/main/src/cgeo/geocaching/cgeodetail.java
index 16af323..0c5a252 100644
--- a/main/src/cgeo/geocaching/cgeodetail.java
+++ b/main/src/cgeo/geocaching/cgeodetail.java
@@ -4,6 +4,8 @@ import cgeo.geocaching.activity.AbstractActivity;
import cgeo.geocaching.apps.cache.GeneralAppsFactory;
import cgeo.geocaching.apps.cache.navi.NavigationAppFactory;
import cgeo.geocaching.compatibility.Compatibility;
+import cgeo.geocaching.connector.ConnectorFactory;
+import cgeo.geocaching.connector.IConnector;
import cgeo.geocaching.enumerations.CacheSize;
import cgeo.geocaching.utils.CollectionUtils;
import cgeo.geocaching.utils.CryptUtils;
@@ -50,6 +52,7 @@ import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;
+import android.widget.TextView.BufferType;
import java.net.URLEncoder;
import java.util.ArrayList;
@@ -512,7 +515,7 @@ public class cgeodetail extends AbstractActivity {
menu.add(1, 5, 0, res.getString(R.string.cache_menu_spoilers)).setIcon(android.R.drawable.ic_menu_gallery); // spoiler images
}
- if (cache != null && cache.coords != null) {
+ if (cache != null && cache.coords != null && cache.supportsCachesAround()) {
menu.add(0, 10, 0, res.getString(R.string.cache_menu_around)).setIcon(android.R.drawable.ic_menu_rotate); // caches around
}
@@ -1083,6 +1086,21 @@ public class cgeodetail extends AbstractActivity {
displayLogs();
+ // data license
+ IConnector connector = ConnectorFactory.getConnector(cache);
+ if (connector != null) {
+ String license = connector.getLicenseText(cache);
+ if (StringUtils.isNotBlank(license)) {
+ ((LinearLayout) findViewById(R.id.license_box)).setVisibility(View.VISIBLE);
+ TextView licenseView = ((TextView) findViewById(R.id.license));
+ licenseView.setText(Html.fromHtml(license), BufferType.SPANNABLE);
+ licenseView.setClickable(true);
+ licenseView.setMovementMethod(LinkMovementMethod.getInstance());
+ } else {
+ ((LinearLayout) findViewById(R.id.license_box)).setVisibility(View.GONE);
+ }
+ }
+
if (geo != null)
geoUpdate.updateLoc(geo);
}
@@ -1904,6 +1922,9 @@ public class cgeodetail extends AbstractActivity {
if (view == null) {
return;
}
+ if (!cache.supportsUserActions()) {
+ return;
+ }
try {
registerForContextMenu(view);
diff --git a/main/src/cgeo/geocaching/connector/AbstractConnector.java b/main/src/cgeo/geocaching/connector/AbstractConnector.java
index 50a328e..54fab87 100644
--- a/main/src/cgeo/geocaching/connector/AbstractConnector.java
+++ b/main/src/cgeo/geocaching/connector/AbstractConnector.java
@@ -1,6 +1,11 @@
package cgeo.geocaching.connector;
+import cgeo.geocaching.cgBase;
import cgeo.geocaching.cgCache;
+import cgeo.geocaching.cgSearch;
+import cgeo.geocaching.cgeoapplication;
+
+import java.util.UUID;
public abstract class AbstractConnector implements IConnector {
@@ -23,4 +28,24 @@ public abstract class AbstractConnector implements IConnector {
public boolean supportsLogging() {
return false;
}
+
+ @Override
+ public String getLicenseText(final cgCache cache) {
+ return null;
+ }
+
+ @Override
+ public boolean supportsUserActions() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsCachesAround() {
+ return false;
+ }
+
+ @Override
+ public UUID searchByGeocode(cgBase base, String geocode, String guid, cgeoapplication app, cgSearch search, int reason) {
+ return null;
+ }
}
diff --git a/main/src/cgeo/geocaching/connector/ConnectorFactory.java b/main/src/cgeo/geocaching/connector/ConnectorFactory.java
index 867f48d..788df13 100644
--- a/main/src/cgeo/geocaching/connector/ConnectorFactory.java
+++ b/main/src/cgeo/geocaching/connector/ConnectorFactory.java
@@ -1,10 +1,25 @@
package cgeo.geocaching.connector;
import cgeo.geocaching.cgCache;
+import cgeo.geocaching.connector.opencaching.ApiOpenCachingConnector;
+import cgeo.geocaching.connector.opencaching.OpenCachingConnector;
public final class ConnectorFactory {
private static final GCConnector GC_CONNECTOR = new GCConnector();
- private static final IConnector[] connectors = new IConnector[] { GC_CONNECTOR, new OCConnector(), new OXConnector() };
+ private static final IConnector[] connectors = new IConnector[] {
+ GC_CONNECTOR,
+ new OpenCachingConnector("OpenCaching.DE", "www.opencaching.de", "OC"),
+ new OpenCachingConnector("OpenCaching.CZ", "www.opencaching.cz", "OZ"),
+ new ApiOpenCachingConnector("OpenCaching.CO.UK", "www.opencaching.org.uk", "OK", "arU4okouc4GEjMniE2fq"),
+ new OpenCachingConnector("OpenCaching.ES", "www.opencachingspain.es", "OC"),
+ new OpenCachingConnector("OpenCaching.IT", "www.opencaching.it", "OC"),
+ new OpenCachingConnector("OpenCaching.JP", "www.opencaching.jp", "OJ"),
+ new OpenCachingConnector("OpenCaching.NO/SE", "www.opencaching.no", "OS"),
+ new OpenCachingConnector("OpenCaching.NL", "www.opencaching.nl", "OB"),
+ new ApiOpenCachingConnector("OpenCaching.PL", "www.opencaching.pl", "OP", "GkxM47WkUkLQXXsZ9qSh"),
+ new ApiOpenCachingConnector("OpenCaching.US", "www.opencaching.us", "OU", "pTsYAYSXFcfcRQnYE6uA"),
+ new OXConnector()
+ };
public static IConnector[] getConnectors() {
return connectors;
@@ -20,8 +35,12 @@ public final class ConnectorFactory {
}
public static IConnector getConnector(cgCache cache) {
+ return getConnector(cache.geocode);
+ }
+
+ public static IConnector getConnector(String geocode) {
for (IConnector connector : connectors) {
- if (connector.canHandle(cache.geocode)) {
+ if (connector.canHandle(geocode)) {
return connector;
}
}
diff --git a/main/src/cgeo/geocaching/connector/GCConnector.java b/main/src/cgeo/geocaching/connector/GCConnector.java
index c7db821..2eea539 100644
--- a/main/src/cgeo/geocaching/connector/GCConnector.java
+++ b/main/src/cgeo/geocaching/connector/GCConnector.java
@@ -1,14 +1,27 @@
package cgeo.geocaching.connector;
+import cgeo.geocaching.cgBase;
import cgeo.geocaching.cgCache;
+import cgeo.geocaching.cgCacheWrap;
+import cgeo.geocaching.cgSearch;
+import cgeo.geocaching.cgSettings;
+import cgeo.geocaching.cgeoapplication;
import org.apache.commons.lang3.StringUtils;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
public class GCConnector extends AbstractConnector implements IConnector {
@Override
public boolean canHandle(String geocode) {
- return StringUtils.isNotBlank(geocode) && geocode.toUpperCase().startsWith("GC");
+ return StringUtils.isNotBlank(geocode) && StringUtils.startsWithIgnoreCase(geocode, "GC");
}
@Override
@@ -30,4 +43,89 @@ public class GCConnector extends AbstractConnector implements IConnector {
public boolean supportsLogging() {
return true;
}
+
+ @Override
+ public String getName() {
+ return "GeoCaching.com";
+ }
+
+ @Override
+ public String getHost() {
+ return "www.geocaching.com";
+ }
+
+ @Override
+ public boolean supportsUserActions() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsCachesAround() {
+ return true;
+ }
+
+ @Override
+ public UUID searchByGeocode(final cgBase base, String geocode, final String guid, final cgeoapplication app, final cgSearch search, final int reason) {
+ final String host = "www.geocaching.com";
+ final String path = "/seek/cache_details.aspx";
+ final String method = "GET";
+ final Map<String, String> params = new HashMap<String, String>();
+ if (StringUtils.isNotBlank(geocode)) {
+ params.put("wp", geocode);
+ } else if (StringUtils.isNotBlank(guid)) {
+ params.put("guid", guid);
+ }
+ params.put("decrypt", "y");
+
+ String page = base.requestLogged(false, host, path, method, params, false, false, false);
+
+ if (StringUtils.isEmpty(page)) {
+ if (app.isThere(geocode, guid, true, false)) {
+ if (StringUtils.isBlank(geocode) && StringUtils.isNotBlank(guid)) {
+ Log.i(cgSettings.tag, "Loading old cache from cache.");
+
+ geocode = app.getGeocode(guid);
+ }
+
+ final List<cgCache> cacheList = new ArrayList<cgCache>();
+ cacheList.add(app.getCacheByGeocode(geocode));
+ search.addGeocode(geocode);
+ search.error = null;
+
+ app.addSearch(search, cacheList, false, reason);
+
+ cacheList.clear();
+
+ return search.getCurrentId();
+ }
+
+ Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No data from server");
+ return null;
+ }
+
+ final cgCacheWrap caches = base.parseCache(page, reason);
+ if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) {
+ if (caches != null && StringUtils.isNotBlank(caches.error)) {
+ search.error = caches.error;
+ }
+ if (caches != null && StringUtils.isNotBlank(caches.url)) {
+ search.url = caches.url;
+ }
+
+ app.addSearch(search, null, true, reason);
+
+ Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No cache parsed");
+ return null;
+ }
+
+ if (app == null) {
+ Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No application found");
+ return null;
+ }
+
+ List<cgCache> cacheList = base.processSearchResults(search, caches, 0, 0, null);
+ app.addSearch(search, cacheList, true, reason);
+
+ return search.getCurrentId();
+ }
}
diff --git a/main/src/cgeo/geocaching/connector/IConnector.java b/main/src/cgeo/geocaching/connector/IConnector.java
index 66defd6..07ae43e 100644
--- a/main/src/cgeo/geocaching/connector/IConnector.java
+++ b/main/src/cgeo/geocaching/connector/IConnector.java
@@ -1,15 +1,80 @@
package cgeo.geocaching.connector;
+import cgeo.geocaching.cgBase;
import cgeo.geocaching.cgCache;
+import cgeo.geocaching.cgSearch;
+import cgeo.geocaching.cgeoapplication;
+
+import java.util.UUID;
public interface IConnector {
+ /**
+ * get name for display (currently only used in links)
+ *
+ * @return
+ */
+ public String getName();
+
+ /**
+ * return true, if this connector is responsible for the given cache
+ *
+ * @param geocode
+ * @return
+ */
public boolean canHandle(final String geocode);
public boolean supportsRefreshCache(final cgCache cache);
+ /**
+ * get browser URL for the given cache
+ *
+ * @param cache
+ * @return
+ */
public String getCacheUrl(final cgCache cache);
+ /**
+ * enable/disable watchlist controls in cache details
+ *
+ * @return
+ */
public boolean supportsWatchList();
+ /**
+ * enable/disable logging controls in cache details
+ *
+ * @return
+ */
public boolean supportsLogging();
+
+ /**
+ * get host name of the connector server for dynamic loading of data
+ *
+ * @return
+ */
+ public String getHost();
+
+ /**
+ * get cache data license text
+ *
+ * @param cache
+ * @return
+ */
+ public String getLicenseText(final cgCache cache);
+
+ /**
+ * enable/disable user actions in cache details
+ *
+ * @return
+ */
+ public boolean supportsUserActions();
+
+ /**
+ * enable/disable "caches around" action in cache details
+ *
+ * @return
+ */
+ public boolean supportsCachesAround();
+
+ public UUID searchByGeocode(final cgBase base, final String geocode, final String guid, final cgeoapplication app, final cgSearch search, final int reason);
}
diff --git a/main/src/cgeo/geocaching/connector/OCConnector.java b/main/src/cgeo/geocaching/connector/OCConnector.java
deleted file mode 100644
index ca252ad..0000000
--- a/main/src/cgeo/geocaching/connector/OCConnector.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package cgeo.geocaching.connector;
-
-import cgeo.geocaching.cgCache;
-
-import org.apache.commons.lang3.StringUtils;
-
-/**
- * connector for OpenCaching.de (and several other country domains)
- *
- */
-public class OCConnector extends AbstractConnector implements IConnector {
- @Override
- public boolean canHandle(String geocode) {
- return StringUtils.isNotBlank(geocode) && geocode.toUpperCase().startsWith("OC");
- }
-
- @Override
- public String getCacheUrl(cgCache cache) {
- return "http://www.opencaching.de/viewcache.php?wp=" + cache.geocode;
- }
-}
diff --git a/main/src/cgeo/geocaching/connector/OXConnector.java b/main/src/cgeo/geocaching/connector/OXConnector.java
index a1203bc..ca2f4b9 100644
--- a/main/src/cgeo/geocaching/connector/OXConnector.java
+++ b/main/src/cgeo/geocaching/connector/OXConnector.java
@@ -12,7 +12,7 @@ public class OXConnector extends AbstractConnector implements IConnector {
@Override
public boolean canHandle(String geocode) {
- return StringUtils.isNotBlank(geocode) && geocode.toUpperCase().startsWith("OX");
+ return StringUtils.isNotBlank(geocode) && StringUtils.startsWithIgnoreCase(geocode, "OX");
}
@Override
@@ -20,4 +20,14 @@ public class OXConnector extends AbstractConnector implements IConnector {
return "http://www.opencaching.com/#!geocache/" + cache.geocode;
}
+ @Override
+ public String getName() {
+ return "OpenCaching.com";
+ }
+
+ @Override
+ public String getHost() {
+ return "www.opencaching.com";
+ }
+
}
diff --git a/main/src/cgeo/geocaching/connector/opencaching/ApiOpenCachingConnector.java b/main/src/cgeo/geocaching/connector/opencaching/ApiOpenCachingConnector.java
new file mode 100644
index 0000000..63c773e
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/opencaching/ApiOpenCachingConnector.java
@@ -0,0 +1,52 @@
+package cgeo.geocaching.connector.opencaching;
+
+import cgeo.geocaching.cgBase;
+import cgeo.geocaching.cgCache;
+import cgeo.geocaching.cgCacheWrap;
+import cgeo.geocaching.cgSearch;
+import cgeo.geocaching.cgeoapplication;
+import cgeo.geocaching.connector.IConnector;
+import cgeo.geocaching.utils.CryptUtils;
+
+import java.util.List;
+import java.util.UUID;
+
+public class ApiOpenCachingConnector extends OpenCachingConnector implements IConnector {
+
+ private final String cK;
+
+ public ApiOpenCachingConnector(String name, String host, String prefix, String cK) {
+ super(name, host, prefix);
+ this.cK = cK;
+ }
+
+ public String getAuthentication(int level) {
+ return CryptUtils.rot13("&pbafhzre_xrl=" + cK);
+ }
+
+ @Override
+ public String getLicenseText(final cgCache cache) {
+ // NOT TO BE TRANSLATED
+ return "<a href=\"" + getCacheUrl(cache) + "\">" + getName() + "</a> data licensed under the Creative Commons BY-SA 3.0 License";
+ }
+
+ @Override
+ public boolean supportsRefreshCache(cgCache cache) {
+ return true;
+ }
+
+ @Override
+ public UUID searchByGeocode(final cgBase base, final String geocode, final String guid, final cgeoapplication app, final cgSearch search, final int reason) {
+ final cgCache cache = OkapiClient.getCache(geocode);
+ if (cache == null) {
+ return null;
+ }
+ final cgCacheWrap caches = new cgCacheWrap();
+ caches.cacheList.add(cache);
+
+ final List<cgCache> cacheList = base.processSearchResults(search, caches, 0, 0, null);
+ app.addSearch(search, cacheList, true, reason);
+
+ return search.getCurrentId();
+ }
+}
diff --git a/main/src/cgeo/geocaching/connector/opencaching/OkapiClient.java b/main/src/cgeo/geocaching/connector/opencaching/OkapiClient.java
new file mode 100644
index 0000000..e9dcded
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/opencaching/OkapiClient.java
@@ -0,0 +1,260 @@
+package cgeo.geocaching.connector.opencaching;
+
+import cgeo.geocaching.cgBase;
+import cgeo.geocaching.cgCache;
+import cgeo.geocaching.cgImage;
+import cgeo.geocaching.cgLog;
+import cgeo.geocaching.connector.ConnectorFactory;
+import cgeo.geocaching.connector.IConnector;
+import cgeo.geocaching.enumerations.CacheSize;
+import cgeo.geocaching.geopoint.GeopointParser;
+
+import org.apache.commons.lang3.StringUtils;
+import org.json.JSONArray;
+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.Date;
+import java.util.List;
+import java.util.Locale;
+
+final public class OkapiClient {
+ private static final String CACHE_VOTES = "rating_votes";
+ private static final String CACHE_NOTFOUNDS = "notfounds";
+ private static final String CACHE_FOUNDS = "founds";
+ private static final String CACHE_HIDDEN = "date_hidden";
+ private static final String CACHE_LATEST_LOGS = "latest_logs";
+ private static final String CACHE_IMAGE_URL = "url";
+ private static final String CACHE_IMAGE_CAPTION = "caption";
+ private static final String CACHE_IMAGE_IS_SPOILER = "is_spoiler";
+ private static final String CACHE_IMAGES = "images";
+ private static final String CACHE_HINT = "hint";
+ private static final String CACHE_DESCRIPTION = "description";
+ private static final String CACHE_RECOMMENDATIONS = "recommendations";
+ private static final String CACHE_RATING = "rating";
+ private static final String CACHE_TERRAIN = "terrain";
+ private static final String CACHE_DIFFICULTY = "difficulty";
+ private static final String CACHE_OWNER = "owner";
+ private static final String CACHE_STATUS = "status";
+ private static final String CACHE_TYPE = "type";
+ 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 LOG_TYPE = "type";
+ private static final String LOG_COMMENT = "comment";
+ private static final String LOG_DATE = "date";
+ private static final String LOG_USER = "user";
+
+ private static final String USER_USERNAME = "username";
+
+ private static final String SERVICE_CACHE = "/okapi/services/caches/geocache";
+ private static final String SERVICE_CACHE_FIELDS = "fields=code|name|location|type|status|owner|founds|notfounds|size|difficulty|terrain|rating|rating_votes|recommendations|description|hint|images|latest_logs|date_hidden";
+
+ public static cgCache getCache(final String geoCode) {
+ final String params = "cache_code=" + geoCode + "&" + SERVICE_CACHE_FIELDS;
+ final String data = request(geoCode, SERVICE_CACHE, params, 1);
+
+ if (StringUtils.isBlank(data)) {
+ return null;
+ }
+
+ final cgCache cache = parseCache(data);
+
+ cache.updated = new Date().getTime();
+ cache.detailedUpdate = new Date().getTime();
+
+ return cache;
+ }
+
+ private static cgCache parseCache(final String data) {
+ final cgCache cache = new cgCache();
+ try {
+ final JSONObject response = new JSONObject(data);
+ cache.geocode = response.getString(CACHE_CODE);
+ cache.name = response.getString(CACHE_NAME);
+ // not used: names
+ setLocation(cache, response.getString(CACHE_LOCATION));
+ cache.type = getCacheType(response.getString(CACHE_TYPE));
+
+ final String status = response.getString(CACHE_STATUS);
+ cache.disabled = status.equalsIgnoreCase("Temporarily unavailable");
+ cache.archived = status.equalsIgnoreCase("Archived");
+
+ // not used: url
+ final JSONObject owner = response.getJSONObject(CACHE_OWNER);
+ cache.owner = parseUser(owner);
+
+ cache.logCounts.put(cgBase.LOG_FOUND_IT, response.getInt(CACHE_FOUNDS));
+ cache.logCounts.put(cgBase.LOG_DIDNT_FIND_IT, response.getInt(CACHE_NOTFOUNDS));
+ cache.size = getCacheSize(response);
+ cache.difficulty = (float) response.getDouble(CACHE_DIFFICULTY);
+ cache.terrain = (float) response.getDouble(CACHE_TERRAIN);
+ if (response.has(CACHE_RATING) && !isNull(response.getString(CACHE_RATING))) {
+ cache.rating = (float) response.getDouble(CACHE_RATING);
+ }
+ cache.votes = response.getInt(CACHE_VOTES);
+
+ cache.favouriteCnt = response.getInt(CACHE_RECOMMENDATIONS);
+ // not used: req_password
+ cache.description = response.getString(CACHE_DESCRIPTION);
+ cache.hint = Html.fromHtml(response.getString(CACHE_HINT)).toString();
+ // not used: hints
+
+ final JSONArray images = response.getJSONArray(CACHE_IMAGES);
+ if (images != null) {
+ JSONObject imageResponse;
+ cgImage image;
+ for (int i = 0; i < images.length(); i++) {
+ imageResponse = images.getJSONObject(i);
+ if (imageResponse.getBoolean(CACHE_IMAGE_IS_SPOILER)) {
+ image = new cgImage();
+ image.title = imageResponse.getString(CACHE_IMAGE_CAPTION);
+ image.url = absoluteUrl(imageResponse.getString(CACHE_IMAGE_URL), cache.geocode);
+ if (cache.spoilers == null) {
+ cache.spoilers = new ArrayList<cgImage>();
+ }
+ cache.spoilers.add(image);
+ }
+ }
+ }
+
+ // not used: attrnames
+ cache.logs = parseLogs(response.getJSONArray(CACHE_LATEST_LOGS));
+ cache.hidden = parseDate(response.getString(CACHE_HIDDEN));
+
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return cache;
+ }
+
+ private static String absoluteUrl(String url, String geocode) {
+ final Uri uri = Uri.parse(url);
+
+ if (!uri.isAbsolute()) {
+ final IConnector connector = ConnectorFactory.getConnector(geocode);
+ return "http://" + connector.getHost() + "/" + url;
+ }
+ return url;
+ }
+
+ private static boolean isNull(String string) {
+ return string.equalsIgnoreCase("null");
+ }
+
+ private static String parseUser(JSONObject user) throws JSONException {
+ return user.getString(USER_USERNAME);
+ }
+
+ private static List<cgLog> parseLogs(JSONArray logsJSON) {
+ List<cgLog> result = null;
+ for (int i = 0; i < logsJSON.length(); i++) {
+ try {
+ JSONObject logResponse = logsJSON.getJSONObject(i);
+ cgLog log = new cgLog();
+ log.date = parseDate(logResponse.getString(LOG_DATE)).getTime();
+ log.log = logResponse.getString(LOG_COMMENT).trim();
+ log.type = parseLogType(logResponse.getString(LOG_TYPE));
+ log.author = parseUser(logResponse.getJSONObject(LOG_USER));
+ if (result == null) {
+ result = new ArrayList<cgLog>();
+ }
+ result.add(log);
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+ return result;
+ }
+
+ private static int parseLogType(String logType) {
+ if ("Found it".equalsIgnoreCase(logType)) {
+ return cgBase.LOG_FOUND_IT;
+ }
+ else if ("Didn't find it".equalsIgnoreCase(logType)) {
+ return cgBase.LOG_DIDNT_FIND_IT;
+ }
+ return cgBase.LOG_NOTE;
+ }
+
+ 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) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private static void setLocation(final cgCache cache, final String location) {
+ final String latitude = StringUtils.substringBefore(location, "|");
+ final String longitude = StringUtils.substringAfter(location, "|");
+ // FIXME: the next lines should be a setter at cgCache
+ cache.coords = GeopointParser.parse(latitude, longitude);
+ cache.latitudeString = cgBase.formatLatitude(cache.coords.getLatitude(), true);
+ cache.longitudeString = cgBase.formatLongitude(cache.coords.getLongitude(), true);
+ }
+
+ private static CacheSize getCacheSize(final JSONObject response) {
+ double size = 0;
+ try {
+ size = response.getDouble("size");
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ switch ((int) Math.round(size)) {
+ case 1:
+ return CacheSize.MICRO;
+ case 2:
+ return CacheSize.SMALL;
+ case 3:
+ return CacheSize.REGULAR;
+ case 4:
+ return CacheSize.LARGE;
+ case 5:
+ return CacheSize.LARGE;
+ default:
+ break;
+ }
+ return CacheSize.NOT_CHOSEN;
+ }
+
+ private static String getCacheType(final String type) {
+ if (type.equalsIgnoreCase("Traditional")) {
+ return "traditional";
+ } else if (type.equalsIgnoreCase("Multi")) {
+ return "multi";
+ } else if (type.equalsIgnoreCase("Quiz")) {
+ return "mystery";
+ } else if (type.equalsIgnoreCase("Virtual")) {
+ return "virtual";
+ }
+ return "other";
+ }
+
+ private static String request(final String geoCode, final String service, final String params, final int level) {
+ final IConnector connector = ConnectorFactory.getConnector(geoCode);
+ if (connector == null) {
+ return null;
+ }
+ if (!(connector instanceof ApiOpenCachingConnector)) {
+ return null;
+ }
+
+ return cgBase.requestJSON(connector.getHost(), service, params + ((ApiOpenCachingConnector) connector).getAuthentication(level));
+ }
+}
diff --git a/main/src/cgeo/geocaching/connector/opencaching/OpenCachingConnector.java b/main/src/cgeo/geocaching/connector/opencaching/OpenCachingConnector.java
new file mode 100644
index 0000000..0fe69f5
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/opencaching/OpenCachingConnector.java
@@ -0,0 +1,41 @@
+package cgeo.geocaching.connector.opencaching;
+
+import cgeo.geocaching.cgCache;
+import cgeo.geocaching.connector.AbstractConnector;
+import cgeo.geocaching.connector.IConnector;
+
+import org.apache.commons.lang3.StringUtils;
+
+public class OpenCachingConnector extends AbstractConnector implements IConnector {
+
+ private final String host;
+ private final String name;
+ private final String prefix;
+
+ public OpenCachingConnector(final String name, final String host, final String prefix) {
+ this.name = name;
+ this.host = host;
+ this.prefix = prefix;
+ }
+
+ @Override
+ public boolean canHandle(String geocode) {
+ return StringUtils.isNotBlank(geocode) && StringUtils.startsWithIgnoreCase(geocode, prefix);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getCacheUrl(cgCache cache) {
+ return "http://" + host + "/viewcache.php?wp=" + cache.geocode;
+ }
+
+ @Override
+ public String getHost() {
+ return host;
+ }
+
+}