aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--main/res/drawable-hdpi/marker_disabled_oc.pngbin0 -> 2266 bytes
-rw-r--r--main/res/drawable-hdpi/marker_disabled_other.pngbin0 -> 2239 bytes
-rw-r--r--main/res/drawable-hdpi/marker_oc.pngbin0 -> 2406 bytes
-rw-r--r--main/res/drawable-hdpi/marker_other.pngbin0 -> 2346 bytes
-rw-r--r--main/res/drawable-mdpi/marker_disabled_oc.pngbin0 -> 2897 bytes
-rw-r--r--main/res/drawable-mdpi/marker_disabled_other.pngbin0 -> 2911 bytes
-rw-r--r--main/res/drawable-mdpi/marker_oc.pngbin0 -> 2511 bytes
-rw-r--r--main/res/drawable-mdpi/marker_other.pngbin0 -> 2468 bytes
-rw-r--r--main/res/layout/init.xml22
-rw-r--r--main/res/values/strings.xml4
-rw-r--r--main/src/cgeo/geocaching/CacheDetailActivity.java4
-rw-r--r--main/src/cgeo/geocaching/LogEntry.java4
-rw-r--r--main/src/cgeo/geocaching/Settings.java38
-rw-r--r--main/src/cgeo/geocaching/SettingsActivity.java19
-rw-r--r--main/src/cgeo/geocaching/cgCache.java17
-rw-r--r--main/src/cgeo/geocaching/cgeocaches.java14
-rw-r--r--main/src/cgeo/geocaching/connector/AbstractConnector.java26
-rw-r--r--main/src/cgeo/geocaching/connector/ConnectorFactory.java53
-rw-r--r--main/src/cgeo/geocaching/connector/IConnector.java28
-rw-r--r--main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java1
-rw-r--r--main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java2
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCConnector.java14
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCParser.java69
-rw-r--r--main/src/cgeo/geocaching/connector/gc/Login.java6
-rw-r--r--main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java5
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java514
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCApiConnector.java6
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCConnector.java6
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java52
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCXMLClient.java110
-rw-r--r--main/src/cgeo/geocaching/enumerations/CacheRealm.java22
-rw-r--r--main/src/cgeo/geocaching/files/GPXParser.java12
-rw-r--r--main/src/cgeo/geocaching/files/LocParser.java16
-rw-r--r--main/src/cgeo/geocaching/files/LocalStorage.java10
-rw-r--r--main/src/cgeo/geocaching/gcvote/GCVote.java14
-rw-r--r--main/src/cgeo/geocaching/geopoint/DistanceParser.java5
-rw-r--r--main/src/cgeo/geocaching/geopoint/Geopoint.java2
-rw-r--r--main/src/cgeo/geocaching/geopoint/GeopointParser.java5
-rw-r--r--main/src/cgeo/geocaching/maps/CGeoMap.java3
-rw-r--r--main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java10
-rw-r--r--main/src/cgeo/geocaching/utils/MatcherWrapper.java91
41 files changed, 1088 insertions, 116 deletions
diff --git a/main/res/drawable-hdpi/marker_disabled_oc.png b/main/res/drawable-hdpi/marker_disabled_oc.png
new file mode 100644
index 0000000..b759bbf
--- /dev/null
+++ b/main/res/drawable-hdpi/marker_disabled_oc.png
Binary files differ
diff --git a/main/res/drawable-hdpi/marker_disabled_other.png b/main/res/drawable-hdpi/marker_disabled_other.png
new file mode 100644
index 0000000..748c3c6
--- /dev/null
+++ b/main/res/drawable-hdpi/marker_disabled_other.png
Binary files differ
diff --git a/main/res/drawable-hdpi/marker_oc.png b/main/res/drawable-hdpi/marker_oc.png
new file mode 100644
index 0000000..96f2987
--- /dev/null
+++ b/main/res/drawable-hdpi/marker_oc.png
Binary files differ
diff --git a/main/res/drawable-hdpi/marker_other.png b/main/res/drawable-hdpi/marker_other.png
new file mode 100644
index 0000000..94e170b
--- /dev/null
+++ b/main/res/drawable-hdpi/marker_other.png
Binary files differ
diff --git a/main/res/drawable-mdpi/marker_disabled_oc.png b/main/res/drawable-mdpi/marker_disabled_oc.png
new file mode 100644
index 0000000..36f2d5b
--- /dev/null
+++ b/main/res/drawable-mdpi/marker_disabled_oc.png
Binary files differ
diff --git a/main/res/drawable-mdpi/marker_disabled_other.png b/main/res/drawable-mdpi/marker_disabled_other.png
new file mode 100644
index 0000000..52ae651
--- /dev/null
+++ b/main/res/drawable-mdpi/marker_disabled_other.png
Binary files differ
diff --git a/main/res/drawable-mdpi/marker_oc.png b/main/res/drawable-mdpi/marker_oc.png
new file mode 100644
index 0000000..03e1fe7
--- /dev/null
+++ b/main/res/drawable-mdpi/marker_oc.png
Binary files differ
diff --git a/main/res/drawable-mdpi/marker_other.png b/main/res/drawable-mdpi/marker_other.png
new file mode 100644
index 0000000..2456d82
--- /dev/null
+++ b/main/res/drawable-mdpi/marker_other.png
Binary files differ
diff --git a/main/res/layout/init.xml b/main/res/layout/init.xml
index 289e276..28e0a3c 100644
--- a/main/res/layout/init.xml
+++ b/main/res/layout/init.xml
@@ -60,6 +60,28 @@
<RelativeLayout style="@style/separator_horizontal_layout" >
<View style="@style/separator_horizontal" />
<TextView style="@style/separator_horizontal_headline"
+ android:text="@string/init_oc" />
+ </RelativeLayout>
+ <CheckBox android:id="@+id/oc_option"
+ style="@style/checkbox_full"
+ android:text="@string/init_oc_activate" />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="5dip"
+ android:padding="3dip"
+ android:textSize="14sp"
+ android:textColor="?text_color"
+ android:text="@string/init_oc_username_description" />
+ <EditText style="@style/edittext_full"
+ android:id="@+id/oc_username"
+ android:hint="@string/init_oc_username" />
+<!-- ** -->
+ <RelativeLayout style="@style/separator_horizontal_layout" >
+ <View style="@style/separator_horizontal" />
+ <TextView style="@style/separator_horizontal_headline"
android:text="@string/init_gcvote" />
</RelativeLayout>
<EditText style="@style/edittext_full"
diff --git a/main/res/values/strings.xml b/main/res/values/strings.xml
index 222a4b2..c1ae45d 100644
--- a/main/res/values/strings.xml
+++ b/main/res/values/strings.xml
@@ -344,6 +344,10 @@
<!-- init -->
<string name="init_geocaching">Geocaching.com</string>
+ <string name="init_oc">opencaching.de</string>
+ <string name="init_oc_activate">Activate opencaching.de on live-map and in searches</string>
+ <string name="init_oc_username_description">Enter your opencaching.de user name in order to allow marking your finds.</string>
+ <string name="init_oc_username">Enter your user name</string>
<string name="init_gcvote">GCvote.com</string>
<string name="init_twitter">Twitter</string>
<string name="init_username">Username</string>
diff --git a/main/src/cgeo/geocaching/CacheDetailActivity.java b/main/src/cgeo/geocaching/CacheDetailActivity.java
index a2cacf6..2b64924 100644
--- a/main/src/cgeo/geocaching/CacheDetailActivity.java
+++ b/main/src/cgeo/geocaching/CacheDetailActivity.java
@@ -34,6 +34,7 @@ import cgeo.geocaching.utils.GeoDirHandler;
import cgeo.geocaching.utils.HtmlUtils;
import cgeo.geocaching.utils.ImageHelper;
import cgeo.geocaching.utils.Log;
+import cgeo.geocaching.utils.MatcherWrapper;
import cgeo.geocaching.utils.TranslationUtils;
import cgeo.geocaching.utils.UnknownTagsHandler;
@@ -102,7 +103,6 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
@@ -1926,7 +1926,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
backcolor = color.darker_gray;
}
else {
- Matcher matcher = DARK_COLOR_PATTERN.matcher(text);
+ MatcherWrapper matcher = new MatcherWrapper(DARK_COLOR_PATTERN, text);
if (matcher.find()) {
backcolor = color.darker_gray;
}
diff --git a/main/src/cgeo/geocaching/LogEntry.java b/main/src/cgeo/geocaching/LogEntry.java
index b625bb5..1cb3de3 100644
--- a/main/src/cgeo/geocaching/LogEntry.java
+++ b/main/src/cgeo/geocaching/LogEntry.java
@@ -2,6 +2,7 @@ package cgeo.geocaching;
import cgeo.geocaching.enumerations.LogType;
import cgeo.geocaching.utils.DateUtils;
+import cgeo.geocaching.utils.MatcherWrapper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@@ -10,7 +11,6 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class LogEntry {
@@ -107,7 +107,7 @@ public final class LogEntry {
*/
public String getDisplayText() {
if (Settings.getPlainLogs()) {
- Matcher matcher = PATTERN_REMOVE_COLORS.matcher(log);
+ MatcherWrapper matcher = new MatcherWrapper(PATTERN_REMOVE_COLORS, log);
return matcher.replaceAll("");
}
return log;
diff --git a/main/src/cgeo/geocaching/Settings.java b/main/src/cgeo/geocaching/Settings.java
index e059bba..96aab88 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 cb1747a..19df7e3 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;
@@ -27,6 +28,7 @@ import cgeo.geocaching.utils.LazyInitializedList;
import cgeo.geocaching.utils.Log;
import cgeo.geocaching.utils.LogTemplateProvider;
import cgeo.geocaching.utils.LogTemplateProvider.LogContext;
+import cgeo.geocaching.utils.MatcherWrapper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@@ -47,7 +49,6 @@ import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
@@ -541,6 +542,10 @@ public class cgCache implements ICache, IWaypoint {
return getConnector().supportsOwnCoordinates();
}
+ public CacheRealm getCacheRealm() {
+ return getConnector().getCacheRealm();
+ }
+
@Override
public float getDifficulty() {
return difficulty;
@@ -751,7 +756,7 @@ public class cgCache implements ICache, IWaypoint {
@Override
public String getNameForSorting() {
if (null == nameForSorting) {
- final Matcher matcher = NUMBER_PATTERN.matcher(name);
+ final MatcherWrapper matcher = new MatcherWrapper(NUMBER_PATTERN, name);
if (matcher.find()) {
nameForSorting = name.replace(matcher.group(), StringUtils.leftPad(matcher.group(), 6, '0'));
}
@@ -1309,7 +1314,7 @@ public class cgCache implements ICache, IWaypoint {
final Pattern coordPattern = Pattern.compile("\\b[nNsS]{1}\\s*\\d"); // begin of coordinates
int count = 1;
String note = getPersonalNote();
- Matcher matcher = coordPattern.matcher(note);
+ MatcherWrapper matcher = new MatcherWrapper(coordPattern, note);
while (matcher.find()) {
try {
final Geopoint point = new Geopoint(note.substring(matcher.start()));
@@ -1326,7 +1331,7 @@ public class cgCache implements ICache, IWaypoint {
}
note = note.substring(matcher.start() + 1);
- matcher = coordPattern.matcher(note);
+ matcher = new MatcherWrapper(coordPattern, note);
}
} catch (Exception e) {
Log.e("cgCache.parseWaypointsFromNote: " + e.toString());
@@ -1568,7 +1573,7 @@ public class cgCache implements ICache, IWaypoint {
}
// 12:34
final Pattern time = Pattern.compile("\\b(\\d{1,2})\\:(\\d\\d)\\b");
- final Matcher matcher = time.matcher(getDescription());
+ final MatcherWrapper matcher = new MatcherWrapper(time, getDescription());
while (matcher.find()) {
try {
final int hours = Integer.valueOf(matcher.group(1));
@@ -1584,7 +1589,7 @@ public class cgCache implements ICache, IWaypoint {
final String hourLocalized = cgeoapplication.getInstance().getString(R.string.cache_time_full_hours);
if (StringUtils.isNotBlank(hourLocalized)) {
final Pattern fullHours = Pattern.compile("\\b(\\d{1,2})\\s+" + Pattern.quote(hourLocalized), Pattern.CASE_INSENSITIVE);
- final Matcher matcherHours = fullHours.matcher(getDescription());
+ final MatcherWrapper matcherHours = new MatcherWrapper(fullHours, getDescription());
if (matcherHours.find()) {
try {
final int hours = Integer.valueOf(matcherHours.group(1));
diff --git a/main/src/cgeo/geocaching/cgeocaches.java b/main/src/cgeo/geocaching/cgeocaches.java
index 0f9d750..452bacb 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;
@@ -301,7 +303,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity
protected void updateTitle() {
ArrayList<Integer> numbers = new ArrayList<Integer>();
- if (adapter.isFiltered()) {
+ if (adapter != null && adapter.isFiltered()) {
numbers.add(adapter.getCount());
}
if (search != null) {
@@ -1342,7 +1344,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/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java
index 5243f18..91b0ddd 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCParser.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java
@@ -31,6 +31,7 @@ import cgeo.geocaching.utils.BaseUtils;
import cgeo.geocaching.utils.CancellableHandler;
import cgeo.geocaching.utils.LazyInitializedList;
import cgeo.geocaching.utils.Log;
+import cgeo.geocaching.utils.MatcherWrapper;
import ch.boye.httpclientandroidlib.HttpResponse;
@@ -54,7 +55,6 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
-import java.util.regex.Matcher;
public abstract class GCParser {
private final static SimpleDateFormat dateTbIn1 = new SimpleDateFormat("EEEEE, dd MMMMM yyyy", Locale.ENGLISH); // Saturday, 28 March 2009
@@ -128,7 +128,7 @@ public abstract class GCParser {
}
try {
- final Matcher matcherGuidAndDisabled = GCConstants.PATTERN_SEARCH_GUIDANDDISABLED.matcher(row);
+ final MatcherWrapper matcherGuidAndDisabled = new MatcherWrapper(GCConstants.PATTERN_SEARCH_GUIDANDDISABLED, row);
while (matcherGuidAndDisabled.find()) {
if (matcherGuidAndDisabled.groupCount() > 0) {
@@ -169,7 +169,7 @@ public abstract class GCParser {
}
// cache inventory
- final Matcher matcherTbs = GCConstants.PATTERN_SEARCH_TRACKABLES.matcher(row);
+ final MatcherWrapper matcherTbs = new MatcherWrapper(GCConstants.PATTERN_SEARCH_TRACKABLES, row);
String inventoryPre = null;
while (matcherTbs.find()) {
if (matcherTbs.groupCount() > 0) {
@@ -183,7 +183,7 @@ public abstract class GCParser {
}
if (StringUtils.isNotBlank(inventoryPre)) {
- final Matcher matcherTbsInside = GCConstants.PATTERN_SEARCH_TRACKABLESINSIDE.matcher(inventoryPre);
+ final MatcherWrapper matcherTbsInside = new MatcherWrapper(GCConstants.PATTERN_SEARCH_TRACKABLESINSIDE, inventoryPre);
while (matcherTbsInside.find()) {
if (matcherTbsInside.groupCount() == 2 &&
matcherTbsInside.group(2) != null &&
@@ -500,7 +500,7 @@ public abstract class GCParser {
try {
final String attributesPre = BaseUtils.getMatch(page, GCConstants.PATTERN_ATTRIBUTES, true, null);
if (null != attributesPre) {
- final Matcher matcherAttributesInside = GCConstants.PATTERN_ATTRIBUTESINSIDE.matcher(attributesPre);
+ final MatcherWrapper matcherAttributesInside = new MatcherWrapper(GCConstants.PATTERN_ATTRIBUTESINSIDE, attributesPre);
final ArrayList<String> attributes = new ArrayList<String>();
while (matcherAttributesInside.find()) {
@@ -534,24 +534,20 @@ public abstract class GCParser {
}
CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_spoilers);
- final Matcher matcherSpoilersInside = GCConstants.PATTERN_SPOILER_IMAGE.matcher(page);
+ final MatcherWrapper matcherSpoilersInside = new MatcherWrapper(GCConstants.PATTERN_SPOILER_IMAGE, page);
while (matcherSpoilersInside.find()) {
// the original spoiler URL (include .../display/... contains a low-resolution image
// if we shorten the URL we get the original-resolution image
- //
- // Create a new string to avoid referencing the whole page though the matcher (@see BaseUtils.getMatch())
- String url = new String(matcherSpoilersInside.group(1).replace("/display", ""));
+ String url = matcherSpoilersInside.group(1).replace("/display", "");
String title = null;
if (matcherSpoilersInside.group(3) != null) {
- // Create a new string to avoid referencing the whole page though the matcher (@see BaseUtils.getMatch())
- title = new String(matcherSpoilersInside.group(3));
+ title = matcherSpoilersInside.group(3);
}
String description = null;
if (matcherSpoilersInside.group(4) != null) {
- // Create a new string to avoid referencing the whole page though the matcher (@see BaseUtils.getMatch())
- description = new String(matcherSpoilersInside.group(4));
+ description = matcherSpoilersInside.group(4);
}
cache.addSpoiler(new cgImage(url, title, description));
}
@@ -564,7 +560,7 @@ public abstract class GCParser {
try {
cache.setInventoryItems(0);
- final Matcher matcherInventory = GCConstants.PATTERN_INVENTORY.matcher(page);
+ final MatcherWrapper matcherInventory = new MatcherWrapper(GCConstants.PATTERN_INVENTORY, page);
if (matcherInventory.find()) {
if (cache.getInventory() == null) {
cache.setInventory(new ArrayList<cgTrackable>());
@@ -574,15 +570,13 @@ public abstract class GCParser {
final String inventoryPre = matcherInventory.group(2);
if (StringUtils.isNotBlank(inventoryPre)) {
- final Matcher matcherInventoryInside = GCConstants.PATTERN_INVENTORYINSIDE.matcher(inventoryPre);
+ final MatcherWrapper matcherInventoryInside = new MatcherWrapper(GCConstants.PATTERN_INVENTORYINSIDE, inventoryPre);
while (matcherInventoryInside.find()) {
if (matcherInventoryInside.groupCount() > 0) {
final cgTrackable inventoryItem = new cgTrackable();
- // Create a new string to avoid referencing the whole page though the matcher (@see BaseUtils.getMatch())
- inventoryItem.setGuid(new String(matcherInventoryInside.group(1)));
- // Create a new string to avoid referencing the whole page though the matcher (@see BaseUtils.getMatch())
- inventoryItem.setName(new String(matcherInventoryInside.group(2)));
+ inventoryItem.setGuid(matcherInventoryInside.group(1));
+ inventoryItem.setName(matcherInventoryInside.group(2));
cache.getInventory().add(inventoryItem);
cache.setInventoryItems(cache.getInventoryItems() + 1);
@@ -597,14 +591,12 @@ public abstract class GCParser {
}
// cache logs counts
- try
- {
+ try {
final String countlogs = BaseUtils.getMatch(page, GCConstants.PATTERN_COUNTLOGS, true, null);
if (null != countlogs) {
- final Matcher matcherLog = GCConstants.PATTERN_COUNTLOG.matcher(countlogs);
+ final MatcherWrapper matcherLog = new MatcherWrapper(GCConstants.PATTERN_COUNTLOG, countlogs);
- while (matcherLog.find())
- {
+ while (matcherLog.find()) {
String typeStr = matcherLog.group(1);
String countStr = matcherLog.group(2).replaceAll("[.,]", "");
@@ -615,8 +607,7 @@ public abstract class GCParser {
}
}
}
- } catch (Exception e)
- {
+ } catch (Exception e) {
// failed to parse logs
Log.w("GCParser.parseCache: Failed to parse cache log count");
}
@@ -1003,7 +994,7 @@ public abstract class GCParser {
// maintenance, archived needs to be confirmed
- final Matcher matcher = GCConstants.PATTERN_MAINTENANCE.matcher(page);
+ final MatcherWrapper matcher = new MatcherWrapper(GCConstants.PATTERN_MAINTENANCE, page);
try {
if (matcher.find() && matcher.groupCount() > 0) {
@@ -1052,7 +1043,7 @@ public abstract class GCParser {
try {
- final Matcher matcherOk = GCConstants.PATTERN_OK1.matcher(page);
+ final MatcherWrapper matcherOk = new MatcherWrapper(GCConstants.PATTERN_OK1, page);
if (matcherOk.find()) {
Log.i("Log successfully posted to cache #" + cacheid);
@@ -1121,7 +1112,7 @@ public abstract class GCParser {
try {
- final Matcher matcherOk = GCConstants.PATTERN_OK2.matcher(page);
+ final MatcherWrapper matcherOk = new MatcherWrapper(GCConstants.PATTERN_OK2, page);
if (matcherOk.find()) {
Log.i("Log successfully posted to trackable #" + trackingCode);
return StatusCode.NO_ERROR;
@@ -1292,7 +1283,7 @@ public abstract class GCParser {
// trackable owner name
try {
- final Matcher matcherOwner = GCConstants.PATTERN_TRACKABLE_OWNER.matcher(page);
+ final MatcherWrapper matcherOwner = new MatcherWrapper(GCConstants.PATTERN_TRACKABLE_OWNER, page);
if (matcherOwner.find() && matcherOwner.groupCount() > 0) {
trackable.setOwnerGuid(matcherOwner.group(1));
trackable.setOwner(matcherOwner.group(2).trim());
@@ -1307,14 +1298,14 @@ public abstract class GCParser {
// trackable spotted
try {
- final Matcher matcherSpottedCache = GCConstants.PATTERN_TRACKABLE_SPOTTEDCACHE.matcher(page);
+ final MatcherWrapper matcherSpottedCache = new MatcherWrapper(GCConstants.PATTERN_TRACKABLE_SPOTTEDCACHE, page);
if (matcherSpottedCache.find() && matcherSpottedCache.groupCount() > 0) {
trackable.setSpottedGuid(matcherSpottedCache.group(1));
trackable.setSpottedName(matcherSpottedCache.group(2).trim());
trackable.setSpottedType(cgTrackable.SPOTTED_CACHE);
}
- final Matcher matcherSpottedUser = GCConstants.PATTERN_TRACKABLE_SPOTTEDUSER.matcher(page);
+ final MatcherWrapper matcherSpottedUser = new MatcherWrapper(GCConstants.PATTERN_TRACKABLE_SPOTTEDUSER, page);
if (matcherSpottedUser.find() && matcherSpottedUser.groupCount() > 0) {
trackable.setSpottedGuid(matcherSpottedUser.group(1));
trackable.setSpottedName(matcherSpottedUser.group(2).trim());
@@ -1357,7 +1348,7 @@ public abstract class GCParser {
// trackable details & image
try {
- final Matcher matcherDetailsImage = GCConstants.PATTERN_TRACKABLE_DETAILSIMAGE.matcher(page);
+ final MatcherWrapper matcherDetailsImage = new MatcherWrapper(GCConstants.PATTERN_TRACKABLE_DETAILSIMAGE, page);
if (matcherDetailsImage.find() && matcherDetailsImage.groupCount() > 0) {
final String image = StringUtils.trim(matcherDetailsImage.group(3));
final String details = StringUtils.trim(matcherDetailsImage.group(4));
@@ -1376,7 +1367,7 @@ public abstract class GCParser {
// trackable logs
try {
- final Matcher matcherLogs = GCConstants.PATTERN_TRACKABLE_LOG.matcher(page);
+ final MatcherWrapper matcherLogs = new MatcherWrapper(GCConstants.PATTERN_TRACKABLE_LOG, page);
/*
* 1. Type (image)
* 2. Date
@@ -1406,7 +1397,7 @@ public abstract class GCParser {
// Apply the pattern for images in a trackable log entry against each full log (group(0))
final String logEntry = matcherLogs.group(0);
- final Matcher matcherLogImages = GCConstants.PATTERN_TRACKABLE_LOG_IMAGES.matcher(logEntry);
+ final MatcherWrapper matcherLogImages = new MatcherWrapper(GCConstants.PATTERN_TRACKABLE_LOG_IMAGES, logEntry);
/*
* 1. Image URL
* 2. Image title
@@ -1456,7 +1447,7 @@ public abstract class GCParser {
String rawResponse;
if (!getDataFromPage) {
- final Matcher userTokenMatcher = GCConstants.PATTERN_USERTOKEN.matcher(page);
+ final MatcherWrapper userTokenMatcher = new MatcherWrapper(GCConstants.PATTERN_USERTOKEN, page);
if (!userTokenMatcher.find()) {
Log.e("GCParser.loadLogsFromDetails: unable to extract userToken");
return null;
@@ -1555,7 +1546,7 @@ public abstract class GCParser {
final List<LogType> types = new ArrayList<LogType>();
- final Matcher typeBoxMatcher = GCConstants.PATTERN_TYPEBOX.matcher(page);
+ final MatcherWrapper typeBoxMatcher = new MatcherWrapper(GCConstants.PATTERN_TYPEBOX, page);
String typesText = null;
if (typeBoxMatcher.find()) {
if (typeBoxMatcher.groupCount() > 0) {
@@ -1565,7 +1556,7 @@ public abstract class GCParser {
if (typesText != null) {
- final Matcher typeMatcher = GCConstants.PATTERN_TYPE2.matcher(typesText);
+ final MatcherWrapper typeMatcher = new MatcherWrapper(GCConstants.PATTERN_TYPE2, typesText);
while (typeMatcher.find()) {
if (typeMatcher.groupCount() > 1) {
try {
@@ -1603,7 +1594,7 @@ public abstract class GCParser {
final List<TrackableLog> trackableLogs = new ArrayList<TrackableLog>();
- final Matcher trackableMatcher = GCConstants.PATTERN_TRACKABLE.matcher(page);
+ final MatcherWrapper trackableMatcher = new MatcherWrapper(GCConstants.PATTERN_TRACKABLE, page);
while (trackableMatcher.find()) {
if (trackableMatcher.groupCount() > 0) {
diff --git a/main/src/cgeo/geocaching/connector/gc/Login.java b/main/src/cgeo/geocaching/connector/gc/Login.java
index 494bcee..9a60f65 100644
--- a/main/src/cgeo/geocaching/connector/gc/Login.java
+++ b/main/src/cgeo/geocaching/connector/gc/Login.java
@@ -10,6 +10,7 @@ import cgeo.geocaching.network.Network;
import cgeo.geocaching.network.Parameters;
import cgeo.geocaching.utils.BaseUtils;
import cgeo.geocaching.utils.Log;
+import cgeo.geocaching.utils.MatcherWrapper;
import ch.boye.httpclientandroidlib.HttpResponse;
@@ -26,7 +27,6 @@ import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
-import java.util.regex.Matcher;
public abstract class Login {
@@ -349,7 +349,7 @@ public abstract class Login {
}
int count = 1;
- final Matcher matcherViewstateCount = GCConstants.PATTERN_VIEWSTATEFIELDCOUNT.matcher(page);
+ final MatcherWrapper matcherViewstateCount = new MatcherWrapper(GCConstants.PATTERN_VIEWSTATEFIELDCOUNT, page);
if (matcherViewstateCount.find()) {
try {
count = Integer.parseInt(matcherViewstateCount.group(1));
@@ -361,7 +361,7 @@ public abstract class Login {
String[] viewstates = new String[count];
// Get the viewstates
- final Matcher matcherViewstates = GCConstants.PATTERN_VIEWSTATES.matcher(page);
+ final MatcherWrapper matcherViewstates = new MatcherWrapper(GCConstants.PATTERN_VIEWSTATES, page);
while (matcherViewstates.find()) {
String sno = matcherViewstates.group(1); // number of viewstate
int no;
diff --git a/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java b/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java
index e34d277..5965fff 100644
--- a/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java
+++ b/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java
@@ -1,6 +1,7 @@
package cgeo.geocaching.connector.gc;
-import java.util.regex.Matcher;
+import cgeo.geocaching.utils.MatcherWrapper;
+
import java.util.regex.Pattern;
@@ -35,7 +36,7 @@ public final class UTFGridPosition {
* @return
*/
static UTFGridPosition fromString(String key) {
- final Matcher matcher = UTFGridPosition.PATTERN_JSON_KEY.matcher(key);
+ final MatcherWrapper matcher = new MatcherWrapper(UTFGridPosition.PATTERN_JSON_KEY, key);
try {
if (matcher.matches()) {
final int x = Integer.parseInt(matcher.group(1));
diff --git a/main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java b/main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java
new file mode 100644
index 0000000..d0c0e16
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java
@@ -0,0 +1,514 @@
+package cgeo.geocaching.connector.oc;
+
+import cgeo.geocaching.LogEntry;
+import cgeo.geocaching.Settings;
+import cgeo.geocaching.cgCache;
+import cgeo.geocaching.enumerations.CacheSize;
+import cgeo.geocaching.enumerations.CacheType;
+import cgeo.geocaching.enumerations.LogType;
+import cgeo.geocaching.geopoint.Geopoint;
+import cgeo.geocaching.utils.Log;
+
+import org.apache.commons.lang3.StringUtils;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import android.sax.Element;
+import android.sax.EndElementListener;
+import android.sax.EndTextElementListener;
+import android.sax.RootElement;
+import android.sax.StartElementListener;
+import android.util.Xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.regex.Pattern;
+
+public class OC11XMLParser {
+
+ private static Pattern STRIP_DATE = Pattern.compile("\\+0([0-9]){1}\\:00");
+
+ private static class CacheHolder {
+ public cgCache cache;
+ public String latitude;
+ public String longitude;
+ }
+
+ private static class CacheLog {
+ public String cacheId;
+ public LogEntry logEntry;
+ }
+
+ private static class CacheDescription {
+ public String cacheId;
+ public String shortDesc;
+ public String desc;
+ public String hint;
+ }
+
+ private static Date parseFullDate(final String date) {
+ final SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
+ ISO8601DATEFORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
+ final String strippedDate = STRIP_DATE.matcher(date).replaceAll("+0$100");
+ try {
+ return ISO8601DATEFORMAT.parse(strippedDate);
+ } catch (ParseException e) {
+ Log.e("OC11XMLParser.parseFullDate", e);
+ }
+ return null;
+ }
+
+ private static Date parseDayDate(final String date) {
+ final SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
+ ISO8601DATEFORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
+ final String strippedDate = STRIP_DATE.matcher(date).replaceAll("+0$100");
+ try {
+ return ISO8601DATEFORMAT.parse(strippedDate);
+ } catch (ParseException e) {
+ Log.e("OC11XMLParser.parseDayDate", e);
+ }
+ return null;
+ }
+
+ private static CacheSize getCacheSize(final String sizeId) {
+ int size = Integer.parseInt(sizeId);
+
+ switch (size) {
+ case 1:
+ return CacheSize.OTHER;
+ case 2:
+ return CacheSize.MICRO;
+ case 3:
+ return CacheSize.SMALL;
+ case 4:
+ return CacheSize.REGULAR;
+ case 5:
+ case 6:
+ return CacheSize.LARGE;
+ case 8:
+ return CacheSize.VIRTUAL;
+ default:
+ break;
+ }
+ return CacheSize.NOT_CHOSEN;
+ }
+
+ private static CacheType getCacheType(final String typeId) {
+ int type = Integer.parseInt(typeId);
+ switch (type) {
+ case 1: // Other/unbekannter Cachetyp
+ return CacheType.UNKNOWN;
+ case 2: // Trad./normaler Cache
+ return CacheType.TRADITIONAL;
+ case 3: // Multi/Multicache
+ return CacheType.MULTI;
+ case 4: // Virt./virtueller Cache
+ return CacheType.VIRTUAL;
+ case 5: // ICam./Webcam-Cache
+ return CacheType.WEBCAM;
+ case 6: // Event/Event-Cache
+ return CacheType.EVENT;
+ case 7: // Quiz/Rätselcache
+ return CacheType.MYSTERY;
+ case 8: // Math/Mathe-/Physikcache
+ return CacheType.MYSTERY;
+ case 9: // Moving/beweglicher Cache
+ return CacheType.UNKNOWN;
+ case 10: // Driv./Drive-In
+ return CacheType.TRADITIONAL;
+ }
+ return CacheType.UNKNOWN;
+ }
+
+ private static LogType getLogType(final int typeId) {
+ switch (typeId) {
+ case 1:
+ return LogType.FOUND_IT;
+ case 2:
+ return LogType.DIDNT_FIND_IT;
+ case 3:
+ return LogType.NOTE;
+ case 7:
+ return LogType.ATTENDED;
+ case 8:
+ return LogType.WILL_ATTEND;
+ }
+ return LogType.UNKNOWN;
+ }
+
+ private static void setCacheStatus(final int statusId, final cgCache cache) {
+
+ switch (statusId) {
+ case 1:
+ cache.setArchived(false);
+ cache.setDisabled(false);
+ break;
+ case 2:
+ cache.setArchived(false);
+ cache.setDisabled(true);
+ break;
+ default:
+ cache.setArchived(true);
+ cache.setDisabled(false);
+ break;
+ }
+ }
+
+ private static void resetCache(final CacheHolder cacheHolder) {
+ cacheHolder.cache = new cgCache(null);
+ cacheHolder.cache.setReliableLatLon(true);
+ cacheHolder.cache.setDescription(StringUtils.EMPTY);
+ cacheHolder.latitude = "0.0";
+ cacheHolder.longitude = "0.0";
+ }
+
+ private static void resetLog(final CacheLog log) {
+ log.cacheId = StringUtils.EMPTY;
+ log.logEntry = new LogEntry("", 0, LogType.UNKNOWN, "");
+ }
+
+ private static void resetDesc(final CacheDescription desc) {
+ desc.cacheId = StringUtils.EMPTY;
+ desc.shortDesc = StringUtils.EMPTY;
+ desc.desc = StringUtils.EMPTY;
+ desc.hint = StringUtils.EMPTY;
+ }
+
+ public static Collection<cgCache> parseCaches(final InputStream stream) throws IOException {
+
+ final int CACHE_PARSE_LIMIT = 250;
+
+ final Map<String, cgCache> caches = new HashMap<String, cgCache>();
+
+ final CacheHolder cacheHolder = new CacheHolder();
+ final CacheLog logHolder = new CacheLog();
+ final CacheDescription descHolder = new CacheDescription();
+
+ final RootElement root = new RootElement("oc11xml");
+ final Element cacheNode = root.getChild("cache");
+
+ // cache
+ cacheNode.setStartElementListener(new StartElementListener() {
+
+ @Override
+ public void start(Attributes attributes) {
+ resetCache(cacheHolder);
+ }
+
+ });
+
+ cacheNode.setEndElementListener(new EndElementListener() {
+
+ @Override
+ public void end() {
+ cgCache cache = cacheHolder.cache;
+ Geopoint coords = new Geopoint(cacheHolder.latitude, cacheHolder.longitude);
+ if (StringUtils.isNotBlank(cache.getGeocode())
+ && !coords.equals(Geopoint.ZERO)
+ && !cache.isArchived()
+ && caches.size() < CACHE_PARSE_LIMIT) {
+ cache.setCoords(coords);
+ caches.put(cache.getCacheId(), cache);
+ }
+ }
+ });
+
+ // cache.id
+ cacheNode.getChild("id").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ cacheHolder.cache.setCacheId(body);
+ }
+ });
+
+ // cache.longitude
+ cacheNode.getChild("longitude").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ String longitude = body.trim();
+ if (StringUtils.isNotBlank(longitude)) {
+ cacheHolder.longitude = longitude;
+ }
+ }
+ });
+
+ // cache.latitude
+ cacheNode.getChild("latitude").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ String latitude = body.trim();
+ if (StringUtils.isNotBlank(latitude)) {
+ cacheHolder.latitude = latitude;
+ }
+ }
+ });
+
+ // cache.name
+ cacheNode.getChild("name").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ final String content = body.trim();
+ cacheHolder.cache.setName(content);
+ }
+ });
+
+ // cache.waypoints[oc]
+ cacheNode.getChild("waypoints").setStartElementListener(new StartElementListener() {
+
+ @Override
+ public void start(Attributes attrs) {
+ if (attrs.getIndex("oc") > -1) {
+ cacheHolder.cache.setGeocode(attrs.getValue("oc"));
+ }
+ if (attrs.getIndex("gccom") > -1) {
+ String gccode = attrs.getValue("gccom");
+ if (!StringUtils.isBlank(gccode)) {
+ cacheHolder.cache.setDescription(String.format("Listed on geocaching com: <a href=\"http://coord.info/%s\">%s</a><br /><br />", gccode, gccode));
+ }
+ }
+ }
+ });
+
+ // cache.type[id]
+ cacheNode.getChild("type").setStartElementListener(new StartElementListener() {
+
+ @Override
+ public void start(Attributes attrs) {
+ if (attrs.getIndex("id") > -1) {
+ final String typeId = attrs.getValue("id");
+ cacheHolder.cache.setType(getCacheType(typeId));
+ }
+ }
+ });
+
+ // cache.status[id]
+ cacheNode.getChild("status").setStartElementListener(new StartElementListener() {
+
+ @Override
+ public void start(Attributes attrs) {
+ if (attrs.getIndex("id") > -1) {
+ try {
+ final int statusId = Integer.parseInt(attrs.getValue("id"));
+ setCacheStatus(statusId, cacheHolder.cache);
+ } catch (NumberFormatException e) {
+ Log.w(String.format("Failed to parse status of cache '%s'.", cacheHolder.cache.getGeocode()));
+ }
+ }
+ }
+ });
+
+ // cache.size[id]
+ cacheNode.getChild("size").setStartElementListener(new StartElementListener() {
+
+ @Override
+ public void start(Attributes attrs) {
+ if (attrs.getIndex("id") > -1) {
+ final String typeId = attrs.getValue("id");
+ cacheHolder.cache.setSize(getCacheSize(typeId));
+ }
+ }
+ });
+
+ // cache.difficulty
+ cacheNode.getChild("difficulty").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ final String content = body.trim();
+ cacheHolder.cache.setDifficulty(Float.valueOf(content));
+ }
+ });
+
+ // cache.terrain
+ cacheNode.getChild("terrain").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ final String content = body.trim();
+ cacheHolder.cache.setTerrain(Float.valueOf(content));
+ }
+ });
+
+ // cache.terrain
+ cacheNode.getChild("datehidden").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ final String content = body.trim();
+ cacheHolder.cache.setHidden(parseFullDate(content));
+ }
+ });
+
+ // cache.attributes.attribute
+ cacheNode.getChild("attributes").getChild("attribute").setEndTextElementListener(new EndTextElementListener() {
+ @Override
+ public void end(String body) {
+ if (StringUtils.isNotBlank(body)) {
+ cacheHolder.cache.getAttributes().add(body.trim());
+ }
+ }
+ });
+
+ // cachedesc
+ final Element cacheDesc = root.getChild("cachedesc");
+
+ cacheDesc.setStartElementListener(new StartElementListener() {
+
+ @Override
+ public void start(Attributes attributes) {
+ resetDesc(descHolder);
+ }
+ });
+
+ cacheDesc.setEndElementListener(new EndElementListener() {
+
+ @Override
+ public void end() {
+ final cgCache cache = caches.get(descHolder.cacheId);
+ if (cache != null) {
+ cache.setShortdesc(descHolder.shortDesc);
+ cache.setDescription(cache.getDescription() + descHolder.desc);
+ cache.setHint(descHolder.hint);
+ }
+ }
+ });
+
+ // cachedesc.cacheid
+ cacheDesc.getChild("cacheid").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ descHolder.cacheId = body;
+ }
+ });
+
+ // cachedesc.desc
+ cacheDesc.getChild("shortdesc").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ final String content = body.trim();
+ descHolder.shortDesc = content;
+ }
+ });
+
+ // cachedesc.desc
+ cacheDesc.getChild("desc").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ final String content = body.trim();
+ descHolder.desc = content;
+ }
+ });
+
+ // cachedesc.hint
+ cacheDesc.getChild("hint").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ final String content = body.trim();
+ descHolder.hint = content;
+ }
+ });
+
+ // cachelog
+ final Element cacheLog = root.getChild("cachelog");
+
+ cacheLog.setStartElementListener(new StartElementListener() {
+
+ @Override
+ public void start(Attributes attrs) {
+ resetLog(logHolder);
+ }
+ });
+
+ cacheLog.setEndElementListener(new EndElementListener() {
+
+ @Override
+ public void end() {
+ final cgCache cache = caches.get(logHolder.cacheId);
+ if (cache != null && logHolder.logEntry.type != LogType.UNKNOWN) {
+ cache.getLogs().prepend(logHolder.logEntry);
+ if (logHolder.logEntry.type == LogType.FOUND_IT
+ && StringUtils.equals(logHolder.logEntry.author, Settings.getOCConnectorUserName())) {
+ cache.setFound(true);
+ cache.setVisitedDate(logHolder.logEntry.date);
+ }
+ }
+ }
+ });
+
+ // cachelog.cacheid
+ cacheLog.getChild("cacheid").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ logHolder.cacheId = body;
+ }
+ });
+
+ // cachelog.date
+ cacheLog.getChild("date").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String body) {
+ try {
+ logHolder.logEntry.date = parseDayDate(body).getTime();
+ } catch (Exception e) {
+ Log.w("Failed to parse log date: " + e.toString());
+ }
+ }
+ });
+
+ // cachelog.logtype
+ cacheLog.getChild("logtype").setStartElementListener(new StartElementListener() {
+
+ @Override
+ public void start(Attributes attrs) {
+ if (attrs.getIndex("id") > -1) {
+ final int typeId = Integer.parseInt(attrs.getValue("id"));
+ logHolder.logEntry.type = getLogType(typeId);
+ }
+ }
+ });
+
+ // cachelog.userid
+ cacheLog.getChild("userid").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String finderName) {
+ logHolder.logEntry.author = finderName;
+ }
+ });
+
+ // cachelog.text
+ cacheLog.getChild("text").setEndTextElementListener(new EndTextElementListener() {
+
+ @Override
+ public void end(String logText) {
+ logHolder.logEntry.log = logText;
+ }
+ });
+
+ try {
+ Xml.parse(stream, Xml.Encoding.UTF_8, root.getContentHandler());
+ return caches.values();
+ } catch (SAXException e) {
+ Log.e("Cannot parse .gpx file as oc11xml: could not parse XML - " + e.toString());
+ return null;
+ }
+ }
+}
diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java
index 17c961a..74968e7 100644
--- a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java
+++ b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java
@@ -34,4 +34,10 @@ public class OCApiConnector extends OCConnector implements ISearchByGeocode {
}
return new SearchResult(cache);
}
+
+ @Override
+ public boolean isActivated() {
+ // currently always active, but only for details download
+ return true;
+ }
}
diff --git a/main/src/cgeo/geocaching/connector/oc/OCConnector.java b/main/src/cgeo/geocaching/connector/oc/OCConnector.java
index c098d12..24fd7d6 100644
--- a/main/src/cgeo/geocaching/connector/oc/OCConnector.java
+++ b/main/src/cgeo/geocaching/connector/oc/OCConnector.java
@@ -2,6 +2,7 @@ package cgeo.geocaching.connector.oc;
import cgeo.geocaching.cgCache;
import cgeo.geocaching.connector.AbstractConnector;
+import cgeo.geocaching.enumerations.CacheRealm;
import java.util.regex.Pattern;
@@ -51,4 +52,9 @@ public class OCConnector extends AbstractConnector {
return "http://" + host + "/viewcache.php?wp=";
}
+ @Override
+ public CacheRealm getCacheRealm() {
+ return CacheRealm.OC;
+ }
+
}
diff --git a/main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java b/main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java
new file mode 100644
index 0000000..3a2f42e
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java
@@ -0,0 +1,52 @@
+package cgeo.geocaching.connector.oc;
+
+import cgeo.geocaching.SearchResult;
+import cgeo.geocaching.Settings;
+import cgeo.geocaching.cgCache;
+import cgeo.geocaching.connector.capability.ISearchByCenter;
+import cgeo.geocaching.connector.capability.ISearchByGeocode;
+import cgeo.geocaching.connector.capability.ISearchByViewPort;
+import cgeo.geocaching.geopoint.Geopoint;
+import cgeo.geocaching.geopoint.Viewport;
+import cgeo.geocaching.utils.CancellableHandler;
+
+public class OCXMLApiConnector extends OCConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort {
+
+ private final static double SEARCH_DISTANCE_LIMIT = 15.0;
+ private final static double NEARBY_SEARCH_DISTANCE = 5.0;
+
+ public OCXMLApiConnector(String name, String host, String prefix) {
+ super(name, host, prefix);
+ }
+
+ @Override
+ public SearchResult searchByGeocode(final String geocode, final String guid, CancellableHandler handler) {
+ final cgCache cache = OCXMLClient.getCache(geocode);
+ if (cache == null) {
+ return null;
+ }
+ return new SearchResult(cache);
+ }
+
+ @Override
+ public SearchResult searchByCenter(final Geopoint center) {
+ return new SearchResult(OCXMLClient.getCachesAround(center, NEARBY_SEARCH_DISTANCE));
+ }
+
+ @Override
+ public SearchResult searchByViewport(final Viewport viewport, final String[] tokens) {
+ final Geopoint center = viewport.getCenter();
+ double distance = center.distanceTo(viewport.bottomLeft) * 1.15;
+ if (distance > SEARCH_DISTANCE_LIMIT) {
+ distance = SEARCH_DISTANCE_LIMIT;
+ }
+ return new SearchResult(OCXMLClient.getCachesAround(center, distance));
+ }
+
+ @Override
+ public boolean isActivated() {
+ // currently only tested and working with oc.de
+ return Settings.isOCConnectorActive();
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/connector/oc/OCXMLClient.java b/main/src/cgeo/geocaching/connector/oc/OCXMLClient.java
new file mode 100644
index 0000000..26b42e3
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/oc/OCXMLClient.java
@@ -0,0 +1,110 @@
+package cgeo.geocaching.connector.oc;
+
+import cgeo.geocaching.cgCache;
+import cgeo.geocaching.cgData;
+import cgeo.geocaching.connector.ConnectorFactory;
+import cgeo.geocaching.connector.IConnector;
+import cgeo.geocaching.enumerations.LoadFlags;
+import cgeo.geocaching.geopoint.Geopoint;
+import cgeo.geocaching.geopoint.GeopointFormatter;
+import cgeo.geocaching.network.Network;
+import cgeo.geocaching.network.Parameters;
+import cgeo.geocaching.utils.Log;
+
+import ch.boye.httpclientandroidlib.HttpResponse;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.zip.GZIPInputStream;
+
+public class OCXMLClient {
+
+ private static final String SERVICE_CACHE = "/xml/ocxml11.php";
+
+ // Url for single cache requests
+ // http://www.opencaching.de/xml/ocxml11.php?modifiedsince=20060320000000&user=0&cache=1&cachedesc=1&cachelog=1&picture=1&removedobject=0&session=0&doctype=0&charset=utf-8&wp=OCC9BE
+
+ public static cgCache getCache(final String geoCode) {
+ try {
+ final Parameters params = getOCXmlQueryParameters(true, true);
+ params.put("wp", geoCode);
+ final InputStream data = request(ConnectorFactory.getConnector(geoCode), SERVICE_CACHE, params);
+
+ if (data == null) {
+ return null;
+ }
+
+ Collection<cgCache> caches = OC11XMLParser.parseCaches(new GZIPInputStream(data));
+ if (caches.iterator().hasNext()) {
+ cgCache cache = caches.iterator().next();
+ cache.setDetailed(true);
+ cgData.saveCache(cache, LoadFlags.SAVE_ALL);
+ return cache;
+ }
+ return null;
+ } catch (IOException e) {
+ Log.e("Error parsing cache '" + geoCode + "': " + e.toString());
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Collection<cgCache> getCachesAround(final Geopoint center, final double distance) {
+ try {
+ final Parameters params = getOCXmlQueryParameters(false, false);
+ params.put("lat", GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, center));
+ params.put("lon", GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, center));
+ params.put("distance", String.format(Locale.US, "%f", distance));
+ final InputStream data = request(ConnectorFactory.getConnector("OCXXX"), SERVICE_CACHE, params);
+
+ if (data == null) {
+ return CollectionUtils.EMPTY_COLLECTION;
+ }
+
+ return OC11XMLParser.parseCaches(new GZIPInputStream(data));
+ } catch (IOException e) {
+ Log.e("Error parsing nearby search result: " + e.toString());
+ return CollectionUtils.EMPTY_COLLECTION;
+ }
+ }
+
+ private static InputStream request(final IConnector connector, final String service, final Parameters params) {
+ if (connector == null) {
+ return null;
+ }
+ if (!(connector instanceof OCXMLApiConnector)) {
+ return null;
+ }
+
+ final String host = connector.getHost();
+ if (StringUtils.isBlank(host)) {
+ return null;
+ }
+
+ final String uri = "http://" + host + service;
+ HttpResponse resp = Network.getRequest(uri, params);
+ if (resp != null) {
+ try {
+ return resp.getEntity().getContent();
+ } catch (IllegalStateException e) {
+ // fall through and return null
+ } catch (IOException e) {
+ // fall through and return null
+ }
+ }
+ return null;
+ }
+
+ private static Parameters getOCXmlQueryParameters(final boolean withDescription, final boolean withLogs) {
+ final Parameters params = new Parameters("modifiedsince", "20060320000000",
+ "user", "0", "cache", "1", "cachedesc", withDescription ? "1" : "0",
+ "cachelog", withLogs ? "1" : "0", "picture", "0", "removedobject", "0",
+ "session", "0", "doctype", "0", "charset", "utf-8", "zip", "gzip");
+ return params;
+ }
+}
diff --git a/main/src/cgeo/geocaching/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/files/GPXParser.java b/main/src/cgeo/geocaching/files/GPXParser.java
index 2ac19bb..e2d7205 100644
--- a/main/src/cgeo/geocaching/files/GPXParser.java
+++ b/main/src/cgeo/geocaching/files/GPXParser.java
@@ -19,6 +19,7 @@ import cgeo.geocaching.enumerations.WaypointType;
import cgeo.geocaching.geopoint.Geopoint;
import cgeo.geocaching.utils.CancellableHandler;
import cgeo.geocaching.utils.Log;
+import cgeo.geocaching.utils.MatcherWrapper;
import org.apache.commons.lang3.StringUtils;
import org.xml.sax.Attributes;
@@ -42,7 +43,6 @@ import java.util.EnumSet;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
public abstract class GPXParser extends FileParser {
@@ -209,7 +209,7 @@ public abstract class GPXParser extends FileParser {
return null;
}
// cut out baseName
- final Matcher m = BASENAME_PATTERN.matcher(stringName);
+ final MatcherWrapper m = new MatcherWrapper(BASENAME_PATTERN, stringName);
if (!m.matches()) {
return null;
}
@@ -235,7 +235,7 @@ public abstract class GPXParser extends FileParser {
static Date parseDate(String inputUntrimmed) throws ParseException {
String input = inputUntrimmed.trim();
// remove milliseconds to reduce number of needed patterns
- final Matcher matcher = PATTERN_MILLISECONDS.matcher(input);
+ final MatcherWrapper matcher = new MatcherWrapper(PATTERN_MILLISECONDS, input);
input = matcher.replaceFirst("");
if (input.contains("Z")) {
return formatSimpleZ.parse(input);
@@ -432,14 +432,14 @@ public abstract class GPXParser extends FileParser {
@Override
public void end(String url) {
- final Matcher matcher = patternGuid.matcher(url);
+ final MatcherWrapper matcher = new MatcherWrapper(patternGuid, url);
if (matcher.matches()) {
final String guid = matcher.group(1);
if (StringUtils.isNotBlank(guid)) {
cache.setGuid(guid);
}
}
- final Matcher matcherCode = patternUrlGeocode.matcher(url);
+ final MatcherWrapper matcherCode = new MatcherWrapper(patternUrlGeocode, url);
if (matcherCode.matches()) {
String geocode = matcherCode.group(1);
cache.setGeocode(geocode);
@@ -836,7 +836,7 @@ public abstract class GPXParser extends FileParser {
return;
}
final String trimmed = input.trim();
- final Matcher matcherGeocode = patternGeocode.matcher(trimmed);
+ final MatcherWrapper matcherGeocode = new MatcherWrapper(patternGeocode, trimmed);
if (matcherGeocode.find()) {
final String geocode = matcherGeocode.group(1);
// a geocode should not be part of a word
diff --git a/main/src/cgeo/geocaching/files/LocParser.java b/main/src/cgeo/geocaching/files/LocParser.java
index 9c24d39..730e224 100644
--- a/main/src/cgeo/geocaching/files/LocParser.java
+++ b/main/src/cgeo/geocaching/files/LocParser.java
@@ -9,6 +9,7 @@ import cgeo.geocaching.enumerations.LoadFlags;
import cgeo.geocaching.geopoint.Geopoint;
import cgeo.geocaching.utils.CancellableHandler;
import cgeo.geocaching.utils.Log;
+import cgeo.geocaching.utils.MatcherWrapper;
import org.apache.commons.lang3.StringUtils;
@@ -22,7 +23,6 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class LocParser extends FileParser {
@@ -148,12 +148,12 @@ public final class LocParser extends FileParser {
public static cgCache parseCache(final String pointString) {
final cgCache cache = new cgCache();
- final Matcher matcherGeocode = patternGeocode.matcher(pointString);
+ final MatcherWrapper matcherGeocode = new MatcherWrapper(patternGeocode, pointString);
if (matcherGeocode.find()) {
cache.setGeocode(matcherGeocode.group(1).trim());
}
- final Matcher matcherName = patternName.matcher(pointString);
+ final MatcherWrapper matcherName = new MatcherWrapper(patternName, pointString);
if (matcherName.find()) {
final String name = matcherName.group(1).trim();
cache.setName(StringUtils.substringBeforeLast(name, " by ").trim());
@@ -161,24 +161,24 @@ public final class LocParser extends FileParser {
cache.setName(cache.getGeocode());
}
- final Matcher matcherLat = patternLat.matcher(pointString);
- final Matcher matcherLon = patternLon.matcher(pointString);
+ final MatcherWrapper matcherLat = new MatcherWrapper(patternLat, pointString);
+ final MatcherWrapper matcherLon = new MatcherWrapper(patternLon, pointString);
if (matcherLat.find() && matcherLon.find()) {
cache.setCoords(parsePoint(matcherLat.group(1).trim(), matcherLon.group(1).trim()));
}
- final Matcher matcherDifficulty = patternDifficulty.matcher(pointString);
+ final MatcherWrapper matcherDifficulty = new MatcherWrapper(patternDifficulty, pointString);
try {
if (matcherDifficulty.find()) {
cache.setDifficulty(Float.parseFloat(matcherDifficulty.group(1).trim()));
}
- final Matcher matcherTerrain = patternTerrain.matcher(pointString);
+ final MatcherWrapper matcherTerrain = new MatcherWrapper(patternTerrain, pointString);
if (matcherTerrain.find()) {
cache.setTerrain(Float.parseFloat(matcherTerrain.group(1).trim()));
}
- final Matcher matcherContainer = patternContainer.matcher(pointString);
+ final MatcherWrapper matcherContainer = new MatcherWrapper(patternContainer, pointString);
if (matcherContainer.find()) {
final int size = Integer.parseInt(matcherContainer.group(1).trim());
if (size >= 1 && size <= 8) {
diff --git a/main/src/cgeo/geocaching/files/LocalStorage.java b/main/src/cgeo/geocaching/files/LocalStorage.java
index 4cf28f7..3101ae9 100644
--- a/main/src/cgeo/geocaching/files/LocalStorage.java
+++ b/main/src/cgeo/geocaching/files/LocalStorage.java
@@ -1,11 +1,11 @@
package cgeo.geocaching.files;
+import cgeo.geocaching.cgeoapplication;
import cgeo.geocaching.utils.CryptUtils;
import cgeo.geocaching.utils.Log;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpResponse;
-
import org.apache.commons.lang3.StringUtils;
import android.os.Environment;
@@ -30,6 +30,8 @@ public class LocalStorage {
/** Name of the local private directory used to hold cached information */
public final static String cache = ".cgeo";
+ private static File internalStorageBase;
+
/**
* Return the primary storage cache root (external media if mounted, phone otherwise).
*
@@ -67,7 +69,11 @@ public class LocalStorage {
}
private static File getInternalStorageBase() {
- return new File(new File(Environment.getDataDirectory(), "data"), "cgeo.geocaching");
+ if (internalStorageBase == null) {
+ // A race condition will do no harm as the operation is idempotent. No need to synchronize.
+ internalStorageBase = cgeoapplication.getInstance().getApplicationContext().getFilesDir().getParentFile();
+ }
+ return internalStorageBase;
}
/**
diff --git a/main/src/cgeo/geocaching/gcvote/GCVote.java b/main/src/cgeo/geocaching/gcvote/GCVote.java
index 3d87724..e98ba56 100644
--- a/main/src/cgeo/geocaching/gcvote/GCVote.java
+++ b/main/src/cgeo/geocaching/gcvote/GCVote.java
@@ -6,6 +6,7 @@ import cgeo.geocaching.network.Network;
import cgeo.geocaching.network.Parameters;
import cgeo.geocaching.utils.LeastRecentlyUsedMap;
import cgeo.geocaching.utils.Log;
+import cgeo.geocaching.utils.MatcherWrapper;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
@@ -15,7 +16,6 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class GCVote {
@@ -98,7 +98,7 @@ public final class GCVote {
return null;
}
- final Matcher matcherVoteElement = patternVoteElement.matcher(page);
+ final MatcherWrapper matcherVoteElement = new MatcherWrapper(patternVoteElement, page);
while (matcherVoteElement.find()) {
String voteData = matcherVoteElement.group(1);
if (voteData == null) {
@@ -107,7 +107,7 @@ public final class GCVote {
String guid = null;
try {
- final Matcher matcherGuid = patternGuid.matcher(voteData);
+ final MatcherWrapper matcherGuid = new MatcherWrapper(patternGuid, voteData);
if (matcherGuid.find()) {
if (matcherGuid.groupCount() > 0) {
guid = matcherGuid.group(1);
@@ -122,7 +122,7 @@ public final class GCVote {
boolean loggedIn = false;
try {
- final Matcher matcherLoggedIn = patternLogIn.matcher(page);
+ final MatcherWrapper matcherLoggedIn = new MatcherWrapper(patternLogIn, page);
if (matcherLoggedIn.find()) {
if (matcherLoggedIn.groupCount() > 0) {
if (matcherLoggedIn.group(1).equalsIgnoreCase("true")) {
@@ -136,7 +136,7 @@ public final class GCVote {
float rating = 0;
try {
- final Matcher matcherRating = patternRating.matcher(voteData);
+ final MatcherWrapper matcherRating = new MatcherWrapper(patternRating, voteData);
if (matcherRating.find()) {
rating = Float.parseFloat(matcherRating.group(1));
}
@@ -149,7 +149,7 @@ public final class GCVote {
int votes = -1;
try {
- final Matcher matcherVotes = patternVotes.matcher(voteData);
+ final MatcherWrapper matcherVotes = new MatcherWrapper(patternVotes, voteData);
if (matcherVotes.find()) {
votes = Integer.parseInt(matcherVotes.group(1));
}
@@ -163,7 +163,7 @@ public final class GCVote {
float myVote = 0;
if (loggedIn) {
try {
- final Matcher matcherVote = patternVote.matcher(voteData);
+ final MatcherWrapper matcherVote = new MatcherWrapper(patternVote, voteData);
if (matcherVote.find()) {
myVote = Float.parseFloat(matcherVote.group(1));
}
diff --git a/main/src/cgeo/geocaching/geopoint/DistanceParser.java b/main/src/cgeo/geocaching/geopoint/DistanceParser.java
index d8db8e4..e1692f4 100644
--- a/main/src/cgeo/geocaching/geopoint/DistanceParser.java
+++ b/main/src/cgeo/geocaching/geopoint/DistanceParser.java
@@ -1,9 +1,10 @@
package cgeo.geocaching.geopoint;
+import cgeo.geocaching.utils.MatcherWrapper;
+
import org.apache.commons.lang3.StringUtils;
import java.util.Locale;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class DistanceParser {
@@ -22,7 +23,7 @@ public final class DistanceParser {
* if the given number is invalid
*/
public static float parseDistance(String distanceText, final boolean metricUnit) {
- final Matcher matcher = pattern.matcher(distanceText);
+ final MatcherWrapper matcher = new MatcherWrapper(pattern, distanceText);
if (!matcher.find()) {
throw new NumberFormatException(distanceText);
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/geopoint/GeopointParser.java b/main/src/cgeo/geocaching/geopoint/GeopointParser.java
index 7604b9d..97a9ec8 100644
--- a/main/src/cgeo/geocaching/geopoint/GeopointParser.java
+++ b/main/src/cgeo/geocaching/geopoint/GeopointParser.java
@@ -1,9 +1,10 @@
package cgeo.geocaching.geopoint;
+import cgeo.geocaching.utils.MatcherWrapper;
+
import org.apache.commons.lang3.StringUtils;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
@@ -105,7 +106,7 @@ class GeopointParser {
{
final Pattern pattern = LatLon.LAT == latlon ? patternLat : patternLon;
- final Matcher matcher = pattern.matcher(text);
+ final MatcherWrapper matcher = new MatcherWrapper(pattern, text);
if (matcher.find()) {
final double sign = matcher.group(1).equalsIgnoreCase("S") || matcher.group(1).equalsIgnoreCase("W") ? -1.0 : 1.0;
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
diff --git a/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java b/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java
index 96edebf..61478b5 100644
--- a/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java
+++ b/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java
@@ -7,6 +7,7 @@ import cgeo.geocaching.network.Network;
import cgeo.geocaching.network.OAuth;
import cgeo.geocaching.network.Parameters;
import cgeo.geocaching.utils.Log;
+import cgeo.geocaching.utils.MatcherWrapper;
import ch.boye.httpclientandroidlib.client.entity.UrlEncodedFormEntity;
import ch.boye.httpclientandroidlib.util.EntityUtils;
@@ -24,7 +25,6 @@ import android.view.View;
import android.widget.Button;
import android.widget.EditText;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TwitterAuthorizationActivity extends AbstractActivity {
@@ -142,11 +142,11 @@ public class TwitterAuthorizationActivity extends AbstractActivity {
if (StringUtils.isNotBlank(line)) {
- final Matcher paramsMatcher1 = paramsPattern1.matcher(line);
+ final MatcherWrapper paramsMatcher1 = new MatcherWrapper(paramsPattern1, line);
if (paramsMatcher1.find()) {
OAtoken = paramsMatcher1.group(1);
}
- final Matcher paramsMatcher2 = paramsPattern2.matcher(line);
+ final MatcherWrapper paramsMatcher2 = new MatcherWrapper(paramsPattern2, line);
if (paramsMatcher2.find()) {
OAtokenSecret = paramsMatcher2.group(1);
}
@@ -189,11 +189,11 @@ public class TwitterAuthorizationActivity extends AbstractActivity {
OAtoken = "";
OAtokenSecret = "";
- final Matcher paramsMatcher1 = paramsPattern1.matcher(line);
+ final MatcherWrapper paramsMatcher1 = new MatcherWrapper(paramsPattern1, line);
if (paramsMatcher1.find()) {
OAtoken = paramsMatcher1.group(1);
}
- final Matcher paramsMatcher2 = paramsPattern2.matcher(line);
+ final MatcherWrapper paramsMatcher2 = new MatcherWrapper(paramsPattern2, line);
if (paramsMatcher2.find() && paramsMatcher2.groupCount() > 0) {
OAtokenSecret = paramsMatcher2.group(1);
}
diff --git a/main/src/cgeo/geocaching/utils/MatcherWrapper.java b/main/src/cgeo/geocaching/utils/MatcherWrapper.java
new file mode 100644
index 0000000..c3c1663
--- /dev/null
+++ b/main/src/cgeo/geocaching/utils/MatcherWrapper.java
@@ -0,0 +1,91 @@
+package cgeo.geocaching.utils;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Wrapper around the regex {@link Matcher} class. This implementation optimizes the memory usage of the matched
+ * Strings.
+ *
+ */
+public class MatcherWrapper {
+ private final Matcher matcher;
+
+ public MatcherWrapper(Pattern pattern, String input) {
+ this.matcher = pattern.matcher(input);
+ }
+
+ /**
+ * see {@link Matcher#find()}
+ */
+ public boolean find() {
+ return matcher.find();
+ }
+
+ /**
+ * see {@link Matcher#group(int)}
+ */
+ public String group(int index) {
+ return newString(matcher.group(index));
+ }
+
+ /**
+ * This method explicitly creates a new String instance from an already existing String. This is necessary to avoid
+ * huge memory leaks in our parser. If we do regular expression matching on large Strings, the returned matches are
+ * otherwise memory mapped substrings of the huge original String, therefore blocking the garbage collector from
+ * removing the huge input String.
+ * <p>
+ * Do not change this method, even if Findbugs and other tools will report a violation for that line!
+ *
+ * @param input
+ * @return
+ */
+ private static String newString(String input) {
+ if (input == null) {
+ return null;
+ }
+ return new String(input); // DON'T REMOVE THE "new String" HERE!
+ }
+
+ /**
+ * see {@link Matcher#groupCount()}
+ */
+ public int groupCount() {
+ return matcher.groupCount();
+ }
+
+ /**
+ * see {@link Matcher#group()}
+ */
+ public String group() {
+ return newString(matcher.group());
+ }
+
+ /**
+ * see {@link Matcher#start()}
+ */
+ public int start() {
+ return matcher.start();
+ }
+
+ /**
+ * see {@link Matcher#replaceAll(String)}
+ */
+ public String replaceAll(String replacement) {
+ return newString(matcher.replaceAll(replacement));
+ }
+
+ /**
+ * see {@link Matcher#matches()}
+ */
+ public boolean matches() {
+ return matcher.matches();
+ }
+
+ /**
+ * see {@link Matcher#replaceFirst(String)}
+ */
+ public String replaceFirst(String replacement) {
+ return newString(matcher.replaceFirst(replacement));
+ }
+}