diff options
Diffstat (limited to 'main/src')
18 files changed, 884 insertions, 29 deletions
diff --git a/main/src/cgeo/geocaching/Settings.java b/main/src/cgeo/geocaching/Settings.java index a766470..5898bf7 100644 --- a/main/src/cgeo/geocaching/Settings.java +++ b/main/src/cgeo/geocaching/Settings.java @@ -109,6 +109,8 @@ public final class Settings { private static final String KEY_PLAIN_LOGS = "plainLogs"; private static final String KEY_NATIVE_UA = "nativeUa"; private static final String KEY_MAP_DIRECTORY = "mapDirectory"; + private static final String KEY_CONNECTOR_OC_ACTIVE = "connectorOCActive"; + private static final String KEY_CONNECTOR_OC_USER = "connectorOCUser"; private final static int unitsMetric = 1; @@ -305,6 +307,42 @@ public final class Settings { }); } + public static boolean isOCConnectorActive() { + return sharedPrefs.getBoolean(KEY_CONNECTOR_OC_ACTIVE, false); + } + + public static boolean setOCConnectorActive(final boolean isActive) { + return editSharedSettings(new PrefRunnable() { + + @Override + public void edit(Editor edit) { + edit.putBoolean(KEY_CONNECTOR_OC_ACTIVE, isActive); + } + }); + } + + public static String getOCConnectorUserName() { + String ocConnectorUser = sharedPrefs.getString(KEY_CONNECTOR_OC_USER, null); + if (StringUtils.isBlank(ocConnectorUser)) { + return StringUtils.EMPTY; + } + return ocConnectorUser; + } + + public static boolean setOCConnectorUserName(final String userName) { + return editSharedSettings(new PrefRunnable() { + + @Override + public void edit(Editor edit) { + if (StringUtils.isBlank(userName)) { + edit.remove(KEY_CONNECTOR_OC_USER); + } else { + edit.putString(KEY_CONNECTOR_OC_USER, userName); + } + } + }); + } + public static boolean isGCvoteLogin() { final String preUsername = sharedPrefs.getString(KEY_USERNAME, null); final String prePassword = sharedPrefs.getString(KEY_GCVOTE_PASSWORD, null); diff --git a/main/src/cgeo/geocaching/SettingsActivity.java b/main/src/cgeo/geocaching/SettingsActivity.java index 199e083..0a41942 100644 --- a/main/src/cgeo/geocaching/SettingsActivity.java +++ b/main/src/cgeo/geocaching/SettingsActivity.java @@ -242,6 +242,21 @@ public class SettingsActivity extends AbstractActivity { } }); + // opencaching.de settings + final CheckBox ocCheck = (CheckBox) findViewById(R.id.oc_option); + ocCheck.setChecked(Settings.isOCConnectorActive()); + ocCheck.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + Settings.setOCConnectorActive(ocCheck.isChecked()); + } + }); + EditText ocUserEdit = (EditText) findViewById(R.id.oc_username); + if (ocUserEdit.getText().length() == 0) { + ocUserEdit.setText(Settings.getOCConnectorUserName()); + } + // gcvote settings final ImmutablePair<String, String> gcvoteLogin = Settings.getGCvoteLogin(); if (null != gcvoteLogin && null != gcvoteLogin.right) { @@ -813,6 +828,7 @@ public class SettingsActivity extends AbstractActivity { String signatureNew = ((EditText) findViewById(R.id.signature)).getText().toString(); String mapDirectoryNew = StringUtils.trimToEmpty(((EditText) findViewById(R.id.map_directory)).getText().toString()); String themesDirectoryNew = StringUtils.trimToEmpty(((EditText) findViewById(R.id.themefolder)).getText().toString()); + String ocUserName = StringUtils.trimToEmpty(((EditText) findViewById(R.id.oc_username)).getText().toString()); String altitudeNew = StringUtils.trimToNull(((EditText) findViewById(R.id.altitude)).getText().toString()); int altitudeNewInt = parseNumber(altitudeNew, 0); @@ -826,6 +842,7 @@ public class SettingsActivity extends AbstractActivity { final boolean status4 = Settings.setAltCorrection(altitudeNewInt); final boolean status5 = Settings.setMapFileDirectory(mapDirectoryNew); final boolean status6 = Settings.setCustomRenderThemeBaseFolder(themesDirectoryNew); + final boolean status7 = Settings.setOCConnectorUserName(ocUserName); Settings.setShowWaypointsThreshold(waypointThreshold); String importNew = StringUtils.trimToEmpty(((EditText) findViewById(R.id.gpx_importdir)).getText().toString()); @@ -833,7 +850,7 @@ public class SettingsActivity extends AbstractActivity { Settings.setGpxImportDir(importNew); Settings.setGpxExportDir(exportNew); - return status1 && status2 && status3 && status4 && status5 && status6; + return status1 && status2 && status3 && status4 && status5 && status6 && status7; } /** diff --git a/main/src/cgeo/geocaching/cgCache.java b/main/src/cgeo/geocaching/cgCache.java index 7fbbf0c..b486142 100644 --- a/main/src/cgeo/geocaching/cgCache.java +++ b/main/src/cgeo/geocaching/cgCache.java @@ -11,6 +11,7 @@ import cgeo.geocaching.connector.gc.GCConnector; import cgeo.geocaching.connector.gc.GCConstants; import cgeo.geocaching.connector.gc.Tile; import cgeo.geocaching.enumerations.CacheAttribute; +import cgeo.geocaching.enumerations.CacheRealm; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LoadFlags; @@ -545,6 +546,10 @@ public class cgCache implements ICache, IWaypoint { return getConnector().supportsOwnCoordinates(); } + public CacheRealm getCacheRealm() { + return getConnector().getCacheRealm(); + } + @Override public float getDifficulty() { return difficulty; diff --git a/main/src/cgeo/geocaching/cgeocaches.java b/main/src/cgeo/geocaching/cgeocaches.java index 901743c..1210dd4 100644 --- a/main/src/cgeo/geocaching/cgeocaches.java +++ b/main/src/cgeo/geocaching/cgeocaches.java @@ -7,6 +7,8 @@ import cgeo.geocaching.activity.FilteredActivity; import cgeo.geocaching.activity.Progress; import cgeo.geocaching.apps.cache.navi.NavigationAppFactory; import cgeo.geocaching.apps.cachelist.CacheListAppFactory; +import cgeo.geocaching.connector.ConnectorFactory; +import cgeo.geocaching.connector.capability.ISearchByCenter; import cgeo.geocaching.connector.gc.AbstractSearchThread; import cgeo.geocaching.connector.gc.GCParser; import cgeo.geocaching.connector.gc.SearchHandler; @@ -1376,7 +1378,17 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity @Override public void runSearch() { + search = GCParser.searchByCoords(coords, Settings.getCacheType(), Settings.isShowCaptcha()); + + for (ISearchByCenter centerConn : ConnectorFactory.getSearchByCenterConnectors()) { + if (centerConn.isActivated()) { + SearchResult temp = centerConn.searchByCenter(coords); + if (temp != null) { + search.addGeocodes(temp.getGeocodes()); + } + } + } replaceCacheListFromSearch(); } } diff --git a/main/src/cgeo/geocaching/connector/AbstractConnector.java b/main/src/cgeo/geocaching/connector/AbstractConnector.java index 0308ae7..9604b5f 100644 --- a/main/src/cgeo/geocaching/connector/AbstractConnector.java +++ b/main/src/cgeo/geocaching/connector/AbstractConnector.java @@ -1,9 +1,8 @@ package cgeo.geocaching.connector; -import cgeo.geocaching.SearchResult; import cgeo.geocaching.cgCache; +import cgeo.geocaching.enumerations.CacheRealm; import cgeo.geocaching.geopoint.Geopoint; -import cgeo.geocaching.geopoint.Viewport; import org.apache.commons.lang3.StringUtils; @@ -26,7 +25,7 @@ public abstract class AbstractConnector implements IConnector { /** * Uploading modified coordinates to website - * + * * @param cache * @param wpt * @return success @@ -64,11 +63,6 @@ public abstract class AbstractConnector implements IConnector { return false; } - @Override - public SearchResult searchByViewport(Viewport viewport, String[] tokens) { - return null; - } - protected static boolean isNumericId(final String string) { try { return Integer.parseInt(string) > 0; @@ -107,4 +101,20 @@ public abstract class AbstractConnector implements IConnector { } abstract protected String getCacheUrlPrefix(); + + /** + * {@link IConnector} + */ + @Override + public CacheRealm getCacheRealm() { + return CacheRealm.OTHER; + } + + /** + * {@link IConnector} + */ + @Override + public boolean isActivated() { + return false; + } } diff --git a/main/src/cgeo/geocaching/connector/ConnectorFactory.java b/main/src/cgeo/geocaching/connector/ConnectorFactory.java index bc4dcc0..4802c3e 100644 --- a/main/src/cgeo/geocaching/connector/ConnectorFactory.java +++ b/main/src/cgeo/geocaching/connector/ConnectorFactory.java @@ -3,25 +3,31 @@ package cgeo.geocaching.connector; import cgeo.geocaching.ICache; import cgeo.geocaching.SearchResult; import cgeo.geocaching.cgTrackable; +import cgeo.geocaching.connector.capability.ISearchByCenter; +import cgeo.geocaching.connector.capability.ISearchByViewPort; import cgeo.geocaching.connector.gc.GCConnector; import cgeo.geocaching.connector.oc.OCApiConnector; import cgeo.geocaching.connector.oc.OCConnector; +import cgeo.geocaching.connector.oc.OCXMLApiConnector; import cgeo.geocaching.connector.ox.OXConnector; import cgeo.geocaching.geopoint.Viewport; import org.apache.commons.lang3.StringUtils; +import java.util.ArrayList; +import java.util.List; + public final class ConnectorFactory { private static final UnknownConnector UNKNOWN_CONNECTOR = new UnknownConnector(); private static final IConnector[] connectors = new IConnector[] { GCConnector.getInstance(), - new OCConnector("OpenCaching.DE", "www.opencaching.de", "OC"), + new OCXMLApiConnector("OpenCaching.DE", "www.opencaching.de", "OC"), new OCConnector("OpenCaching.CZ", "www.opencaching.cz", "OZ"), new OCApiConnector("OpenCaching.CO.UK", "www.opencaching.org.uk", "OK", "arU4okouc4GEjMniE2fq"), new OCConnector("OpenCaching.ES", "www.opencachingspain.es", "OC"), new OCConnector("OpenCaching.IT", "www.opencaching.it", "OC"), new OCConnector("OpenCaching.JP", "www.opencaching.jp", "OJ"), - new OCConnector("OpenCaching.NO/SE", "www.opencaching.no", "OS"), + new OCConnector("OpenCaching.NO/SE", "www.opencaching.se", "OS"), new OCApiConnector("OpenCaching.NL", "www.opencaching.nl", "OB", "PdzU8jzIlcfMADXaYN8j"), new OCApiConnector("OpenCaching.PL", "www.opencaching.pl", "OP", "GkxM47WkUkLQXXsZ9qSh"), new OCApiConnector("OpenCaching.US", "www.opencaching.us", "OU", "pTsYAYSXFcfcRQnYE6uA"), @@ -31,10 +37,37 @@ public final class ConnectorFactory { UNKNOWN_CONNECTOR // the unknown connector MUST be the last one }; + private static final ISearchByViewPort[] searchByViewPortConns; + + private static final ISearchByCenter[] searchByCenterConns; + + static { + List<ISearchByViewPort> vpConns = new ArrayList<ISearchByViewPort>(); + for (IConnector conn : connectors) { + if (conn instanceof ISearchByViewPort) { + vpConns.add((ISearchByViewPort) conn); + } + } + searchByViewPortConns = vpConns.toArray(new ISearchByViewPort[] {}); + + List<ISearchByCenter> centerConns = new ArrayList<ISearchByCenter>(); + for (IConnector conn : connectors) { + // GCConnector is handled specially, omit it here! + if (conn instanceof ISearchByCenter && !(conn instanceof GCConnector)) { + centerConns.add((ISearchByCenter) conn); + } + } + searchByCenterConns = centerConns.toArray(new ISearchByCenter[] {}); + } + public static IConnector[] getConnectors() { return connectors; } + public static ISearchByCenter[] getSearchByCenterConnectors() { + return searchByCenterConns; + } + public static boolean canHandle(final String geocode) { if (isInvalidGeocode(geocode)) { return false; @@ -74,11 +107,19 @@ public final class ConnectorFactory { return StringUtils.isBlank(geocode) || !Character.isLetterOrDigit(geocode.charAt(0)); } - /** @see IConnector#searchByViewport */ + /** @see ISearchByViewPort#searchByViewport */ public static SearchResult searchByViewport(final Viewport viewport, final String[] tokens) { - // We have only connector capable of doing a 'searchByViewport()' - // If there is a second connector the information has to be collected from all collectors - return GCConnector.getInstance().searchByViewport(viewport, tokens); + + SearchResult result = new SearchResult(); + for (ISearchByViewPort vpconn : searchByViewPortConns) { + if (vpconn.isActivated()) { + SearchResult temp = vpconn.searchByViewport(viewport, tokens); + if (temp != null) { + result.addGeocodes(temp.getGeocodes()); + } + } + } + return result; } public static String getGeocodeFromURL(final String url) { diff --git a/main/src/cgeo/geocaching/connector/IConnector.java b/main/src/cgeo/geocaching/connector/IConnector.java index 69cc7d1..2944bed 100644 --- a/main/src/cgeo/geocaching/connector/IConnector.java +++ b/main/src/cgeo/geocaching/connector/IConnector.java @@ -1,9 +1,8 @@ package cgeo.geocaching.connector; -import cgeo.geocaching.SearchResult; import cgeo.geocaching.cgCache; +import cgeo.geocaching.enumerations.CacheRealm; import cgeo.geocaching.geopoint.Geopoint; -import cgeo.geocaching.geopoint.Viewport; public interface IConnector { /** @@ -73,15 +72,6 @@ public interface IConnector { public boolean supportsUserActions(); /** - * Search caches by viewport. - * - * @param viewport - * @param tokens - * @return - */ - public SearchResult searchByViewport(final Viewport viewport, final String[] tokens); - - /** * return true if this is a ZIP file containing a GPX file * * @param fileName @@ -115,7 +105,7 @@ public interface IConnector { /** * enable/disable uploading modified coordinates to website - * + * * @return true, when uploading is possible */ public boolean supportsOwnCoordinates(); @@ -137,4 +127,18 @@ public interface IConnector { */ public boolean deleteModifiedCoordinates(cgCache cache); + /** + * The CacheRealm this cache belongs to + * + * @return + */ + public CacheRealm getCacheRealm(); + + /** + * Return true if this connector is activated for online + * interaction (download details, do searches, ...) + * + * @return + */ + public boolean isActivated(); } diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java b/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java index 62645c2..3fdd61f 100644 --- a/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java @@ -10,4 +10,5 @@ import cgeo.geocaching.geopoint.Geopoint; public interface ISearchByCenter { public SearchResult searchByCenter(final Geopoint center); + public boolean isActivated(); } diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java b/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java index 316cf00..f1bd2ce 100644 --- a/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java @@ -5,4 +5,6 @@ import cgeo.geocaching.geopoint.Viewport; public interface ISearchByViewPort { public SearchResult searchByViewport(final Viewport viewport, final String[] tokens); + + public boolean isActivated(); } diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java index b196504..8943835 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java @@ -7,6 +7,8 @@ import cgeo.geocaching.cgData; import cgeo.geocaching.connector.AbstractConnector; import cgeo.geocaching.connector.capability.ISearchByCenter; import cgeo.geocaching.connector.capability.ISearchByGeocode; +import cgeo.geocaching.connector.capability.ISearchByViewPort; +import cgeo.geocaching.enumerations.CacheRealm; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; @@ -18,7 +20,7 @@ import org.apache.commons.lang3.StringUtils; import java.util.regex.Pattern; -public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter { +public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort { private static final String HTTP_COORD_INFO = "http://coord.info/"; private static final Pattern gpxZipFilePattern = Pattern.compile("\\d{7,}(_.+)?\\.zip", Pattern.CASE_INSENSITIVE); @@ -216,4 +218,14 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, protected String getCacheUrlPrefix() { return HTTP_COORD_INFO; } + + @Override + public CacheRealm getCacheRealm() { + return CacheRealm.GC; + } + + @Override + public boolean isActivated() { + return true; + } } diff --git a/main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java b/main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java new file mode 100644 index 0000000..d0c0e16 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java @@ -0,0 +1,514 @@ +package cgeo.geocaching.connector.oc; + +import cgeo.geocaching.LogEntry; +import cgeo.geocaching.Settings; +import cgeo.geocaching.cgCache; +import cgeo.geocaching.enumerations.CacheSize; +import cgeo.geocaching.enumerations.CacheType; +import cgeo.geocaching.enumerations.LogType; +import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.utils.Log; + +import org.apache.commons.lang3.StringUtils; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; + +import android.sax.Element; +import android.sax.EndElementListener; +import android.sax.EndTextElementListener; +import android.sax.RootElement; +import android.sax.StartElementListener; +import android.util.Xml; + +import java.io.IOException; +import java.io.InputStream; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; +import java.util.regex.Pattern; + +public class OC11XMLParser { + + private static Pattern STRIP_DATE = Pattern.compile("\\+0([0-9]){1}\\:00"); + + private static class CacheHolder { + public cgCache cache; + public String latitude; + public String longitude; + } + + private static class CacheLog { + public String cacheId; + public LogEntry logEntry; + } + + private static class CacheDescription { + public String cacheId; + public String shortDesc; + public String desc; + public String hint; + } + + private static Date parseFullDate(final String date) { + final SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US); + ISO8601DATEFORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); + final String strippedDate = STRIP_DATE.matcher(date).replaceAll("+0$100"); + try { + return ISO8601DATEFORMAT.parse(strippedDate); + } catch (ParseException e) { + Log.e("OC11XMLParser.parseFullDate", e); + } + return null; + } + + private static Date parseDayDate(final String date) { + final SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd", Locale.US); + ISO8601DATEFORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); + final String strippedDate = STRIP_DATE.matcher(date).replaceAll("+0$100"); + try { + return ISO8601DATEFORMAT.parse(strippedDate); + } catch (ParseException e) { + Log.e("OC11XMLParser.parseDayDate", e); + } + return null; + } + + private static CacheSize getCacheSize(final String sizeId) { + int size = Integer.parseInt(sizeId); + + switch (size) { + case 1: + return CacheSize.OTHER; + case 2: + return CacheSize.MICRO; + case 3: + return CacheSize.SMALL; + case 4: + return CacheSize.REGULAR; + case 5: + case 6: + return CacheSize.LARGE; + case 8: + return CacheSize.VIRTUAL; + default: + break; + } + return CacheSize.NOT_CHOSEN; + } + + private static CacheType getCacheType(final String typeId) { + int type = Integer.parseInt(typeId); + switch (type) { + case 1: // Other/unbekannter Cachetyp + return CacheType.UNKNOWN; + case 2: // Trad./normaler Cache + return CacheType.TRADITIONAL; + case 3: // Multi/Multicache + return CacheType.MULTI; + case 4: // Virt./virtueller Cache + return CacheType.VIRTUAL; + case 5: // ICam./Webcam-Cache + return CacheType.WEBCAM; + case 6: // Event/Event-Cache + return CacheType.EVENT; + case 7: // Quiz/Rätselcache + return CacheType.MYSTERY; + case 8: // Math/Mathe-/Physikcache + return CacheType.MYSTERY; + case 9: // Moving/beweglicher Cache + return CacheType.UNKNOWN; + case 10: // Driv./Drive-In + return CacheType.TRADITIONAL; + } + return CacheType.UNKNOWN; + } + + private static LogType getLogType(final int typeId) { + switch (typeId) { + case 1: + return LogType.FOUND_IT; + case 2: + return LogType.DIDNT_FIND_IT; + case 3: + return LogType.NOTE; + case 7: + return LogType.ATTENDED; + case 8: + return LogType.WILL_ATTEND; + } + return LogType.UNKNOWN; + } + + private static void setCacheStatus(final int statusId, final cgCache cache) { + + switch (statusId) { + case 1: + cache.setArchived(false); + cache.setDisabled(false); + break; + case 2: + cache.setArchived(false); + cache.setDisabled(true); + break; + default: + cache.setArchived(true); + cache.setDisabled(false); + break; + } + } + + private static void resetCache(final CacheHolder cacheHolder) { + cacheHolder.cache = new cgCache(null); + cacheHolder.cache.setReliableLatLon(true); + cacheHolder.cache.setDescription(StringUtils.EMPTY); + cacheHolder.latitude = "0.0"; + cacheHolder.longitude = "0.0"; + } + + private static void resetLog(final CacheLog log) { + log.cacheId = StringUtils.EMPTY; + log.logEntry = new LogEntry("", 0, LogType.UNKNOWN, ""); + } + + private static void resetDesc(final CacheDescription desc) { + desc.cacheId = StringUtils.EMPTY; + desc.shortDesc = StringUtils.EMPTY; + desc.desc = StringUtils.EMPTY; + desc.hint = StringUtils.EMPTY; + } + + public static Collection<cgCache> parseCaches(final InputStream stream) throws IOException { + + final int CACHE_PARSE_LIMIT = 250; + + final Map<String, cgCache> caches = new HashMap<String, cgCache>(); + + final CacheHolder cacheHolder = new CacheHolder(); + final CacheLog logHolder = new CacheLog(); + final CacheDescription descHolder = new CacheDescription(); + + final RootElement root = new RootElement("oc11xml"); + final Element cacheNode = root.getChild("cache"); + + // cache + cacheNode.setStartElementListener(new StartElementListener() { + + @Override + public void start(Attributes attributes) { + resetCache(cacheHolder); + } + + }); + + cacheNode.setEndElementListener(new EndElementListener() { + + @Override + public void end() { + cgCache cache = cacheHolder.cache; + Geopoint coords = new Geopoint(cacheHolder.latitude, cacheHolder.longitude); + if (StringUtils.isNotBlank(cache.getGeocode()) + && !coords.equals(Geopoint.ZERO) + && !cache.isArchived() + && caches.size() < CACHE_PARSE_LIMIT) { + cache.setCoords(coords); + caches.put(cache.getCacheId(), cache); + } + } + }); + + // cache.id + cacheNode.getChild("id").setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String body) { + cacheHolder.cache.setCacheId(body); + } + }); + + // cache.longitude + cacheNode.getChild("longitude").setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String body) { + String longitude = body.trim(); + if (StringUtils.isNotBlank(longitude)) { + cacheHolder.longitude = longitude; + } + } + }); + + // cache.latitude + cacheNode.getChild("latitude").setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String body) { + String latitude = body.trim(); + if (StringUtils.isNotBlank(latitude)) { + cacheHolder.latitude = latitude; + } + } + }); + + // cache.name + cacheNode.getChild("name").setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String body) { + final String content = body.trim(); + cacheHolder.cache.setName(content); + } + }); + + // cache.waypoints[oc] + cacheNode.getChild("waypoints").setStartElementListener(new StartElementListener() { + + @Override + public void start(Attributes attrs) { + if (attrs.getIndex("oc") > -1) { + cacheHolder.cache.setGeocode(attrs.getValue("oc")); + } + if (attrs.getIndex("gccom") > -1) { + String gccode = attrs.getValue("gccom"); + if (!StringUtils.isBlank(gccode)) { + cacheHolder.cache.setDescription(String.format("Listed on geocaching com: <a href=\"http://coord.info/%s\">%s</a><br /><br />", gccode, gccode)); + } + } + } + }); + + // cache.type[id] + cacheNode.getChild("type").setStartElementListener(new StartElementListener() { + + @Override + public void start(Attributes attrs) { + if (attrs.getIndex("id") > -1) { + final String typeId = attrs.getValue("id"); + cacheHolder.cache.setType(getCacheType(typeId)); + } + } + }); + + // cache.status[id] + cacheNode.getChild("status").setStartElementListener(new StartElementListener() { + + @Override + public void start(Attributes attrs) { + if (attrs.getIndex("id") > -1) { + try { + final int statusId = Integer.parseInt(attrs.getValue("id")); + setCacheStatus(statusId, cacheHolder.cache); + } catch (NumberFormatException e) { + Log.w(String.format("Failed to parse status of cache '%s'.", cacheHolder.cache.getGeocode())); + } + } + } + }); + + // cache.size[id] + cacheNode.getChild("size").setStartElementListener(new StartElementListener() { + + @Override + public void start(Attributes attrs) { + if (attrs.getIndex("id") > -1) { + final String typeId = attrs.getValue("id"); + cacheHolder.cache.setSize(getCacheSize(typeId)); + } + } + }); + + // cache.difficulty + cacheNode.getChild("difficulty").setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String body) { + final String content = body.trim(); + cacheHolder.cache.setDifficulty(Float.valueOf(content)); + } + }); + + // cache.terrain + cacheNode.getChild("terrain").setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String body) { + final String content = body.trim(); + cacheHolder.cache.setTerrain(Float.valueOf(content)); + } + }); + + // cache.terrain + cacheNode.getChild("datehidden").setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String body) { + final String content = body.trim(); + cacheHolder.cache.setHidden(parseFullDate(content)); + } + }); + + // cache.attributes.attribute + cacheNode.getChild("attributes").getChild("attribute").setEndTextElementListener(new EndTextElementListener() { + @Override + public void end(String body) { + if (StringUtils.isNotBlank(body)) { + cacheHolder.cache.getAttributes().add(body.trim()); + } + } + }); + + // cachedesc + final Element cacheDesc = root.getChild("cachedesc"); + + cacheDesc.setStartElementListener(new StartElementListener() { + + @Override + public void start(Attributes attributes) { + resetDesc(descHolder); + } + }); + + cacheDesc.setEndElementListener(new EndElementListener() { + + @Override + public void end() { + final cgCache cache = caches.get(descHolder.cacheId); + if (cache != null) { + cache.setShortdesc(descHolder.shortDesc); + cache.setDescription(cache.getDescription() + descHolder.desc); + cache.setHint(descHolder.hint); + } + } + }); + + // cachedesc.cacheid + cacheDesc.getChild("cacheid").setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String body) { + descHolder.cacheId = body; + } + }); + + // cachedesc.desc + cacheDesc.getChild("shortdesc").setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String body) { + final String content = body.trim(); + descHolder.shortDesc = content; + } + }); + + // cachedesc.desc + cacheDesc.getChild("desc").setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String body) { + final String content = body.trim(); + descHolder.desc = content; + } + }); + + // cachedesc.hint + cacheDesc.getChild("hint").setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String body) { + final String content = body.trim(); + descHolder.hint = content; + } + }); + + // cachelog + final Element cacheLog = root.getChild("cachelog"); + + cacheLog.setStartElementListener(new StartElementListener() { + + @Override + public void start(Attributes attrs) { + resetLog(logHolder); + } + }); + + cacheLog.setEndElementListener(new EndElementListener() { + + @Override + public void end() { + final cgCache cache = caches.get(logHolder.cacheId); + if (cache != null && logHolder.logEntry.type != LogType.UNKNOWN) { + cache.getLogs().prepend(logHolder.logEntry); + if (logHolder.logEntry.type == LogType.FOUND_IT + && StringUtils.equals(logHolder.logEntry.author, Settings.getOCConnectorUserName())) { + cache.setFound(true); + cache.setVisitedDate(logHolder.logEntry.date); + } + } + } + }); + + // cachelog.cacheid + cacheLog.getChild("cacheid").setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String body) { + logHolder.cacheId = body; + } + }); + + // cachelog.date + cacheLog.getChild("date").setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String body) { + try { + logHolder.logEntry.date = parseDayDate(body).getTime(); + } catch (Exception e) { + Log.w("Failed to parse log date: " + e.toString()); + } + } + }); + + // cachelog.logtype + cacheLog.getChild("logtype").setStartElementListener(new StartElementListener() { + + @Override + public void start(Attributes attrs) { + if (attrs.getIndex("id") > -1) { + final int typeId = Integer.parseInt(attrs.getValue("id")); + logHolder.logEntry.type = getLogType(typeId); + } + } + }); + + // cachelog.userid + cacheLog.getChild("userid").setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String finderName) { + logHolder.logEntry.author = finderName; + } + }); + + // cachelog.text + cacheLog.getChild("text").setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String logText) { + logHolder.logEntry.log = logText; + } + }); + + try { + Xml.parse(stream, Xml.Encoding.UTF_8, root.getContentHandler()); + return caches.values(); + } catch (SAXException e) { + Log.e("Cannot parse .gpx file as oc11xml: could not parse XML - " + e.toString()); + return null; + } + } +} diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java index 17c961a..74968e7 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java @@ -34,4 +34,10 @@ public class OCApiConnector extends OCConnector implements ISearchByGeocode { } return new SearchResult(cache); } + + @Override + public boolean isActivated() { + // currently always active, but only for details download + return true; + } } diff --git a/main/src/cgeo/geocaching/connector/oc/OCConnector.java b/main/src/cgeo/geocaching/connector/oc/OCConnector.java index c098d12..24fd7d6 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCConnector.java @@ -2,6 +2,7 @@ package cgeo.geocaching.connector.oc; import cgeo.geocaching.cgCache; import cgeo.geocaching.connector.AbstractConnector; +import cgeo.geocaching.enumerations.CacheRealm; import java.util.regex.Pattern; @@ -51,4 +52,9 @@ public class OCConnector extends AbstractConnector { return "http://" + host + "/viewcache.php?wp="; } + @Override + public CacheRealm getCacheRealm() { + return CacheRealm.OC; + } + } diff --git a/main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java b/main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java new file mode 100644 index 0000000..3a2f42e --- /dev/null +++ b/main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java @@ -0,0 +1,52 @@ +package cgeo.geocaching.connector.oc; + +import cgeo.geocaching.SearchResult; +import cgeo.geocaching.Settings; +import cgeo.geocaching.cgCache; +import cgeo.geocaching.connector.capability.ISearchByCenter; +import cgeo.geocaching.connector.capability.ISearchByGeocode; +import cgeo.geocaching.connector.capability.ISearchByViewPort; +import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.geopoint.Viewport; +import cgeo.geocaching.utils.CancellableHandler; + +public class OCXMLApiConnector extends OCConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort { + + private final static double SEARCH_DISTANCE_LIMIT = 15.0; + private final static double NEARBY_SEARCH_DISTANCE = 5.0; + + public OCXMLApiConnector(String name, String host, String prefix) { + super(name, host, prefix); + } + + @Override + public SearchResult searchByGeocode(final String geocode, final String guid, CancellableHandler handler) { + final cgCache cache = OCXMLClient.getCache(geocode); + if (cache == null) { + return null; + } + return new SearchResult(cache); + } + + @Override + public SearchResult searchByCenter(final Geopoint center) { + return new SearchResult(OCXMLClient.getCachesAround(center, NEARBY_SEARCH_DISTANCE)); + } + + @Override + public SearchResult searchByViewport(final Viewport viewport, final String[] tokens) { + final Geopoint center = viewport.getCenter(); + double distance = center.distanceTo(viewport.bottomLeft) * 1.15; + if (distance > SEARCH_DISTANCE_LIMIT) { + distance = SEARCH_DISTANCE_LIMIT; + } + return new SearchResult(OCXMLClient.getCachesAround(center, distance)); + } + + @Override + public boolean isActivated() { + // currently only tested and working with oc.de + return Settings.isOCConnectorActive(); + } + +} diff --git a/main/src/cgeo/geocaching/connector/oc/OCXMLClient.java b/main/src/cgeo/geocaching/connector/oc/OCXMLClient.java new file mode 100644 index 0000000..26b42e3 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/oc/OCXMLClient.java @@ -0,0 +1,110 @@ +package cgeo.geocaching.connector.oc; + +import cgeo.geocaching.cgCache; +import cgeo.geocaching.cgData; +import cgeo.geocaching.connector.ConnectorFactory; +import cgeo.geocaching.connector.IConnector; +import cgeo.geocaching.enumerations.LoadFlags; +import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.geopoint.GeopointFormatter; +import cgeo.geocaching.network.Network; +import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.utils.Log; + +import ch.boye.httpclientandroidlib.HttpResponse; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; +import java.util.Locale; +import java.util.zip.GZIPInputStream; + +public class OCXMLClient { + + private static final String SERVICE_CACHE = "/xml/ocxml11.php"; + + // Url for single cache requests + // http://www.opencaching.de/xml/ocxml11.php?modifiedsince=20060320000000&user=0&cache=1&cachedesc=1&cachelog=1&picture=1&removedobject=0&session=0&doctype=0&charset=utf-8&wp=OCC9BE + + public static cgCache getCache(final String geoCode) { + try { + final Parameters params = getOCXmlQueryParameters(true, true); + params.put("wp", geoCode); + final InputStream data = request(ConnectorFactory.getConnector(geoCode), SERVICE_CACHE, params); + + if (data == null) { + return null; + } + + Collection<cgCache> caches = OC11XMLParser.parseCaches(new GZIPInputStream(data)); + if (caches.iterator().hasNext()) { + cgCache cache = caches.iterator().next(); + cache.setDetailed(true); + cgData.saveCache(cache, LoadFlags.SAVE_ALL); + return cache; + } + return null; + } catch (IOException e) { + Log.e("Error parsing cache '" + geoCode + "': " + e.toString()); + return null; + } + } + + @SuppressWarnings("unchecked") + public static Collection<cgCache> getCachesAround(final Geopoint center, final double distance) { + try { + final Parameters params = getOCXmlQueryParameters(false, false); + params.put("lat", GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, center)); + params.put("lon", GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, center)); + params.put("distance", String.format(Locale.US, "%f", distance)); + final InputStream data = request(ConnectorFactory.getConnector("OCXXX"), SERVICE_CACHE, params); + + if (data == null) { + return CollectionUtils.EMPTY_COLLECTION; + } + + return OC11XMLParser.parseCaches(new GZIPInputStream(data)); + } catch (IOException e) { + Log.e("Error parsing nearby search result: " + e.toString()); + return CollectionUtils.EMPTY_COLLECTION; + } + } + + private static InputStream request(final IConnector connector, final String service, final Parameters params) { + if (connector == null) { + return null; + } + if (!(connector instanceof OCXMLApiConnector)) { + return null; + } + + final String host = connector.getHost(); + if (StringUtils.isBlank(host)) { + return null; + } + + final String uri = "http://" + host + service; + HttpResponse resp = Network.getRequest(uri, params); + if (resp != null) { + try { + return resp.getEntity().getContent(); + } catch (IllegalStateException e) { + // fall through and return null + } catch (IOException e) { + // fall through and return null + } + } + return null; + } + + private static Parameters getOCXmlQueryParameters(final boolean withDescription, final boolean withLogs) { + final Parameters params = new Parameters("modifiedsince", "20060320000000", + "user", "0", "cache", "1", "cachedesc", withDescription ? "1" : "0", + "cachelog", withLogs ? "1" : "0", "picture", "0", "removedobject", "0", + "session", "0", "doctype", "0", "charset", "utf-8", "zip", "gzip"); + return params; + } +} diff --git a/main/src/cgeo/geocaching/enumerations/CacheRealm.java b/main/src/cgeo/geocaching/enumerations/CacheRealm.java new file mode 100644 index 0000000..5a203ab --- /dev/null +++ b/main/src/cgeo/geocaching/enumerations/CacheRealm.java @@ -0,0 +1,22 @@ +package cgeo.geocaching.enumerations; + +import cgeo.geocaching.R; + +public enum CacheRealm { + + GC("gc", "geocaching.com", R.drawable.marker, R.drawable.marker_disabled), + OC("oc", "OpenCaching Network", R.drawable.marker_oc, R.drawable.marker_disabled_oc), + OTHER("other", "Other", R.drawable.marker_other, R.drawable.marker_disabled_other); + + public final String id; + public final String name; + public final int markerId; + public final int markerDisabledId; + + CacheRealm(String id, String name, int markerId, int markerDisabledId) { + this.id = id; + this.name = name; + this.markerId = markerId; + this.markerDisabledId = markerDisabledId; + } +} diff --git a/main/src/cgeo/geocaching/geopoint/Geopoint.java b/main/src/cgeo/geocaching/geopoint/Geopoint.java index 0dfdbcb..428ebbd 100644 --- a/main/src/cgeo/geocaching/geopoint/Geopoint.java +++ b/main/src/cgeo/geocaching/geopoint/Geopoint.java @@ -23,6 +23,8 @@ public final class Geopoint implements ICoordinates, Parcelable { public static final double rad2deg = 180 / Math.PI; public static final float erad = 6371.0f; + public static final Geopoint ZERO = new Geopoint(0.0, 0.0); + private final double latitude; private final double longitude; diff --git a/main/src/cgeo/geocaching/maps/CGeoMap.java b/main/src/cgeo/geocaching/maps/CGeoMap.java index 32814c4..356f763 100644 --- a/main/src/cgeo/geocaching/maps/CGeoMap.java +++ b/main/src/cgeo/geocaching/maps/CGeoMap.java @@ -1634,6 +1634,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto .append(cache.isReliableLatLon()) .append(cache.getType().id) .append(cache.isDisabled() || cache.isArchived()) + .append(cache.getCacheRealm().id) .append(cache.isOwn()) .append(cache.isFound()) .append(cache.hasUserModifiedCoords()) @@ -1653,7 +1654,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto final ArrayList<int[]> insets = new ArrayList<int[]>(8); // background: disabled or not - final Drawable marker = getResources().getDrawable(cache.isDisabled() || cache.isArchived() ? R.drawable.marker_disabled : R.drawable.marker); + final Drawable marker = getResources().getDrawable(cache.isDisabled() || cache.isArchived() ? cache.getCacheRealm().markerDisabledId : cache.getCacheRealm().markerId); layers.add(marker); final int resolution = marker.getIntrinsicWidth() > 40 ? 1 : 0; // reliable or not |
