aboutsummaryrefslogtreecommitdiffstats
path: root/main
diff options
context:
space:
mode:
authorrsudev <rasch@munin-soft.de>2013-05-28 23:17:56 -0700
committerrsudev <rasch@munin-soft.de>2013-05-28 23:17:56 -0700
commitf6779ac30d6ac1357d67654ea17afa1f1d6fa770 (patch)
treee1e84fca95948ef7ba13be7e12809388b24d3267 /main
parentd6f1bc648361076c3b2038a6ab9b175e1de39b3d (diff)
parenta7de49db675048db5ae0c05ccd730a8579e6876e (diff)
downloadcgeo-f6779ac30d6ac1357d67654ea17afa1f1d6fa770.zip
cgeo-f6779ac30d6ac1357d67654ea17afa1f1d6fa770.tar.gz
cgeo-f6779ac30d6ac1357d67654ea17afa1f1d6fa770.tar.bz2
Merge pull request #2787 from rsudev/ocde_okapi_2
Implements OKAPI access for opencaching.de
Diffstat (limited to 'main')
-rw-r--r--main/AndroidManifest.xml24
-rw-r--r--main/build.xml6
-rw-r--r--main/project/attributes_okapi/AttrGen/.classpath6
-rw-r--r--main/project/attributes_okapi/AttrGen/.project17
-rw-r--r--main/project/attributes_okapi/AttrGen/.settings/org.eclipse.jdt.core.prefs11
-rw-r--r--main/project/attributes_okapi/AttrGen/.settings/org.eclipse.jdt.ui.prefs54
-rw-r--r--main/project/attributes_okapi/AttrGen/build.xml14
-rw-r--r--main/project/attributes_okapi/AttrGen/src/GenerateAttributes.java172
-rw-r--r--main/project/attributes_okapi/AttributeParser.java327
-rw-r--r--main/project/attributes_okapi/attributes.xml1940
-rw-r--r--main/project/attributes_okapi/genattr.jarbin0 -> 4006 bytes
-rw-r--r--main/project/attributes_okapi/genattr.sh2
-rw-r--r--main/project/attributes_okapi/readme.txt10
-rw-r--r--main/res/layout/authorization_activity.xml (renamed from main/res/layout/twitter_authorization_activity.xml)9
-rw-r--r--main/res/layout/init.xml39
-rw-r--r--main/res/values-cs/strings.xml2
-rw-r--r--main/res/values-de/strings.xml24
-rw-r--r--main/res/values-fr/strings.xml2
-rw-r--r--main/res/values-it/strings.xml2
-rw-r--r--main/res/values-ja/strings.xml2
-rw-r--r--main/res/values-nl/strings.xml2
-rw-r--r--main/res/values-pl/strings.xml2
-rw-r--r--main/res/values-sv/strings.xml2
-rw-r--r--main/res/values/.gitignore1
-rw-r--r--main/res/values/strings.xml25
-rw-r--r--main/src/cgeo/geocaching/CacheDetailActivity.java32
-rw-r--r--main/src/cgeo/geocaching/Geocache.java11
-rw-r--r--main/src/cgeo/geocaching/MainActivity.java4
-rw-r--r--main/src/cgeo/geocaching/Settings.java83
-rw-r--r--main/src/cgeo/geocaching/SettingsActivity.java31
-rw-r--r--main/src/cgeo/geocaching/VisitCacheActivity.java74
-rw-r--r--main/src/cgeo/geocaching/connector/AbstractConnector.java27
-rw-r--r--main/src/cgeo/geocaching/connector/ConnectorFactory.java5
-rw-r--r--main/src/cgeo/geocaching/connector/IConnector.java41
-rw-r--r--main/src/cgeo/geocaching/connector/ILoggingManager.java32
-rw-r--r--main/src/cgeo/geocaching/connector/ImageResult.java23
-rw-r--r--main/src/cgeo/geocaching/connector/LogResult.java23
-rw-r--r--main/src/cgeo/geocaching/connector/NoLoggingManager.java46
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCConnector.java26
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java132
-rw-r--r--main/src/cgeo/geocaching/connector/oc/AttributeParser.java327
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java762
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCApiConnector.java6
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java82
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java109
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java67
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCXMLClient.java122
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OkapiClient.java371
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java69
-rw-r--r--main/src/cgeo/geocaching/enumerations/LogType.java62
-rw-r--r--main/src/cgeo/geocaching/loaders/CoordsGeocacheListLoader.java6
-rw-r--r--main/src/cgeo/geocaching/maps/CGeoMap.java2
-rw-r--r--main/src/cgeo/geocaching/network/OAuth.java16
-rw-r--r--main/src/cgeo/geocaching/network/OAuthAuthorizationActivity.java326
-rw-r--r--main/src/cgeo/geocaching/twitter/Twitter.java2
-rw-r--r--main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java299
-rw-r--r--main/templates/ocde_okapi.xml5
-rw-r--r--main/templates/private.properties7
58 files changed, 4549 insertions, 1376 deletions
diff --git a/main/AndroidManifest.xml b/main/AndroidManifest.xml
index 17fed31..95bbe8e 100644
--- a/main/AndroidManifest.xml
+++ b/main/AndroidManifest.xml
@@ -179,6 +179,24 @@
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" android:host="www.geocaching.com" android:pathPrefix="/seek/cache_details.aspx" />
</intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="http" android:host="opencaching.de" android:pathPrefix="/OC" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="http" android:host="www.opencaching.de" android:pathPrefix="/OC" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="http" android:host="www.opencaching.de" android:pathPrefix="/viewcache.php" />
+ </intent-filter>
</activity>
<activity
android:name="cgeo.geocaching.TrackableActivity"
@@ -236,5 +254,11 @@
android:name=".speech.SpeechService"
android:label="@string/tts_service" >
</service>
+ <activity
+ android:name=".connector.oc.OCAuthorizationActivity"
+ android:label="@string/app_name"
+ android:windowSoftInputMode="stateHidden"
+ android:configChanges="keyboardHidden|orientation" >
+ </activity>
</application>
</manifest>
diff --git a/main/build.xml b/main/build.xml
index febb4ae..3d611d1 100644
--- a/main/build.xml
+++ b/main/build.xml
@@ -83,6 +83,12 @@
<copy file="./templates/mapsapikey.xml" todir="./res/values/" overwrite="true">
<filterset refid="build-tokens" />
</copy>
+ <copy file="./templates/ocde_okapi.xml" todir="./res/values/" overwrite="true">
+ <filterset>
+ <filter token="ocde.okapi.consumer.key" value="${ocde.okapi.consumer.key}"/>
+ <filter token="ocde.okapi.consumer.secret" value="${ocde.okapi.consumer.secret}"/>
+ </filterset>
+ </copy>
</target>
<!--
diff --git a/main/project/attributes_okapi/AttrGen/.classpath b/main/project/attributes_okapi/AttrGen/.classpath
new file mode 100644
index 0000000..18d70f0
--- /dev/null
+++ b/main/project/attributes_okapi/AttrGen/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/main/project/attributes_okapi/AttrGen/.project b/main/project/attributes_okapi/AttrGen/.project
new file mode 100644
index 0000000..cccc238
--- /dev/null
+++ b/main/project/attributes_okapi/AttrGen/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>AttrGen</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/main/project/attributes_okapi/AttrGen/.settings/org.eclipse.jdt.core.prefs b/main/project/attributes_okapi/AttrGen/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..8000cd6
--- /dev/null
+++ b/main/project/attributes_okapi/AttrGen/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/main/project/attributes_okapi/AttrGen/.settings/org.eclipse.jdt.ui.prefs b/main/project/attributes_okapi/AttrGen/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..cc7a309
--- /dev/null
+++ b/main/project/attributes_okapi/AttrGen/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,54 @@
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=false
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=true
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=true
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=true
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/main/project/attributes_okapi/AttrGen/build.xml b/main/project/attributes_okapi/AttrGen/build.xml
new file mode 100644
index 0000000..e8426f3
--- /dev/null
+++ b/main/project/attributes_okapi/AttrGen/build.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project default="create_run_jar" name="Create Runnable Jar for Project AttrGen">
+ <!--this file was created by Eclipse Runnable JAR Export Wizard-->
+ <!--ANT 1.7 is required -->
+ <target name="create_run_jar">
+ <jar destfile="../genattr.jar" filesetmanifest="mergewithoutmain">
+ <manifest>
+ <attribute name="Main-Class" value="GenerateAttributes"/>
+ <attribute name="Class-Path" value="."/>
+ </manifest>
+ <fileset dir="bin"/>
+ </jar>
+ </target>
+</project>
diff --git a/main/project/attributes_okapi/AttrGen/src/GenerateAttributes.java b/main/project/attributes_okapi/AttrGen/src/GenerateAttributes.java
new file mode 100644
index 0000000..725dfff
--- /dev/null
+++ b/main/project/attributes_okapi/AttrGen/src/GenerateAttributes.java
@@ -0,0 +1,172 @@
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+
+public class GenerateAttributes {
+
+ /**
+ * @param args
+ */
+ public static void main(String[] args) {
+
+ File inFile = new File(args[0]);
+ InputStream inputStream;
+
+ try {
+
+ writeHeader();
+
+ inputStream = new FileInputStream(inFile);
+ Reader reader = new InputStreamReader(inputStream,"UTF-8");
+
+ InputSource is = new InputSource(reader);
+ is.setEncoding("UTF-8");
+
+ parseAttributes(is);
+
+ writeTrailer();
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void writeHeader() {
+ System.out.print(
+"// This is a generated file, do not change manually!\n" +
+"\n" +
+"package cgeo.geocaching.connector.oc;\n" +
+"\n" +
+"import java.util.HashMap;\n" +
+"import java.util.Map;\n" +
+"\n" +
+"public class AttributeParser {\n" +
+"\n" +
+" private final static Map<String, Integer> attrMapDe;\n" +
+" private final static Map<String, Integer> attrMapPl;\n" +
+"\n" +
+" static {\n" +
+" attrMapDe = new HashMap<String, Integer>();\n" +
+" attrMapPl = new HashMap<String, Integer>();\n" +
+"\n" +
+" // last header line\n");
+ }
+
+ private static void writeAttr(AttrInfo attr) {
+
+ for(String name : attr.names) {
+ if (attr.oc_de_id > 0) {
+ System.out.println(" attrMapDe.put(\"" + name + "\", " + attr.oc_de_id + ");");
+ }
+ if (attr.oc_pl_id > 0) {
+ System.out.println(" attrMapPl.put(\"" + name + "\", " + attr.oc_pl_id + ");");
+ }
+ }
+
+ }
+
+ private static void writeTrailer() {
+ System.out.print(
+" // first trailer line\n" +
+"\n" +
+" }\n" +
+"\n" +
+" public static int getOcDeId(final String name) {\n" +
+"\n" +
+" int result = 0;\n" +
+"\n" +
+" if (attrMapDe.containsKey(name)) {\n" +
+" result = attrMapDe.get(name);\n" +
+" }\n" +
+" return result;\n" +
+" }\n" +
+"}\n");
+ }
+
+ private static void parseAttributes(InputSource stream) {
+
+ try {
+
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ SAXParser saxParser = factory.newSAXParser();
+
+ DefaultHandler handler = new DefaultHandler() {
+
+ AttrInfo attr;
+ ArrayList<String> names;
+ boolean readingName;
+
+ public void startElement(String uri, String localName,
+ String qName, Attributes attributes)
+ throws SAXException {
+
+ if (qName.equalsIgnoreCase("attr")) {
+ attr = new AttrInfo();
+ names = new ArrayList<String>();
+ }
+
+ if (attr != null && qName.equalsIgnoreCase("opencaching")) {
+ if ("http://opencaching.de/".equalsIgnoreCase(attributes.getValue("site_url"))) {
+ attr.oc_de_id = Integer.parseInt(attributes.getValue("id"));
+ } else if ("http://opencaching.pl/".equalsIgnoreCase(attributes.getValue("site_url"))) {
+ attr.oc_pl_id = Integer.parseInt(attributes.getValue("id"));
+ }
+ }
+
+ if (names != null && qName.equalsIgnoreCase("name")) {
+ readingName = true;
+ }
+ }
+
+ public void endElement(String uri, String localName,
+ String qName)
+ throws SAXException {
+
+ if (attr != null && qName.equalsIgnoreCase("attr")) {
+ attr.names = names.toArray(new String[]{});
+ names = null;
+ writeAttr(attr);
+ attr = null;
+ }
+
+ readingName = false;
+ }
+
+ public void characters(char ch[], int start, int length)
+ throws SAXException {
+
+ if (readingName) {
+ names.add(new String(ch, start, length));
+ }
+ }
+
+ };
+
+ saxParser.parse(stream, handler);
+
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ static class AttrInfo {
+ public int oc_de_id;
+ public int oc_pl_id;
+ public String[] names;
+ }
+
+}
diff --git a/main/project/attributes_okapi/AttributeParser.java b/main/project/attributes_okapi/AttributeParser.java
new file mode 100644
index 0000000..63bee77
--- /dev/null
+++ b/main/project/attributes_okapi/AttributeParser.java
@@ -0,0 +1,327 @@
+// This is a generated file, do not change manually!
+
+package cgeo.geocaching.connector.oc;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class AttributeParser {
+
+ private final static Map<String, Integer> attrMapDe;
+ private final static Map<String, Integer> attrMapPl;
+
+ static {
+ attrMapDe = new HashMap<String, Integer>();
+ attrMapPl = new HashMap<String, Integer>();
+
+ // last header line
+ attrMapDe.put("Listed at Opencaching only", 6);
+ attrMapDe.put("Dostępna tylko na Opencaching", 6);
+ attrMapDe.put("Nur bei Opencaching logbar", 6);
+ attrMapDe.put("Solo loggeable en Opencaching", 6);
+ attrMapDe.put("Loggabile solo su Opencaching", 6);
+ attrMapPl.put("Near a Survey Marker", 54);
+ attrMapPl.put("W pobliżu punktu geodezyjnego", 54);
+ attrMapPl.put("Whereigo Cache", 55);
+ attrMapPl.put("Whereigo Cache", 55);
+ attrMapPl.put("Whereigo Cache", 55);
+ attrMapDe.put("Letterbox Cache", 8);
+ attrMapPl.put("Letterbox Cache", 56);
+ attrMapDe.put("Skrzynka typu Letterbox", 8);
+ attrMapPl.put("Skrzynka typu Letterbox", 56);
+ attrMapDe.put("Letterbox (benötigt Stempel)", 8);
+ attrMapPl.put("Letterbox (benötigt Stempel)", 56);
+ attrMapDe.put("Letterbox (necesita un estampador)", 8);
+ attrMapPl.put("Letterbox (necesita un estampador)", 56);
+ attrMapDe.put("Letterbox (richiede un timbro)", 8);
+ attrMapPl.put("Letterbox (richiede un timbro)", 56);
+ attrMapPl.put("GeoHotel", 43);
+ attrMapPl.put("GeoHotel", 43);
+ attrMapPl.put("GeoHotel", 43);
+ attrMapPl.put("Magnetic cache", 49);
+ attrMapPl.put("Przyczepiona magnesem", 49);
+ attrMapPl.put("magnetischer Cache", 49);
+ attrMapPl.put("Description contains an audio file", 50);
+ attrMapPl.put("Opis zawiera plik audio", 50);
+ attrMapPl.put("Offset cache", 51);
+ attrMapPl.put("Offset cache", 51);
+ attrMapPl.put("Peilungscache", 51);
+ attrMapPl.put("Garmin's wireless beacon", 52);
+ attrMapPl.put("Beacon - Garmin Chirp", 52);
+ attrMapPl.put("Funksignal – Garmin Chirp", 52);
+ attrMapPl.put("Dead Drop USB cache", 53);
+ attrMapPl.put("Dead Drop USB skrzynka", 53);
+ attrMapDe.put("Has a moving target", 31);
+ attrMapDe.put("bewegliches Ziel", 31);
+ attrMapDe.put("Objetivo en movimiento", 31);
+ attrMapDe.put("Oggetto in movimento", 31);
+ attrMapDe.put("Webcam Cache", 32);
+ attrMapDe.put("Webcam Cache", 32);
+ attrMapDe.put("Webcam Cache", 32);
+ attrMapDe.put("Webcam Cache", 32);
+ attrMapDe.put("Other cache type", 57);
+ attrMapDe.put("sonstiger Cachetyp", 57);
+ attrMapDe.put("Otro tipo de cache", 57);
+ attrMapDe.put("Altro tipo di cache", 57);
+ attrMapDe.put("Investigation required", 54);
+ attrMapDe.put("Recherche", 54);
+ attrMapDe.put("Investigación", 54);
+ attrMapDe.put("Ricerca", 54);
+ attrMapDe.put("Puzzle / Mystery", 55);
+ attrMapDe.put("Rätsel", 55);
+ attrMapDe.put("Puzzle / Misterio", 55);
+ attrMapDe.put("Puzzle / Mystery", 55);
+ attrMapDe.put("Arithmetical problem", 56);
+ attrMapDe.put("Rechenaufgabe", 56);
+ attrMapDe.put("Problema matemático", 56);
+ attrMapDe.put("Problema matematico", 56);
+ attrMapDe.put("Ask owner for start conditions", 58);
+ attrMapDe.put("Startbedingungen beim Owner erfragen", 58);
+ attrMapDe.put("Ask owner for start conditions", 58);
+ attrMapDe.put("Ask owner for start conditions", 58);
+ attrMapPl.put("Wheelchair accessible", 44);
+ attrMapPl.put("Dostępna dla niepełnosprawnych", 44);
+ attrMapPl.put("rollstuhltauglich", 44);
+ attrMapDe.put("Near the parking area", 24);
+ attrMapDe.put("nahe beim Auto", 24);
+ attrMapDe.put("Cerca de un Parking", 24);
+ attrMapDe.put("Vicino all'area di parcheggio", 24);
+ attrMapPl.put("Access only by walk", 84);
+ attrMapPl.put("Dostępna tylko pieszo", 84);
+ attrMapDe.put("Long walk", 25);
+ attrMapDe.put("längere Wanderung", 25);
+ attrMapDe.put("Larga caminata", 25);
+ attrMapDe.put("Lunga camminata", 25);
+ attrMapDe.put("Swamp, marsh or wading", 26);
+ attrMapDe.put("sumpfig/matschiges Gelände / waten", 26);
+ attrMapDe.put("Pantano / terreno fangoso", 26);
+ attrMapDe.put("Palude o marcita", 26);
+ attrMapDe.put("Hilly area", 27);
+ attrMapDe.put("hügeliges Gelände", 27);
+ attrMapDe.put("Terreno montañoso", 27);
+ attrMapDe.put("Area collinare", 27);
+ attrMapDe.put("Some climbing (no gear needed)", 28);
+ attrMapDe.put("leichtes Klettern (ohne Ausrüstung)", 28);
+ attrMapDe.put("fácil de subir (sin equipo)", 28);
+ attrMapDe.put("Arrampicata (attrezzatura non necessaria)", 28);
+ attrMapDe.put("Swimming required", 29);
+ attrMapDe.put("Schwimmen erforderlich", 29);
+ attrMapDe.put("Requiere nadar", 29);
+ attrMapDe.put("Nuoto necessario", 29);
+ attrMapDe.put("Access or parking fee", 36);
+ attrMapDe.put("Zugangs- bzw. Parkentgelt", 36);
+ attrMapDe.put("Acceso o parking pagando", 36);
+ attrMapDe.put("Tassa di ingresso o di parcheggio", 36);
+ attrMapPl.put("Bikes allowed", 85);
+ attrMapPl.put("Dostępna rowerem", 85);
+ attrMapPl.put("Hidden in natural surroundings (forests, mountains, etc.)", 60);
+ attrMapPl.put("Umiejscowiona na łonie natury (lasy, góry, itp.)", 60);
+ attrMapPl.put("Historic site", 61);
+ attrMapPl.put("Miejsce historyczne", 61);
+ attrMapDe.put("Point of interest", 30);
+ attrMapDe.put("interessanter Ort", 30);
+ attrMapDe.put("Punto de interes", 30);
+ attrMapDe.put("Punto di interesse", 30);
+ attrMapDe.put("Hidden wihin enclosed rooms (caves, buildings etc.)", 33);
+ attrMapDe.put("in geschlossenen Räumen (Höhle, Gebäude, etc.)", 33);
+ attrMapDe.put("en espacios confinados (cuevas, edificios, etc)", 33);
+ attrMapDe.put("All'interno di stanze chiuse (caverne, edifici, ecc.)", 33);
+ attrMapDe.put("Hidden under water", 34);
+ attrMapDe.put("Im Wasser versteckt", 34);
+ attrMapDe.put("En el agua", 34);
+ attrMapDe.put("Nell'acqua", 34);
+ attrMapDe.put("Parking area nearby", 18);
+ attrMapDe.put("Parkplatz in der Nähe", 18);
+ attrMapDe.put("Parking cercano", 18);
+ attrMapDe.put("Parcheggio nei pressi", 18);
+ attrMapDe.put("Public transportation", 19);
+ attrMapDe.put("erreichbar mit ÖVM", 19);
+ attrMapDe.put("Transporte Público", 19);
+ attrMapDe.put("Trasporto pubblico", 19);
+ attrMapDe.put("Drinking water nearby", 20);
+ attrMapDe.put("Trinkwasser in der Nähe", 20);
+ attrMapDe.put("Agua potable en las cercanias", 20);
+ attrMapDe.put("Acqua potabile nei pressi", 20);
+ attrMapDe.put("Public restrooms nearby", 21);
+ attrMapDe.put("öffentliche Toilette in der Nähe", 21);
+ attrMapDe.put("Aseos públicos cercanos", 21);
+ attrMapDe.put("Bagni pubblici nei pressi", 21);
+ attrMapDe.put("Public phone nearby", 22);
+ attrMapDe.put("Telefon in der Nähe", 22);
+ attrMapDe.put("Teléfono Público en las cercanias", 22);
+ attrMapDe.put("Telefono pubblico nei pressi", 22);
+ attrMapDe.put("First aid available", 23);
+ attrMapDe.put("Erste Hilfe verfügbar", 23);
+ attrMapDe.put("Disponible socorro rapido", 23);
+ attrMapDe.put("Disponibile pronto soccorso", 23);
+ attrMapDe.put("Available 24/7", 38);
+ attrMapDe.put("rund um die Uhr machbar", 38);
+ attrMapDe.put("Disponible las 24 horas", 38);
+ attrMapDe.put("Disponibile 24 ore", 38);
+ attrMapDe.put("Not 24/7", 39);
+ attrMapPl.put("Not 24/7", 80);
+ attrMapDe.put("Dostępna w określonych godzinach", 39);
+ attrMapPl.put("Dostępna w określonych godzinach", 80);
+ attrMapDe.put("nur zu bestimmten Uhrzeiten", 39);
+ attrMapPl.put("nur zu bestimmten Uhrzeiten", 80);
+ attrMapDe.put("Sólo disponible a ciertas horas", 39);
+ attrMapPl.put("Sólo disponible a ciertas horas", 80);
+ attrMapDe.put("Disponibile solo in certi orari", 39);
+ attrMapPl.put("Disponibile solo in certi orari", 80);
+ attrMapDe.put("Not recommended at night", 40);
+ attrMapDe.put("nur tagüber", 40);
+ attrMapDe.put("solo por el día", 40);
+ attrMapDe.put("solo di giorno", 40);
+ attrMapPl.put("Recommended at night", 91);
+ attrMapPl.put("Zalecane szukanie nocą", 91);
+ attrMapPl.put("am besten nachts findbar", 91);
+ attrMapDe.put("Only at night", 1);
+ attrMapDe.put("nur bei Nacht", 1);
+ attrMapDe.put("Sólo por la noche", 1);
+ attrMapDe.put("Solo di notte", 1);
+ attrMapDe.put("All seasons", 42);
+ attrMapDe.put("ganzjähig zugänglich", 42);
+ attrMapDe.put("Todas las temporadas", 42);
+ attrMapDe.put("Tutte le stagioni", 42);
+ attrMapDe.put("Only available during specified seasons", 60);
+ attrMapDe.put("Nur zu bestimmten Zeiten im Jahr", 60);
+ attrMapDe.put("Sólo disponible durante las estaciones especificadas", 60);
+ attrMapDe.put("Disponibile solo in certe stagioni", 60);
+ attrMapDe.put("Breeding season / protected nature", 43);
+ attrMapDe.put("Brutsaison / Naturschutz", 43);
+ attrMapDe.put("Temporada de reproducción / protección de la naturaleza", 43);
+ attrMapDe.put("Stagione di riproduzione / natura protetta", 43);
+ attrMapDe.put("Available during winter", 44);
+ attrMapDe.put("schneesicheres Versteck", 44);
+ attrMapDe.put("Nieve en el escondite", 44);
+ attrMapDe.put("Luogo a prova di neve", 44);
+ attrMapDe.put("Not at high water level", 41);
+ attrMapDe.put("nicht bei Hochwasser oder Flut", 41);
+ attrMapDe.put("Compass required", 47);
+ attrMapPl.put("Compass required", 47);
+ attrMapDe.put("Potrzebny kompas", 47);
+ attrMapPl.put("Potrzebny kompas", 47);
+ attrMapDe.put("Kompass", 47);
+ attrMapPl.put("Kompass", 47);
+ attrMapDe.put("Brújula", 47);
+ attrMapPl.put("Brújula", 47);
+ attrMapDe.put("Bussola", 47);
+ attrMapPl.put("Bussola", 47);
+ attrMapPl.put("Take something to write", 48);
+ attrMapPl.put("Weź coś do pisania", 48);
+ attrMapPl.put("You may need a shovel", 81);
+ attrMapPl.put("Potrzebna łopatka", 81);
+ attrMapDe.put("Flashlight required", 48);
+ attrMapPl.put("Flashlight required", 82);
+ attrMapDe.put("Potrzebna latarka", 48);
+ attrMapPl.put("Potrzebna latarka", 82);
+ attrMapDe.put("Taschenlampe", 48);
+ attrMapPl.put("Taschenlampe", 82);
+ attrMapDe.put("Linterna", 48);
+ attrMapPl.put("Linterna", 82);
+ attrMapDe.put("Lampada tascabile", 48);
+ attrMapPl.put("Lampada tascabile", 82);
+ attrMapDe.put("Climbing gear required", 49);
+ attrMapDe.put("Kletterzeug", 49);
+ attrMapDe.put("Equipo de escalada", 49);
+ attrMapDe.put("Attrezzatura per arrampicata", 49);
+ attrMapDe.put("Cave equipment required", 50);
+ attrMapDe.put("Höhlenzeug", 50);
+ attrMapDe.put("Equipación para cuevas", 50);
+ attrMapDe.put("Attrezzatura per grotta", 50);
+ attrMapDe.put("Diving equipment required", 51);
+ attrMapDe.put("Taucherausrüstung", 51);
+ attrMapDe.put("Diving equipment", 51);
+ attrMapDe.put("Equipo de buceo", 51);
+ attrMapDe.put("Special tools required", 46);
+ attrMapPl.put("Special tools required", 83);
+ attrMapDe.put("Wymagany dodatkowy sprzęt", 46);
+ attrMapPl.put("Wymagany dodatkowy sprzęt", 83);
+ attrMapDe.put("spezielle Ausrüstung", 46);
+ attrMapPl.put("spezielle Ausrüstung", 83);
+ attrMapDe.put("Equipamiento especial", 46);
+ attrMapPl.put("Equipamiento especial", 83);
+ attrMapDe.put("Equipaggiamento speciale", 46);
+ attrMapPl.put("Equipaggiamento speciale", 83);
+ attrMapDe.put("Requires a boat", 52);
+ attrMapPl.put("Requires a boat", 86);
+ attrMapDe.put("Wymaga sprzętu pływającego", 52);
+ attrMapPl.put("Wymaga sprzętu pływającego", 86);
+ attrMapDe.put("Wasserfahrzeug", 52);
+ attrMapPl.put("Wasserfahrzeug", 86);
+ attrMapDe.put("Barca", 52);
+ attrMapPl.put("Barca", 86);
+ attrMapDe.put("Barca", 52);
+ attrMapPl.put("Barca", 86);
+ attrMapDe.put("No GPS required", 35);
+ attrMapDe.put("ohne GPS findbar", 35);
+ attrMapDe.put("Sin GPS", 35);
+ attrMapDe.put("Senza GPS", 35);
+ attrMapDe.put("Dangerous area", 9);
+ attrMapPl.put("Dangerous area", 90);
+ attrMapDe.put("Skrzynka niebezpieczna", 9);
+ attrMapPl.put("Skrzynka niebezpieczna", 90);
+ attrMapDe.put("gefährliches Gebiet", 9);
+ attrMapPl.put("gefährliches Gebiet", 90);
+ attrMapDe.put("Zona Peligrosa", 9);
+ attrMapPl.put("Zona Peligrosa", 90);
+ attrMapDe.put("Area pericolosa", 9);
+ attrMapPl.put("Area pericolosa", 90);
+ attrMapDe.put("Active railway nearby", 10);
+ attrMapDe.put("aktive Eisenbahnlinie in der Nähe", 10);
+ attrMapDe.put("Cerca del ferrocarril activo", 10);
+ attrMapDe.put("Ferrovia attiva nei pressi", 10);
+ attrMapDe.put("Cliff / Rocks", 11);
+ attrMapDe.put("Klippen / Felsen", 11);
+ attrMapDe.put("Acantilado / Rocas", 11);
+ attrMapDe.put("Scogliera / Rocce", 11);
+ attrMapDe.put("Hunting", 12);
+ attrMapDe.put("Jagdgebiet", 12);
+ attrMapDe.put("Zona de Caza", 12);
+ attrMapDe.put("Caccia", 12);
+ attrMapDe.put("Thorns", 13);
+ attrMapDe.put("Dornen", 13);
+ attrMapDe.put("Espinas", 13);
+ attrMapDe.put("Spine", 13);
+ attrMapDe.put("Ticks", 14);
+ attrMapDe.put("Zecken", 14);
+ attrMapDe.put("Garrapatas", 14);
+ attrMapDe.put("Zecche", 14);
+ attrMapDe.put("Abandoned mines", 15);
+ attrMapDe.put("Folgen des Bergbaus", 15);
+ attrMapDe.put("Mina abandonada", 15);
+ attrMapDe.put("Miniere abbandonate", 15);
+ attrMapDe.put("Poisonous plants", 16);
+ attrMapDe.put("giftige Pflanzen", 16);
+ attrMapDe.put("Planta venenosa", 16);
+ attrMapDe.put("Piante velenose", 16);
+ attrMapDe.put("Dangerous animals", 17);
+ attrMapDe.put("giftige/gefährliche Tiere", 17);
+ attrMapDe.put("Animales Peligrosos", 17);
+ attrMapDe.put("Animali pericolosi", 17);
+ attrMapPl.put("Quick cache", 40);
+ attrMapPl.put("Szybka skrzynka", 40);
+ attrMapDe.put("Overnight stay necessary", 37);
+ attrMapDe.put("Übernachtung erforderlich", 37);
+ attrMapDe.put("Necesario pernoctar", 37);
+ attrMapDe.put("Necessario pernottamento", 37);
+ attrMapPl.put("Take your children", 41);
+ attrMapPl.put("Można zabrać dzieci", 41);
+ attrMapDe.put("Suited for children (10-12 yo)", 59);
+ attrMapDe.put("kindgerecht (10-12 Jahre)", 59);
+ attrMapDe.put("Apto para niños (10-12 años)", 59);
+ attrMapDe.put("Suited for children (10-12 anni)", 59);
+ // first trailer line
+
+ }
+
+ public static int getOcDeId(final String name) {
+
+ int result = 0;
+
+ if (attrMapDe.containsKey(name)) {
+ result = attrMapDe.get(name);
+ }
+ return result;
+ }
+}
diff --git a/main/project/attributes_okapi/attributes.xml b/main/project/attributes_okapi/attributes.xml
new file mode 100644
index 0000000..ede81a8
--- /dev/null
+++ b/main/project/attributes_okapi/attributes.xml
@@ -0,0 +1,1940 @@
+<!--
+
+Current status: WORKING DRAFT
+
+This is the list of all geocache attributes supported by OKAPI. In
+practise, it will usually include any atribute used by at least one of
+the Opencaching installations.
+
+
+NOTES FOR EXTERNAL APP DEVELOPERS
+=================================
+
+DO NOT read or parse this file in your apps! It is NOT guaranteed to stay
+backward-compatible. Use public OKAPI methods instead.
+
+
+NOTES FOR OC/OKAPI DEVELOPERS
+=============================
+
+Every OC installation has its own set of internal attributes. This file
+allows them to be merged with other attributes from other OC installations.
+It is periodically read by every OKAPI installation, *directly from
+the repository*.
+
+This file defines the mapping between:
+
+1. Internal attribute IDs of various Opencaching nodes.
+2. Groundspeak's attribute IDs (used by geocaching.com).
+3. OKAPI's attribute IDs.
+
+Important: This is a verbose, many-to-many, optional relationship! Crow's foot
+notation: http://i.imgur.com/cNGz1xt.png. All those attributes may appear not
+very useful when provided this way, but there is nothing OKAPI can do about it.
+We leave the UI clarity problem for app developers.
+
+-->
+
+<xml>
+
+ <!-- Language forms - status: just an idea, unimplemented (see form=""
+ attributes in <attr> elements). TODO: fill missing form="" attributes. -->
+
+ <form id="is / not is">
+ <prefix lang="en">
+ <include><b>Include</b> the cache, only if it is:</include>
+ <exclude><b>Exclude</b> the cache if it is:</exclude>
+ </prefix>
+ <prefix lang="pl">
+ <include><b>Uwzględnij</b> skrzynkę, tylko jeśli jest:</include>
+ <exclude><b>Pomiń</b> skrzynkę, jeśli jest:</exclude>
+ </prefix>
+ </form>
+
+ <form id="contains / does not contain">
+ <prefix lang="en">
+ <include><b>Include</b> the cache, only if it contains:</include>
+ <exclude><b>Exclude</b> the cache if it contains:</exclude>
+ </prefix>
+ <prefix lang="pl">
+ <include><b>Uwzględnij</b> skrzynkę, tylko jeśli zawiera:</include>
+ <exclude><b>Pomiń</b> skrzynkę, jeśli nie zawiera:</exclude>
+ </prefix>
+ </form>
+
+ <form id="allowed / not allowed">
+ <prefix lang="en">
+ <include>The following <b>must be allowed</b>:</include>
+ <exclude>The following <b>may not be allowed</b>:</exclude>
+ </prefix>
+ </form>
+
+ <form id="required / not required">
+ <prefix lang="en">
+ <include>The following <b>must be required</b>:</include>
+ <exclude>The following <b>may not be required</b>:</exclude>
+ </prefix>
+ </form>
+
+ <form id="if / if not">
+ <prefix lang="en">
+ <include>The following <b>must be true</b>:</include>
+ <exclude>The following <b>may not be true</b>:</exclude>
+ </prefix>
+ </form>
+
+ <!--
+
+ NOTICE: Categories are currently NOT implemented. In particular, the
+ attr[categories] is ignored. See issue 70.
+
+ <category id="de-cache-types">
+ <name lang="en">TODO</name>
+ </category>
+
+ <category id="de-location">
+ <name lang="en">TODO</name>
+ </category>
+
+ <category id="de-facilities">
+ <name lang="en">TODO</name>
+ </category>
+
+ <category id="de-time-and-seasons">
+ <name lang="en">TODO</name>
+ </category>
+
+ <category id="de-tools">
+ <name lang="en">TODO</name>
+ </category>
+
+ <category id="de-dangers">
+ <name lang="en">TODO</name>
+ </category>
+
+ <category id="de-rating">
+ <name lang="en">TODO</name>
+ </category>
+
+ <category id="de-preconditions">
+ <name lang="en">TODO</name>
+ </category>
+
+ <category id="de-accessibility">
+ <name lang="en">TODO</name>
+ </category>
+
+ -->
+
+ <attr okapi_attr_id="oconly" categories="de-listing" form="is / is not">
+ <opencaching site_url="http://opencaching.de/" id="6" />
+ <name lang="en">Listed at Opencaching only</name>
+ <desc lang="en">
+ This geocache is listed at Opencaching only.
+ </desc>
+ <name lang="pl">Dostępna tylko na Opencaching</name>
+ <desc lang="pl">
+ Skrzynka "jednosystemowa", dostępna jedynie poprzez serwis Opencaching.
+ </desc>
+ <name lang="de">Nur bei Opencaching logbar</name>
+ <desc lang="de">
+ Der Geocache ist nur bei Opencaching gelistet. Benutzer anderer
+ Geocache-Datenbanken haben so einen schnellen Überblick, welche Geocaches
+ es sich lohnt näher anzusehen.
+ </desc>
+ <name lang="es">Solo loggeable en Opencaching</name>
+ <desc lang="es">
+ Este geocachee esta publicado sólo en Opencaching. Este atributo permite
+ a los usuarios de otras plataformas encontrar rápidamente caches
+ interesantes de calidad OC geocaching.
+ </desc>
+ <name lang="it">Loggabile solo su Opencaching</name>
+ <desc lang="it">
+ Questa geocache è pubblicata solo su Opencaching. Questo attributo permette
+ agli utenti di altre piattaforme di geocaching di trovare velocemente
+ interessanti cache OC di qualità.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="near-survey-marker" categories="de-cache-types" form="is / is not">
+ <opencaching site_url="http://opencaching.pl/" id="54" />
+ <name lang="en">Near a Survey Marker</name>
+ <desc lang="en">
+ The cache is hidden in near proximity of a survey marker (also known
+ as geodetic marks).
+ </desc>
+ <name lang="pl">W pobliżu punktu geodezyjnego</name>
+ <desc lang="pl">
+ Skrzynka ukryta w pobliżu punktu geodezyjnego.
+ <a href='http://wiki.opencaching.pl/index.php/Benchmark'>Więcej informacji</a>.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="wherigo" categories="de-cache-types" form="is / is not">
+ <opencaching site_url="http://opencaching.pl/" id="55" />
+ <name lang="en">Whereigo Cache</name>
+ <desc lang="en">
+ Cache description includes a file - the Whereigo cartridge. In order to
+ find the cache, you need to download the file and install it on
+ a proper compatible device.
+ </desc>
+ <name lang="pl">Whereigo Cache</name>
+ <desc lang="pl">
+ Opis skrzynki zawiera scenariusz WIGO (ang. Whereigo). Aby móc zdobyć skrzynkę
+ należy pobrać scenariusz i wgrać go do kompatybilnego z nim urządzenia.
+ <a href='http://wiki.opencaching.pl/index.php/Wherigo_czyli_Scenariusze_WIGO'>Więcej informacji</a>.
+ </desc>
+ <name lang="de">Whereigo Cache</name>
+ </attr>
+
+ <attr okapi_attr_id="letterbox" categories="de-cache-types" form="is / is not">
+ <opencaching site_url="http://opencaching.pl/" id="56" />
+ <opencaching site_url="http://opencaching.de/" id="8" />
+ <name lang="en">Letterbox Cache</name>
+ <desc lang="en">
+ There is a stamp in the cache for stamping your personal logbook, and the
+ cache’s logbook will be stamped with your personal stamp. Take care not
+ to mix up stamps and to leave the cache’s stamp in the cache!
+ </desc>
+ <name lang="pl">Skrzynka typu Letterbox</name>
+ <desc lang="pl">
+ W skrzynce znajduje się pieczątka, której nie można zabrać ze sobą.
+ Możesz jej użyć do ostemplowania swojego osobistego dziennika.
+ Logbook skrzynki powinien z kolei zostać ostemplowany Twoją własną
+ pieczątką.
+ <a href='http://wiki.opencaching.pl/index.php/Letterboxing'>Więcej informacji</a>.
+ </desc>
+ <name lang="de">Letterbox (benötigt Stempel)</name>
+ <desc lang="de">
+ In dem Behälter vor Ort befindet sich ein Stempel, mit dem man sein
+ persönliches Logbuch abstempeln kann. Das Logbuch im Geocache wird
+ ebenfalls mit einem persönlichen Stempel signiert. Bitte achte unbedingt
+ darauf, dass du den Stempel aus dem Geocache nicht mitnimmst oder tauschst!
+ <a href='http://wiki.opencaching.de/index.php/Letterboxing'>Weitere Informationen</a>.
+ </desc>
+ <name lang="es">Letterbox (necesita un estampador)</name>
+ <desc lang="es">
+ Hay un sello en el cache para estamparlo en su libro de registro personal,
+ y el libro de registro del cache será sellada con su sello personal.
+ ¡Tenga cuidado de no mezclar los sellos!
+ </desc>
+ <name lang="it">Letterbox (richiede un timbro)</name>
+ <desc lang="it">
+ C'è un timbro nella cache per timbrare il tuo quaderno personale,
+ e il log della cache verrà timbrato con il tuo timbro personale.
+ Fate attenzione a non confondere i timbri e a lasciare il timbro della
+ cache nella cache!
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="geohotel" categories="de-cache-types" form="is / is not">
+ <opencaching site_url="http://opencaching.pl/" id="43" />
+ <name lang="en">GeoHotel</name>
+ <desc lang="en">
+ Primary purpose of the "GeoHotel" caches is to exchange trackables
+ (TravelBugs, GeoKretys, etc.).
+ </desc>
+ <name lang="pl">GeoHotel</name>
+ <desc lang="pl">
+ Atrybut ten oznacza skrzynki których głównym celem jest lokowanie
+ w tej skrzynce różnych wędrujących rzeczy np GeoKrety, GeoLutins,
+ GeoFish itp.
+ </desc>
+ <name lang="de">GeoHotel</name>
+ </attr>
+
+ <attr okapi_attr_id="magnet" categories="de-cache-types" form="is / is not">
+ <opencaching site_url="http://opencaching.pl/" id="49" />
+ <name lang="en">Magnetic cache</name>
+ <desc lang="en">
+ This geocache is attached with a magnet.
+ </desc>
+ <name lang="pl">Przyczepiona magnesem</name>
+ <desc lang="pl">
+ Skrzynka zawiera magnes i przymocowana jest za jego pomocą.
+ </desc>
+ <name lang="de">magnetischer Cache</name>
+ </attr>
+
+ <attr okapi_attr_id="audiofile" categories="de-cache-types" form="if / if not">
+ <opencaching site_url="http://opencaching.pl/" id="50" />
+ <name lang="en">Description contains an audio file</name>
+ <desc lang="pl">
+ In order to find this cache, you must listen to an audio recording,
+ which is attached to the cache description.
+ </desc>
+ <name lang="pl">Opis zawiera plik audio</name>
+ <desc lang="pl">
+ Aby odnaleźć skrzynkę, należy odsłuchać plik dźwiękowy. Może on
+ zawierać zakodowane informacje, dźwięki otoczenia, opis dotarcia
+ do skrzynki.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="offset" categories="de-cache-types" form="is / is not">
+ <opencaching site_url="http://opencaching.pl/" id="51" />
+ <name lang="en">Offset cache</name>
+ <desc lang="pl">
+ A specific type of a MultiCache. The coordinates point to a starting
+ point. The description contains simple instructions to follow
+ once you are in the starting point (usually, an azimuth and a
+ distance).
+ </desc>
+ <name lang="pl">Offset cache</name>
+ <desc lang="pl">
+ Szczególny przypadek skrzynki typu multicache, składający się z
+ punktu startowego (określonego współrzędnymi) oraz jasnych
+ informacjami o sposobie dotarcia do finału (np. azymutu i
+ odległości).
+ </desc>
+ <name lang="de">Peilungscache</name>
+ </attr>
+
+ <attr okapi_attr_id="chirp" categories="de-cache-types" form="contains / does not contain">
+ <groundspeak id="60" inc="true" name="Wireless Beacon" />
+ <opencaching site_url="http://opencaching.pl/" id="52" />
+ <name lang="en">Garmin's wireless beacon</name>
+ <desc lang="en">
+ Contains Garmin's wireless chirp beacon.
+ </desc>
+ <name lang="pl">Beacon - Garmin Chirp</name>
+ <desc lang="pl">
+ Skrzynka zawiera Beacon Garmin chirp.
+ </desc>
+ <name lang="de">Funksignal – Garmin Chirp</name>
+ </attr>
+
+ <attr okapi_attr_id="usb" categories="de-cache-types" form="is / is not">
+ <opencaching site_url="http://opencaching.pl/" id="53" />
+ <name lang="en">Dead Drop USB cache</name>
+ <desc lang="pl">
+ The cache consists of an unmovable USB mass storage device, e.g.
+ fixed into a wall, curb etc. The device contains readme.txt file
+ with cache description and a logbook.txt file where you can log
+ your visit.
+ </desc>
+ <name lang="pl">Dead Drop USB skrzynka</name>
+ <desc lang="pl">
+ Skrzynka typu USB Dead Drop. Pen-drive przymocowany lub wmurowany
+ w ścianę, budynek, krawiężnik itp. Aby zalogować znalezienie, należy
+ wpisać się do pliku logbook.txt znajdującego się w pamięci urządzenia.
+ <a href='http://wiki.opencaching.pl/index.php/Skrzynka_Dead_Drop'>Wiecej informacji</a>.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="moving" categories="de-cache-types" form="if / if not">
+ <!-- This looks redundant to the 'moving cache' type, but it may also be a
+ moving final of a multi- or quiz cache. -->
+ <opencaching site_url="http://opencaching.de/" id="31" />
+ <name lang="en">Has a moving target</name>
+ <desc lang="en">
+ This geocache is moving around. For example, the owner might regularly
+ move the cache from one place to another, or the finders will do this
+ task and post new coordinates in their log entries. The owner must
+ update coordinates in the cache description after each move.
+ </desc>
+ <!-- TODO: "Has a" was added. -->
+ <name lang="de">bewegliches Ziel</name>
+ <desc lang="de">
+ Der Geocache verändert seine Position und ist deshalb nicht immer am
+ gleichen Ort zu finden. Es gibt Varianten, bei denen der Geocache-Besitzer
+ den Geocache regelmäßig an anderen Orten versteckt, oder der Finder den
+ Geocache an einem neuen Ort versteckt. Danach muss der Besitzer jeweils
+ die Beschreibung aktualisieren.
+ </desc>
+ <name lang="es">Objetivo en movimiento</name>
+ <desc lang="es">
+ Este geocache está en movimiento. Por ejemplo, el propietario podía mover
+ la caché periódicamente de un lugar a otro, o un geobuscador podría hacer
+ esto y ofrecer nuevos detalles en su registro. El propietario debe
+ actualizar las coordenadas en la descripción del cache después de cada
+ movimiento.
+ </desc>
+ <name lang="it">Oggetto in movimento</name>
+ <desc lang="it">
+ Questa geocache è in muovimento. Per esempio, il proprietario potrebbe
+ spostare regolarmente la cache da un luogo ad un altro, o i cacher
+ potrebbero eseguire questa operazione e indicare nuove coordinate nel
+ loro log. Il proprietario deve aggiornare le coordinate nella descrizione
+ della cache dopo ogni mossa.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="webcam" categories="de-cache-types" form="is / is not">
+ <opencaching site_url="http://opencaching.de/" id="32" />
+ <name lang="en">Webcam Cache</name>
+ <desc lang="en">
+ There is a webcam at the target location. You must record a webcam
+ picture of your visit and include it in your 'found' log entry. There
+ may be additional requirements like a geocaching banner on the photo.
+ The webcam’s address is included in the cache description.
+ </desc>
+ <name lang="de">Webcam Cache</name>
+ <desc lang="de">
+ Am Ziel befindet sich eine Webcam, und für einen Fund muss man das Bild
+ der Webcam von sich selbst aufnehmen, um nachzuweisen, dass man vor Ort
+ war. Manche Webcam-Caches setzen auch weitere Bedingungen, z.B. einen
+ Geocaching-Banner auf dem Bild. Die Webadresse der Webcam ist in der
+ Beschreibung angegeben.
+ </desc>
+ <name lang="es">Webcam Cache</name>
+ <desc lang="es">
+ Hay una webcam en el lugar de destino. necesidad a alguien para registrar
+ con un pantallazo de la imagen de la webcam de su visita e incluirlo en
+ el registro. Puede haber requisitos adicionales como un signo de
+ geocaching en la imagen. La dirección de la webcam está incluido en la
+ descripción de la cache.
+ </desc>
+ <name lang="it">Webcam Cache</name>
+ <desc lang="it">
+ C'è una webcam nella posizione di destinazione. E' necessario registrare
+ una foto con la webcam della vostra visita e includerla nel log. Ci
+ possono essere requisiti addizionali come un segnale di geocaching sulla
+ foto. L'indirizzo della webcam è incluso nella descrizione della cache.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="other-type" categories="de-cache-types">
+ <!--
+ This looks redundant to the "unknown type" cache type, but
+ it is used vor special variants of basic cache types - e.g. a
+ multicache where you won't just find a box at the final but have
+ to meet an additional challenge there.
+ -->
+ <opencaching site_url="http://opencaching.de/" id="57" />
+ <name lang="en">Other cache type</name>
+ <desc lang="en">
+ This is none of the standard, pre-defined types of cache.
+ Use this attribute for special, unusual caches.
+ </desc>
+ <name lang="de">sonstiger Cachetyp</name>
+ <desc lang="de">
+ Dieser Cache passt in keine der üblichen Kategorien von Caches (Cachearten).
+ </desc>
+ <name lang="es">Otro tipo de cache</name>
+ <desc lang="es">
+ Este es un cache que no pertenece a ninguna de las categorías predefinicas
+ - estándar.
+ </desc>
+ <name lang="it">Altro tipo di cache</name>
+ <desc lang="it">
+ Questa è una cache che non appartiene a nessuna delle categorie standard
+ perdefinite. Usa questo attributo per cache speciali e inusuali.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="investigation" categories="de-preconditions">
+ <opencaching site_url="http://opencaching.de/" id="54" />
+ <name lang="en">Investigation required</name>
+ <desc lang="en">
+ You must investigate additional information before you can seek this cache.
+ </desc>
+ <name lang="de">Recherche</name>
+ <desc lang="de">
+ Für diesen Cache muss vorab nach Informationen gesucht werden.
+ </desc>
+ <name lang="es">Investigación</name>
+ <desc lang="es">
+ Necesitas encontrar más información antes de poder buscar este cache.
+ </desc>
+ <name lang="it">Ricerca</name>
+ <desc lang="it">
+ È necessario trovare ulteriori informazioni prima di poter cercare
+ questa cache.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="puzzle" categories="de-preconditions">
+ <groundspeak id="47" inc="true" name="Field puzzle" />
+ <opencaching site_url="http://opencaching.de/" id="55" />
+ <name lang="en">Puzzle / Mystery</name>
+ <desc lang="en">
+ Puzzles or mysteries have to be solved before or while seeking this cache.
+ </desc>
+ <name lang="de">Rätsel</name>
+ <desc lang="de">
+ Bei diesem Cache sind als Vorarbeit oder während der Suche Rütsel zu lösen.
+ </desc>
+ <name lang="es">Puzzle / Misterio</name>
+ <desc lang="es">
+ Misterio o Puzzle para ser resuelto antes o durante la búsqueda de este cache.
+ </desc>
+ <name lang="it">Puzzle / Mystery</name>
+ <desc lang="it">
+ Puzzle o Mystery devono essere risolti prima o durante la ricerca di
+ questa cache.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="maths" categories="de-preconditions">
+ <opencaching site_url="http://opencaching.de/" id="56" />
+ <name lang="en">Arithmetical problem</name>
+ <desc lang="en">
+ Before or while seeking this cache, arithmetical problems must be solved
+ which go beyond very basic calculations.
+ </desc>
+ <name lang="de">Rechenaufgabe</name>
+ <desc lang="de">
+ Es müssen vorab oder während der Suche Rechenaufgaben gelöst werden, die
+ über das kleine Geocacher 1x1 hinausgehen. zum Beispiel
+ Mittelpunktberechnungen oder Peilungen.
+ </desc>
+ <name lang="es">Problema matemático</name>
+ <desc lang="es">
+ Antes o durante la búsqueda de este cache, resolver problemas matemáticos
+ sencillos más difícil a los cálculos de la base.
+ </desc>
+ <name lang="it">Problema matematico</name>
+ <desc lang="it">
+ Prima o durante la ricerca di questa cache, devono essere risolti problemi
+ matematici più difficili di semplici calcoli base.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="ask-owner" categories="de-preconditions">
+ <opencaching site_url="http://opencaching.de/" id="58" />
+ <name lang="en">Ask owner for start conditions</name>
+ <desc lang="en">
+ Before doing this cache, you must ask the owner for the starting conditions.
+ E.g. the cache may be linked to certain events at varying dates.
+ </desc>
+ <name lang="de">Startbedingungen beim Owner erfragen</name>
+ <desc lang="de">
+ Bei diesem Cache ist es nötig, sich vor dem Angehen des Caches beim
+ Eigentümer über die Bedingungen zum Angehen zu informieren.
+ </desc>
+ <name lang="es">Ask owner for start conditions</name>
+ <desc lang="es">
+ Pregunte a los propietarios por las condiciones iniciales
+ </desc>
+ <name lang="it">Ask owner for start conditions</name>
+ <desc lang="it">
+ Chiedere al proprietario per le condizioni di partenza
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="wheelchair" categories="de-accessibility">
+ <groundspeak id="24" inc="true" name="Wheelchair accessible" />
+ <opencaching site_url="http://opencaching.pl/" id="44" />
+ <name lang="en">Wheelchair accessible</name>
+ <desc lang="en">
+ The cache is hidden in a way which makes it possible to be found
+ when moving on a wheelchair.
+ </desc>
+ <name lang="pl">Dostępna dla niepełnosprawnych</name>
+ <desc lang="pl">
+ Skrzynka ukryta w sposób umożliwiający jej znalezienie osobom
+ poruszającym się na wózku inwalidzkim. Dotyczy to zarówno
+ lokalizacji (np. dojazd alejką pod samą skrzynkę), jak i sposobu
+ ukrycia.
+ </desc>
+ <name lang="de">rollstuhltauglich</name>
+ </attr>
+
+ <attr okapi_attr_id="drivein" categories="de-accessibility">
+ <groundspeak id="53" inc="true" name="Park and grab" />
+ <opencaching site_url="http://opencaching.de/" id="24" />
+ <name lang="en">Near the parking area</name>
+ <desc lang="en">
+ The geocache is located close to a parking area, only a few steps away.
+ </desc>
+ <name lang="de">nahe beim Auto</name>
+ <desc lang="de">
+ Der Parkplatz befindet sich in unmittelbarer Nähe zum Geocache.
+ Es sind nicht mehr als einige Schritte notwendig um den Geocache zu finden.
+ </desc>
+ <name lang="es">Cerca de un Parking</name>
+ <desc lang="es">
+ El geocache se encuentra cerca de un parking, a poca distancia.
+ </desc>
+ <name lang="it">Vicino all'area di parcheggio</name>
+ <desc lang="it">
+ La geocache è posta vicino ad un'area di parcheggio, solo poco distante.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="walk-only" categories="de-accessibility">
+ <opencaching site_url="http://opencaching.pl/" id="84" />
+ <name lang="en">Access only by walk</name>
+ <desc lang="en">
+ The cache is accessible by walk only.
+ </desc>
+ <name lang="pl">Dostępna tylko pieszo</name>
+ <desc lang="pl">
+ Skrzynka dostępna tylko pieszo.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="hike" categories="de-accessibility">
+ <groundspeak id="9" inc="true" name="Significant Hike" />
+ <opencaching site_url="http://opencaching.de/" id="25" />
+ <name lang="en">Long walk</name>
+ <desc lang="en">
+ This cache requires a long walk - more than 5 km round trip. In the
+ mountains and other steep areas, the distance for a 'long walk' may be
+ shorter. Walking shoes and appropriate equipment are recommended.
+ </desc>
+ <name lang="de">längere Wanderung</name>
+ <desc lang="de">
+ Bei diesem Cache erwartet euch eine Wanderung von mehr als 5 Kilometer,
+ vom Ausgangspunkt bis zum Cache und wieder zurück. Im Gebirge und bei
+ entsprechenden Steigungen kann das Attribut auch bei kürzeren Wegstrecken
+ gesetzt sein. Gute Wanderschuhe und entsprechende Ausrüstung empfehlen sich.
+ </desc>
+ <name lang="es">Larga caminata</name>
+ <desc lang="es">
+ Esta cache requiere una larga caminata - más de 5 kilometros de ida y
+ vuelta. En las montañas escarpadas o en otras áreas. Recomendados calzado
+ para caminar y equipo adecuado.
+ </desc>
+ <name lang="it">Lunga camminata</name>
+ <desc lang="it">
+ Questa cache richiede una lunga camminata - più di 5 km tra andata e
+ ritorno. In montagna o in altre aree ripide, la distanza per una cache
+ del genere può essere minore. Sono raccomandati scarpe da escursione ed
+ equipaggiamento adeguato.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="wading" categories="de-accessibility">
+ <groundspeak id="11" inc="true" name="May require wading" />
+ <opencaching site_url="http://opencaching.de/" id="26" />
+ <name lang="en">Swamp, marsh or wading</name>
+ <desc lang="en">
+ This cache requires passing swampy or marshy ground our wading throuh
+ shallow water. Wear appropriate clothes. After rainfall, the terrain
+ may be very demanding or not passable at all.
+ </desc>
+ <name lang="de">sumpfig/matschiges Gelände / waten</name>
+ <desc lang="de">
+ Bei diesem Cache geht es durch sumpfiges oder matschiges Gelände oder
+ es muss durch flaches Wasser gewatet werden. Entsprechende Kleidung
+ wird empfohlen. Nach Regenfällen kann das Gelände wesentlich schwerer
+ oder überhaupt nicht begehbar sein.
+ </desc>
+ <!-- TODO: add 'wading' to Spanish translation -->
+ <name lang="es">Pantano / terreno fangoso</name>
+ <desc lang="es">
+ Esta cache requiere la superación de pantanos. Usar ropa apropiada.
+ Después de la lluvia, el suelo puede ser difícil o no factible.
+ </desc>
+ <!-- TODO: add 'wading' to Italian translation -->
+ <name lang="it">Palude o marcita</name>
+ <desc lang="it">
+ Questa cache richiede il superamento di terreno paludoso o
+ acquitrinoso. Indossare un abbigliamento adeguato. Dopo la pioggia,
+ il terreno può essere molto impegnativo o non praticabile a tutti.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="steep" categories="de-accessibility">
+ <opencaching site_url="http://opencaching.de/" id="27" />
+ <name lang="en">Hilly area</name>
+ <desc lang="en">
+ One or more ascents lie between you and the cache.
+ </desc>
+ <name lang="de">hügeliges Gelände</name>
+ <desc lang="de">
+ Auf dem Weg zum Geocache bzw. während der Cachesuche sind eine oder
+ mehrere Steigungen zu überwinden.
+ </desc>
+ <name lang="es">Terreno montañoso</name>
+ <desc lang="es">
+ Una o más pendientes para acceder al cache.
+ </desc>
+ <name lang="it">Area collinare</name>
+ <desc lang="it">
+ Una o più salite si trovano tra voi e la cache.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="climbing" categories="de-accessibility">
+ <groundspeak id="10" inc="true" name="Difficult climbing" />
+ <opencaching site_url="http://opencaching.de/" id="28" />
+ <name lang="en">Some climbing (no gear needed)</name>
+ <desc lang="en">
+ This cache requires some climbing and you may have to use your hands,
+ but you won’t need climbing gear. Be very careful during rainy weather
+ or before thunderstorms!
+ </desc>
+ <name lang="de">leichtes Klettern (ohne Ausrüstung)</name>
+ <desc lang="de">
+ Während der Cachesuche ist leichtes Klettern notwendig, bei dem man sich
+ z.B. mit den Händen festhalten muss. Gute Trittsicherheit und
+ Schwindelfreiheit empfehlen sich. Es ist jedoch keine Spezialausrüstung
+ notwendig wie z.B. Sicherungsseil, Klettersteigset oder Steigeisen.
+ Besonders bei feuchter Witterung oder vor Gewittern sollte man mit der
+ entsprechenden Vorsicht handeln.
+ </desc>
+ <name lang="es">fácil de subir (sin equipo)</name>
+ <desc lang="es">
+ Esta cache requiere un poco de escalada y puede ser necesario usar las
+ manos, pero no es necesario material de montaña. ¡Tenga mucho cuidado
+ durante la temporada de lluvias.!
+ </desc>
+ <name lang="it">Arrampicata (attrezzatura non necessaria)</name>
+ <desc lang="it">
+ Questa cache richiede un po' di arrampicata e potrebbe essere necessario
+ usare le mani, ma non c'è bisogno di attrezzatura da arrampicata.
+ Prestare molta attenzione durante la stagione delle piogge o prima dei
+ temporali!
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="swimming" categories="de-accessibility">
+ <groundspeak id="12" inc="true" name="May require swimming" />
+ <opencaching site_url="http://opencaching.de/" id="29" />
+ <name lang="en">Swimming required</name>
+ <desc lang="en">
+ This cache requires crossing a river or a lake. The water can be steep.
+ </desc>
+ <name lang="de">Schwimmen erforderlich</name>
+ <desc lang="de">
+ Auf dem Weg zum Geocache muss ein Fluß oder See überquert werden.
+ Das Wasser ist tief genug um zu schwimmen. Je nach Örtlichkeit kann auch
+ ein Schlauchboot, Kajak oder ähnliches verwendet werden (näheres ist
+ in der Beschreibung zum Geocache zu finden). Die Entfernung ist aber ohne
+ besondere Ausdauer noch zu schwimmen.
+ </desc>
+ <name lang="es">Requiere nadar</name>
+ <desc lang="es">
+ Esta cache requiere cruzar un río o un lago. El agua es lo suficientemente
+ profundo para nadar. Puede utilizar un barco, pero la distancia es lo
+ suficientemente corto como para ser asequible para un nadador.
+ </desc>
+ <name lang="it">Nuoto necessario</name>
+ <desc lang="it">
+ Questa cache richiede l'attraversamento di un fiume o un lago. L'acqua è
+ abbastanza profonda per nuotare. È possibile utilizzare una barca, ma la
+ distanza è abbastanza breve per essere alla portata di un nuotatore medio.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="fee" categories="de-accessibility">
+ <groundspeak id="2" inc="true" name="Access or parking fee" />
+ <opencaching site_url="http://opencaching.de/" id="36" />
+ <name lang="en">Access or parking fee</name>
+ <desc lang="en">
+ You must pay an access or parking fee to access this cache.
+ </desc>
+ <name lang="de">Zugangs- bzw. Parkentgelt</name>
+ <desc lang="de">
+ Um zum Cache zu gelangen, müsst ihr entweder einen Eintritt oder eine
+ Parkgebühr bezahlen.
+ </desc>
+ <name lang="es">Acceso o parking pagando</name>
+ <desc lang="es">
+ Deberas pagar un acceso o estacionamiento para acceder a esta cache.
+ </desc>
+ <name lang="it">Tassa di ingresso o di parcheggio</name>
+ <desc lang="it">
+ Devi pagare un accesso o parcheggio a pagamento per accedere a questa cache.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="bike" categories="de-accessibility">
+ <groundspeak id="32" inc="true" name="Bicycles" />
+ <opencaching site_url="http://opencaching.pl/" id="85" />
+ <name lang="en">Bikes allowed</name>
+ <desc lang="en">
+ You can reach the cache by bike.
+ </desc>
+ <name lang="pl">Dostępna rowerem</name>
+ <desc lang="pl">
+ Można dojechać do skrzynki rowerem.
+ </desc>
+ <desc lang="de">Fahrräder erlaubt</desc>
+ </attr>
+
+ <attr okapi_attr_id="nature" categories="de-location">
+ <opencaching site_url="http://opencaching.pl/" id="60" />
+ <name lang="en">Hidden in natural surroundings (forests, mountains, etc.)</name> <!-- i.e. not in a city? -->
+ <desc lang="en">
+ The cache is hidden in a remote and quick place - a forest, wild
+ meadow, a swap, etc.
+ </desc>
+ <name lang="pl">Umiejscowiona na łonie natury (lasy, góry, itp.)</name>
+ <desc lang="pl">
+ Umiejscowiona na łonie natury, z dala od cywilizacji - las, dzika
+ łąka, mokradła.
+ </desc>
+ <desc lang="de">in freier Natur versteckt</desc>
+ </attr>
+
+ <attr okapi_attr_id="historic" categories="de-location">
+ <opencaching site_url="http://opencaching.pl/" id="61" />
+ <name lang="en">Historic site</name>
+ <desc lang="en">
+ The cache is hidden near a historic site - a castle, battleplace,
+ cementary, old bunkers, etc.
+ </desc>
+ <name lang="pl">Miejsce historyczne</name>
+ <desc lang="pl">
+ W sąsiedztwie miejsca historycznego - zamku, pałacu, pola bitwy,
+ cmentarza, ale także fortów czy bunkrów.
+ </desc>
+ <desc lang="de">historischer Ort</desc>
+ </attr>
+
+ <attr okapi_attr_id="poi" categories="de-location">
+ <opencaching site_url="http://opencaching.de/" id="30" />
+ <name lang="en">Point of interest</name>
+ <desc lang="en">
+ There is a point of interest at the cache, like a nice scenic view
+ or a larger castle ruin. This place is worth visiting it even
+ without a geocache nearby.
+ </desc>
+ <name lang="de">interessanter Ort</name>
+ <desc lang="de">
+ Der Geocache ist in unmittelbarer Nähe zu einer Sehenswürdigkeit
+ versteckt. Das kann ein z.B. schüner Aussichtspunkt oder eine größere
+ Burgruine sein. Ein Besuch würde sich auch ohne besonderen Anlass
+ (den Geocache) lohnen.
+ </desc>
+ <name lang="es">Punto de interes</name>
+ <desc lang="es">
+ Hay un monumento cerca del cache, como un paisaje hermoso o un castillo
+ en ruinas. Este lugar es digno de visitar, incluso sin un geocache cerca.
+ </desc>
+ <name lang="it">Punto di interesse</name>
+ <desc lang="it">
+ C'è un punto di interesse alla cache, come un bel panorama o di un
+ castello in rovina. Questo luogo merita una visita anche senza una
+ geocache vicina.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="indoor" categories="de-location">
+ <opencaching site_url="http://opencaching.de/" id="33" />
+ <name lang="en">Hidden wihin enclosed rooms (caves, buildings etc.)</name>
+ <desc lang="en">
+ This geocache is not hidden in the open air, but within a building,
+ a cave or similar.
+ </desc>
+ <name lang="de">in geschlossenen Räumen (Höhle, Gebäude, etc.)</name>
+ <desc lang="de">
+ Das Ziel des Geocaches liegt nicht im Freien, sondern zum Beispiel in
+ einem Gebäude oder einer Höhle.
+ </desc>
+ <name lang="es">en espacios confinados (cuevas, edificios, etc)</name>
+ <desc lang="es">
+ Este geocache no está al aire libre, esta oculto dentro de un edificio,
+ una cueva o similares.
+ </desc>
+ <name lang="it">All'interno di stanze chiuse (caverne, edifici, ecc.)</name>
+ <desc lang="it">
+ Questa geocache non è nascosta all'aria aperta ma all'interno di un
+ edificio, di una grotta o simili.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="submerged" categories="de-location">
+ <opencaching site_url="http://opencaching.de/" id="34" />
+ <name lang="en">Hidden under water</name>
+ <desc lang="en">
+ This cache or one of the stages is placed underwater.
+ You will get wet when doing this cache.
+ </desc>
+ <name lang="de">Im Wasser versteckt</name>
+ <desc lang="de">
+ Der Geocache oder eine der Stationen ist im Wasser versteckt. Um die
+ Aufgabe zu lösen muss man ggf. das Wasser betreten, schwimmen oder tauchen.
+ </desc>
+ <name lang="es">En el agua</name>
+ <!-- TODO: update Spanish translation, English and German descriptions have slightly changed -->
+ <desc lang="es">
+ Esta cache o una de sus etapas se encuentran bajo el agua. Usted debe
+ entrar en el agua, nadar o zambullirse.
+ </desc>
+ <name lang="it">Nell'acqua</name>
+ <!-- TODO: update Italian translation, English and German descriptions have slightly changed -->
+ <desc lang="it">
+ Questa cache o uno dei suoi stadi sono posizionati sott'acqua. Devi
+ entrare in acqua, nuotare o fare una immersione.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="parking" categories="de-facilities">
+ <groundspeak id="25" inc="true" name="Parking available" />
+ <opencaching site_url="http://opencaching.de/" id="18" />
+ <name lang="en">Parking area nearby</name>
+ <desc lang="en">
+ A nearby parking area is situated as starting point for doing this cache.
+ </desc>
+ <name lang="de">Parkplatz in der Nähe</name>
+ <desc lang="de">
+ Es gibt in der Nähe einen Parklplatz, der sich als Startpunkt für die
+ Cachesuche eignet.
+ </desc>
+ <name lang="es">Parking cercano</name>
+ <desc lang="es">
+ Una zona de aparcamiento se encuentra cerca del punto de partida de
+ este cache.
+ </desc>
+ <name lang="it">Parcheggio nei pressi</name>
+ <desc lang="it">
+ Una area di parcheggio è situata nei pressi del punto di partenza di
+ questa cache.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="public-transport" categories="de-facilities">
+ <groundspeak id="26" inc="true" name="Public transportation" />
+ <opencaching site_url="http://opencaching.de/" id="19" />
+ <name lang="en">Public transportation</name>
+ <desc lang="en">
+ This cache is located outside of urban areas and has a public
+ transport station nearby.
+ </desc>
+ <name lang="de">erreichbar mit ÖVM</name>
+ <desc lang="de">
+ Dieser Cache lässt sich mit Hilfe von öffentlichen Verkehrsmitteln erreichen
+ und liegt außerhalb von Städten.
+ </desc>
+ <name lang="es">Transporte Público</name>
+ <desc lang="es">
+ Este cache se encuentra también fuera de las zonas urbanas y una
+ estación de transporte público.
+ </desc>
+ <name lang="it">Trasporto pubblico</name>
+ <desc lang="it">
+ Questa cache è situata al difuori di aree urbane e ha una stazione di
+ trasporto pubblico nelle vicinanze.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="drinking-water" categories="de-facilities">
+ <groundspeak id="27" inc="true" name="Drinking water nearby" />
+ <opencaching site_url="http://opencaching.de/" id="20" />
+ <name lang="en">Drinking water nearby</name>
+ <desc lang="en">
+ There is drinking water along the trail or near the cache. This may be
+ useful especially especially when doing event caches, longer hikes or
+ caches at probably dirty locations.
+ </desc>
+ <name lang="de">Trinkwasser in der Nähe</name>
+ <desc lang="de">
+ Während der Cachetour oder in der Nähe des Geocaches ist Trinkwasser
+ verfügbar. Besonders bei Event-Caches, längeren Multicaches und bei
+ Geocaches wo man vermutlich schmutzig wird kann dies hilfreich sein.
+ </desc>
+ <name lang="es">Agua potable en las cercanias</name>
+ <desc lang="es">
+ Hay agua potable a lo largo de la ruta o cerca de la cache. Este
+ atributo es especialmente útil en la planificación de Eventos,
+ o caches con viajes largos a lugares como las cuevas o minas
+ probablemente esté sucio.
+ </desc>
+ <name lang="it">Acqua potabile nei pressi</name>
+ <desc lang="it">
+ C'è acqua potabile lungo il percorso o nelle vicinanze della cache.
+ Questo attributo è utile soprattutto nella pianificazione di cache
+ evento, di lunghe escursioni o di cache in luoghi probabilmente
+ sporchi come le grotte o le miniere.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="restrooms" categories="de-facilities">
+ <groundspeak id="28" inc="true" name="Public restrooms nearby" />
+ <opencaching site_url="http://opencaching.de/" id="21" />
+ <name lang="en">Public restrooms nearby</name>
+ <desc lang="en">
+ There are public restrooms along the way or near the cache.
+ </desc>
+ <name lang="de">öffentliche Toilette in der Nähe</name>
+ <desc lang="de">
+ Während der Cachetour oder in der Nähe des Geocaches ist eine öffentliche
+ Toilette verfügbar.
+ </desc>
+ <name lang="es">Aseos públicos cercanos</name>
+ <desc lang="es">
+ Hay baños públicos a lo largo de la carretera o en las proximidades
+ del cache.
+ </desc>
+ <name lang="it">Bagni pubblici nei pressi</name>
+ <desc lang="it">
+ Ci sono WC pubblici lungo la strada o nelle vicinanze della cache.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="phone" categories="de-facilities">
+ <groundspeak id="29" inc="true" name="Telephone nearby" />
+ <opencaching site_url="http://opencaching.de/" id="22" />
+ <name lang="en">Public phone nearby</name>
+ <desc lang="en">
+ There is a public phone along the way or near the cache.
+ </desc>
+ <name lang="de">Telefon in der Nähe</name>
+ <desc lang="de">
+ Während der Cachetour oder in der Nähe des Geocaches gibt es ein
+ öffentliches Telefon.
+ </desc>
+ <name lang="es">Teléfono Público en las cercanias</name>
+ <desc lang="es">
+ Hay teléfonos públicos en la carretera o en las proximidades del cache.
+ </desc>
+ <name lang="it">Telefono pubblico nei pressi</name>
+ <desc lang="it">
+ Ci sono telefoni pubblici lungo la strada o nelle vicinanze della cache.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="first-aid" categories="de-facilities">
+ <opencaching site_url="http://opencaching.de/" id="23" />
+ <name lang="en">First aid available</name>
+ <desc lang="en">
+ There is a first aid station, call box, mountain rescue or similar
+ arrangement near the cache.
+ </desc>
+ <name lang="de">Erste Hilfe verfügbar</name>
+ <desc lang="de">
+ In der Nähe des Caches findet ihr eine Erste Hilfe-Station, Notrufsäule,
+ Bergwacht oder entsprechende Einrichtung.
+ </desc>
+ <name lang="es">Disponible socorro rapido</name>
+ <desc lang="es">
+ Hay un punto de socorro, un teléfono para pedir ayuda, un centro de
+ rescate de montaña o similar cerca del cache.
+ </desc>
+ <name lang="it">Disponibile pronto soccorso</name>
+ <desc lang="it">
+ C'è un pronto soccorso, un telefono per chiamate di soccorso, un centro
+ di soccorso alpino o simili nelle vicinanze della cache.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="24-hours" categories="de-time-and-seasons">
+ <groundspeak id="13" inc="true" name="Available at all times" />
+ <opencaching site_url="http://opencaching.de/" id="38" />
+ <name lang="en">Available 24/7</name>
+ <desc lang="en">
+ This cache can be found at any time of day or week.
+ </desc>
+ <name lang="de">rund um die Uhr machbar</name>
+ <desc lang="de">
+ Dieser Cache ist jederzeit machbar, sowohl am Tage als auch in der Nacht.
+ </desc>
+ <name lang="es">Disponible las 24 horas</name>
+ <desc lang="es">
+ Esta cache se puede encontrar tanto de día como de noche.
+ </desc>
+ <name lang="it">Disponibile 24 ore</name>
+ <desc lang="it">
+ Questa cache può essere trovata sia di giorno che di notte.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="not-24-hours" categories="de-time-and-seasons">
+ <groundspeak id="13" inc="false" name="Available at all times" />
+ <opencaching site_url="http://opencaching.pl/" id="80" />
+ <opencaching site_url="http://opencaching.de/" id="39" />
+ <name lang="en">Not 24/7</name>
+ <desc lang="en">
+ This cache can only be done at certain times of day or week - see the cache
+ description for more details. For example, the cache may be placed in an
+ area with restricted opening hours.
+ </desc>
+ <name lang="pl">Dostępna w określonych godzinach</name>
+ <desc lang="pl">
+ Dostępna w określonych dniach, godzinach, często wstęp płatny.
+ Często będzie to muzeum lub skansen. Szczegółowe informacje o
+ dostępności powinny znajdować się w opisie skrzynki.
+ </desc>
+ <name lang="de">nur zu bestimmten Uhrzeiten</name>
+ <desc lang="de">
+ Dieser Cache lässt sich nur zu bestimmten Tageszeiten absolvieren.
+ Nähere Angaben sind in der Beschreibung des Caches zu finden.
+ </desc>
+ <name lang="es">Sólo disponible a ciertas horas</name>
+ <desc lang="es">
+ Esta cache se puede hacer solamente en ciertos momentos del día -
+ véase la descripción de caché para obtener más detalles.
+ </desc>
+ <name lang="it">Disponibile solo in certi orari</name>
+ <desc lang="it">
+ Questa cache può essere cercata solo a certe ore del giorno -
+ vedi la descrizione per ulteriori informazioni.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="daycache" categories="de-time-and-seasons">
+ <groundspeak id="14" inc="false" name="Recommended at night" />
+ <opencaching site_url="http://opencaching.de/" id="40" />
+ <name lang="en">Not recommended at night</name>
+ <desc lang="en">
+ Searching for this cache is not recommended by night. It might be
+ dangerous, or the cache may be hidden in an area where flashlights
+ may attract unwanted attention.
+ </desc>
+ <name lang="de">nur tagüber</name>
+ <desc lang="de">
+ Dieser Cache lässt sich nur tagsüber angehen, zum Beispiel weil das Gelände
+ gefährlich ist oder die Suche mit Taschenlampen in einem Wohngebiet negativ
+ auffallen würde.
+ </desc>
+ <name lang="es">solo por el día</name>
+ <desc lang="es">
+ Deberas encontrar este cache sólo durante el día. Por ejemplo, el área pued
+ ser peligroso y contienen rocas o abismos. O bien, el uso de linternas puede
+ ser imposible porque sería sospechoso en una zona residencial.
+ </desc>
+ <name lang="it">solo di giorno</name>
+ <desc lang="it">
+ Si dovrebbe cercare questa cache solamente durante il giorno. Ad esempio,
+ l'area può essere pericolosa e contenere scogliere o abissi. Oppure,
+ l'utilizzo di torce elettriche potrebbe essere impossibile perché
+ risulterebbe sospetto all'interno di una zona residenziale.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="night-recommended" categories="de-time-and-seasons">
+ <!--
+ GS has "night cache" (52) and "recommended at night" (14).
+ night cache = can normally ONLY be done at night, reflectors etc.
+ recommended at night = can well be done at night
+
+ On OCPL there is no distinction between these two. OCPL uses
+ "Recommended at night" attribute for "Night caches" too.
+ -->
+ <groundspeak id="14" inc="true" name="Recommended at night" />
+ <opencaching site_url="http://opencaching.pl/" id="91" />
+ <name lang="en">Recommended at night</name>
+ <desc lang="pl">
+ It is recommended to search for this cache by night. I.e. there
+ might be some light-reflecting surfaces involved which are usually
+ invisible during daylight.
+ </desc>
+ <name lang="pl">Zalecane szukanie nocą</name>
+ <desc lang="pl">
+ Aby znaleźć skrzynkę zalecane jest poszukiwanie jej nocą, ze
+ względu na miejsce ukrycia lub użyte elementy odblaskowe, których
+ oświetlenie umożliwia odnalezienie skrzynki.
+ </desc>
+ <name lang="de">am besten nachts findbar</name>
+ </attr>
+
+ <attr okapi_attr_id="nightcache" categories="de-time-and-seasons">
+ <!--
+ GS has "night cache" (52) and "recommended at night" (14).
+ night cache = can normally ONLY be done at night, reflectors etc.
+ recommended at night = can well be done at night
+ -->
+ <groundspeak id="52" inc="true" name="Night Cache" />
+ <opencaching site_url="http://opencaching.de/" id="1" />
+ <name lang="en">Only at night</name>
+ <desc lang="en">
+ This geocache can be found at night only - it is a so-called night cache.
+ There may be reflectors which have to be flashlighted and will point
+ to the hiding place, or other special night-caching mechanisms.
+ </desc>
+ <name lang="de">nur bei Nacht</name>
+ <desc lang="de">
+ Der Geocache kann nur bei Nacht gelöst werden und wird deshalb Nachtcache
+ genannt. Zum Beispiel mössen Reflektoren mit einer Taschenlampe
+ angeleuchtet werden, die dann den Weg zum Versteck zeigen.
+ </desc>
+ <name lang="es">Sólo por la noche</name>
+ <desc lang="es">
+ Esta cache se puede encontrar solamente por la noche - tienes que
+ considerar cache notturna. Puede haber placas reflectantes que
+ brillaran y te llevaran al cache, o otros mecanismo especiales para
+ caches nocturnos.
+ </desc>
+ <name lang="it">Solo di notte</name>
+ <desc lang="it">
+ Questa cache può essere trovata solo di notte - è una cosiddetta
+ cache notturna. Ci possono essere targhette riflettenti che devono
+ essere illuminate e ti conducono al nascondiglio, o altri speciali
+ meccanismi di caching notturno.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="all-seasons" categories="de-time-and-seasons">
+ <groundspeak id="62" inc="false" name="Seasonal access" />
+ <opencaching site_url="http://opencaching.de/" id="42" />
+ <name lang="en">All seasons</name>
+ <desc lang="en">
+ This cache can be found the whole year round, while difficulty may
+ depend on seasons.
+ </desc>
+ <name lang="de">ganzjähig zugänglich</name>
+ <desc lang="de">
+ Dieser Cache lässt sich während des gesamten Jahres finden, wobei je
+ nach Jahreszeit die Schwierigkeit bei der Suche schwanken kann.
+ </desc>
+ <name lang="es">Todas las temporadas</name>
+ <desc lang="es">
+ Esta cache se encuentrar durante todo el año, mientras que la dificultad
+ puede depender de las estaciones.
+ </desc>
+ <name lang="it">Tutte le stagioni</name>
+ <desc lang="it">
+ Questa cache si trova tutto l'anno, mentre la difficoltà può dipendere
+ dalle stagioni.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="not-all-seasons" categories="de-time-and-seasons">
+ <groundspeak id="62" inc="true" name="Seasonal access" />
+ <opencaching site_url="http://opencaching.de/" id="60" />
+ <name lang="en">Only available during specified seasons</name>
+ <desc lang="en">
+ This cache can be done at certain seasons only - see the cache
+ description for more details.
+ </desc>
+ <name lang="de">Nur zu bestimmten Zeiten im Jahr</name>
+ <desc lang="de">
+ Dieser Cache lässt sich nur zu bestimmten Zeite im Jahr absolvieren.
+ Näheres ist in der Cachebeschreibung angegeben.
+ </desc>
+ <name lang="es">Sólo disponible durante las estaciones especificadas</name>
+ <desc lang="es">
+ Esta cache se puede hacer en ciertas épocas del año solamente - vea la
+ descripción de cache para obtener más detalles.
+ </desc>
+ <name lang="it">Disponibile solo in certe stagioni</name>
+ <desc lang="it">
+ Questa cache può essere cercata solo in certe stagioni - vedi la
+ descrizione per ulteriori informazioni.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="np-season" categories="de-time-and-seasons">
+ <opencaching site_url="http://opencaching.de/" id="43" />
+ <name lang="en">Breeding season / protected nature</name>
+ <desc lang="en">
+ Don’t seek this cache during animal breeding season! See the cache
+ description on which time of year must be avoided. Also, pay
+ attention to the local terms and signs regarding nature protection.
+ </desc>
+ <name lang="de">Brutsaison / Naturschutz</name>
+ <desc lang="de">
+ Dieser Cache sollte in der Brutsaison nicht absolviert werden. In der
+ Beschreibung sollte angegeben sein, welche Jahreszeit davon betroffen ist.
+ Achte bitte auch auf die örtliche Beschilderung zum Naturschutz.
+ </desc>
+ <name lang="es">Temporada de reproducción / protección de la naturaleza</name>
+ <desc lang="es">
+ ¡No intente esta cache durante la temporada de cría de los animales!
+ Vvéase la descripción del cache de la época del año debe ser evitado.
+ Preste atención también a las condiciones o signos en cuanto al respeto
+ por la naturaleza.
+ </desc>
+ <name lang="it">Stagione di riproduzione / natura protetta</name>
+ <desc lang="it">
+ Non cercare questa cache durante il periodo riproduttivo degli animali!
+ Vedi descrizione della cache quale periodo dell'anno debba essere evitato.
+ Prestate anche attenzione alle condizioni o ai cartelli riguardo il
+ rispetto della natura.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="snow-proof" categories="de-time-and-seasons">
+ <groundspeak id="15" inc="true" name="Available during winter" />
+ <opencaching site_url="http://opencaching.de/" id="44" />
+ <name lang="en">Available during winter</name>
+ <desc lang="en">
+ This cache can be found even after heavy snowing. All stages and the
+ geocache are hidden in a snow-safe way: they will not be covered by
+ fallen snow, or ice, etc.
+ </desc>
+ <name lang="de">schneesicheres Versteck</name>
+ <desc lang="de">
+ Dieser Cache lässt sich auch nach starkem Schneefall suchen. Die einzelnen
+ Stationen und der Geocache sind so versteckt, dass sie nicht von Schnee
+ verdeckt werden, bzw. von Schneehaufen die durch Räumfahrzeuge entstehen.
+ </desc>
+ <name lang="es">Nieve en el escondite</name>
+ <desc lang="es">
+ Este cache también se puede encontrar después de fuertes nevadas. Todas
+ las fases y geocaches se esconde en lugares seguros para la caída de la
+ nieve, no será cubierto por acumulaciones de nieve.
+ </desc>
+ <name lang="it">Luogo a prova di neve</name>
+ <desc lang="it">
+ Questa cache può essere trovata anche dopo forti nevicate. Tutte le fasi
+ e la geocache sono nascosti in luoghi sicuri per la neve: non saranno
+ coperti da neve caduta né da cumuli di neve creati ad esempio da veicoli
+ spalaneve.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="lowwater" categories="de-time-and-seasons">
+ <opencaching site_url="http://opencaching.de/" id="41" />
+ <name lang="en">Not at high water level</name>
+ <desc lang="en">
+ This cache can be done only at low or normal water level. It is
+ inaccessible during flood.
+ </desc>
+ <name lang="de">nicht bei Hochwasser oder Flut</name>
+ <desc lang="de">
+ Der Geocache kann nur bei bei niedrigem oder normalem Wasserstand
+ bzw. bei Ebbe gesucht werden. Bei Hochwasser oder Flut ist er
+ unzugänglich.
+ </desc>
+ <!-- TODO: Spanish and Italian translations -->
+ </attr>
+
+ <attr okapi_attr_id="compass" categories="de-tools">
+ <opencaching site_url="http://opencaching.pl/" id="47" />
+ <opencaching site_url="http://opencaching.de/" id="47" />
+ <name lang="en">Compass required</name>
+ <desc lang="en">
+ A compass is required.
+ </desc>
+ <name lang="pl">Potrzebny kompas</name>
+ <desc lang="pl">
+ Kompas może okazać się niezbędny aby dotrzeć do wskazanego miejsca
+ skrzynki.
+ </desc>
+ <name lang="de">Kompass</name>
+ <desc lang="de">
+ Für diesen Cache braucht ihr einen funktionierenden Kompass für Peilungen
+ oder Orientierungen.
+ </desc>
+ <name lang="es">Brújula</name>
+ <desc lang="es">
+ Se necesita una brújula.
+ </desc>
+ <name lang="it">Bussola</name>
+ <desc lang="it">
+ E' necessaria una bussola.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="pen" categories="de-tools">
+ <opencaching site_url="http://opencaching.pl/" id="48" />
+ <name lang="en">Take something to write</name>
+ <desc lang="en">
+ There is no pencil in the cache. Take something to write with.
+ </desc>
+ <name lang="pl">Weź coś do pisania</name>
+ <desc lang="pl">
+ Skrzynka nie zawiera ołówka, weź ze sobą coś do pisania.
+ </desc>
+ <desc lang="de">Stift mitbringen</desc>
+ </attr>
+
+ <attr okapi_attr_id="digging" categories="de-tools">
+ <opencaching site_url="http://opencaching.pl/" id="81" />
+ <name lang="en">You may need a shovel</name>
+ <desc lang="en">
+ The cache may require more digging. A shovel might come in handy.
+ </desc>
+ <name lang="pl">Potrzebna łopatka</name>
+ <desc lang="pl">
+ Skrzynka jest zakopana w ziemi.
+ </desc>
+ <desc lang="de">Grabwerkzeug benötigt</desc>
+ </attr>
+
+ <attr okapi_attr_id="flashlight" categories="de-tools">
+ <groundspeak id="44" inc="true" name="Flashlight required" />
+ <opencaching site_url="http://opencaching.pl/" id="82" />
+ <opencaching site_url="http://opencaching.de/" id="48" />
+ <name lang="en">Flashlight required</name>
+ <desc lang="en">
+ You will need a flashlight to find this cache.
+ </desc>
+ <name lang="pl">Potrzebna latarka</name>
+ <desc lang="pl">
+ Przy poszukiwaniach tej skrzynki potrzebna jest latarka.
+ </desc>
+ <name lang="de">Taschenlampe</name>
+ <desc lang="de">
+ Um diesen Cache anzugehen, benötigt ihr eine funktionstüchtige
+ Taschenlampe. Denkt auch an Ersatzbatterien.
+ </desc>
+ <name lang="es">Linterna</name>
+ <desc lang="es">
+ Es necesario una linterna para encontrar este cache. ¡No se olvide de las
+ baterías de repuesto!
+ </desc>
+ <name lang="it">Lampada tascabile</name>
+ <desc lang="it">
+ E' necessaria una torcia portatile per trovare questa cache. Non
+ dimenticate le batterie di riserva!
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="climbing-gear" categories="de-tools">
+ <groundspeak id="3" inc="true" name="Climbing gear" />
+ <opencaching site_url="http://opencaching.de/" id="49" />
+ <name lang="en">Climbing gear required</name>
+ <desc lang="en">
+ For this cache, you will need climbing equipment and the knowledge
+ how to use it properly. If you are a beginner, don’t do it alone but
+ use the support of an experienced climber or mountaineer.
+ </desc>
+ <name lang="de">Kletterzeug</name>
+ <desc lang="de">
+ Um diesen Cache absolvieren zu können, benötigt ihr neben der normalen
+ Ausrüstung auch noch Kletterausrüstung, und entsprechendes Wissen um
+ deren Handhabung und ums Klettern. Laien sollten sich auf jeden Fall
+ von einem erfahrenen Kletterer oder Bergsteiger unterstützen lassen.
+ </desc>
+ <name lang="es">Equipo de escalada</name>
+ <desc lang="es">
+ Para este cache, tendrá que utilizar los equipos y saber cómo utilizarlo
+ correctamente. Si usted es un principiante, no lo haga solos, sino que
+ utiliza el apoyo de un experimentado escalador o alpinista.
+ </desc>
+ <name lang="it">Attrezzatura per arrampicata</name>
+ <desc lang="it">
+ Per questa cache, avrete bisogno di materiale da arrampicata e di saperlo
+ usare correttamente. Se sei un principiante, non farlo da solo, ma
+ utilizza il sostegno di uno scalatore esperto o un alpinista.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="cave-equipment" categories="de-tools">
+ <opencaching site_url="http://opencaching.de/" id="50" />
+ <name lang="en">Cave equipment required</name>
+ <desc lang="en">
+ This geocache is hidden in a cave, and you should use appropriate
+ equipment to access it. Beware: Even small caves may confront you with
+ unforeseen problems and dangers, like thunder storms (water!) or a
+ sprained ankle. Have advice first from cave-experienced people! Also
+ take care of protected nature; e.g. bat places must not be disturbed.
+ </desc>
+ <name lang="de">Höhlenzeug</name>
+ <desc lang="de">
+ Der Geocache ist in einer Höhle versteckt und man sollte entsprechende
+ Ausrüstung mitbringen. Vorsicht: Bereits kleinste Höhlensysteme können
+ bei unvorhergesehenen Problem z.B. Gewittern (Wasser!) oder einem
+ verstauchten Knöchel sehr gefährlich werden! Ihr solltet euch vorab
+ gründlich bei erfahreren Höhlengehern informieren. Beachtet auch den
+ Naturschutz – Fledermausquartiere dürfen nicht gestört werden!
+ </desc>
+ <name lang="es">Equipación para cuevas</name>
+ <desc lang="es">
+ Este geocache está escondido en una cueva, y se debe utilizar el equipo
+ adecuado para acceder a ella. Tenga en cuenta que incluso las pequeñas
+ cuevas pueden prever los problemas imprevistos y peligros, como durante
+ las tormentas o con un esguince de tobillo. ¡Acceda con personas
+ experimentadas en cuevas! También debe protegerse la naturaleza sobre
+ todo en esos lugares donde los murciélagos no deben ser molestados.
+ </desc>
+ <name lang="it">Attrezzatura per grotta</name>
+ <desc lang="it">
+ Questa geocache è nascosta in una grotta, e si dovrebbe utilizzare
+ attrezzature adeguate per accedervi. Attenzione: anche piccole grotte
+ possono prevedere problemi imprevisti e pericoli, come in caso di
+ temporali (acqua!) o una caviglia slogata. Consigliatevi prima con
+ persone che abbiano esperienza di grotte! Abbiate anche cura della
+ natura protetta, ad esempio dei luoghi dove i pipistrelli non devono
+ essere disturbati.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="diving-equipment" categories="de-tools">
+ <groundspeak id="5" inc="true" name="Scuba gear" />
+ <opencaching site_url="http://opencaching.de/" id="51" />
+ <name lang="en">Diving equipment required</name>
+ <desc lang="en">
+ You will need diving equipment to find this geocache. The water depth
+ of the cache location is specified in the description. Please note that
+ secure diving requires special training. Without diving experience,
+ you may search this cache in company of a diving teacher.
+ </desc>
+ <name lang="de">Taucherausrüstung</name>
+ <desc lang="de">
+ Um den Geocache zu finden benötigt ihr eine Tauchausrüstung. In welcher
+ Tiefe der Geocache liegt ist in der Beschreibung angegeben. Bitte beachtet,
+ dass Ihr für einen sicheren Tauchgang eine entsprechende Ausbildung
+ benötigt. Als Nicht-Taucher könnt ihr den Geocache evtl. zusammen mit
+ einem Tauchlehrer suchen.
+ </desc>
+ <name lang="es">Diving equipment</name>
+ <desc lang="es">
+ Necesitará un equipo de buceo para encontrar este geocache. La
+ profundidad del agua en la ubicación de la cache se especifica en la
+ descripción. Tenga en cuenta que el buceo requiere un entrenamiento
+ especial. Sin experiencia de buceo, puedes buscar por el caché, junto
+ con un buceador experimentado.
+ </desc>
+ <name lang="it">Equipo de buceo</name>
+ <desc lang="it">
+ Avrete bisogno di attrezzatura subacquea per trovare questa geocache.
+ La profondità d'acqua nella posizione della cache viene specificata nella
+ descrizione. Si prega di notare che l'immersione in tutta sicurezza
+ richiede una formazione specifica. Senza esperienza di immersioni, è
+ possibile cercare questa cache in compagnia di un insegnante di sub.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="special-tools" categories="de-tools">
+ <groundspeak id="51" inc="true" name="Special tool required" />
+ <opencaching site_url="http://opencaching.pl/" id="83" />
+ <opencaching site_url="http://opencaching.de/" id="46" />
+ <name lang="en">Special tools required</name>
+ <desc lang="en">
+ You will need special equipment which is not specified by other attributes.
+ See the cache description on what tools are required.
+ </desc>
+ <name lang="pl">Wymagany dodatkowy sprzęt</name>
+ <desc lang="pl">
+ Niezbędny jest dodatkowy, niestandardowy sprzęt - może to być np.
+ kajak, sprzęt wspinaczkowy, ale również kalkulator, kalosze itp.
+ Ogólnie przedmioty, które nie należą do standardowego wyposażenia
+ poszukiwacza.
+ </desc>
+ <name lang="de">spezielle Ausrüstung</name>
+ <desc lang="de">
+ Für diesen Cache benötigst du weitere Ausrüstung, die nicht durch die
+ anderen Attribute angegeben ist und nicht zur Standardausrüstung eines
+ Geocachers gehört. Was genau du benütigst, ist in der Beschreibung
+ angegeben.
+ </desc>
+ <name lang="es">Equipamiento especial</name>
+ <desc lang="es">
+ Necesitarás un equipo especial no especificado por otros atributos.
+ </desc>
+ <name lang="it">Equipaggiamento speciale</name>
+ <desc lang="it">
+ Avrete bisogno di attrezzature speciali non specificate da altri attributi.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="boat" categories="de-tools">
+ <groundspeak id="4" inc="true" name="Boat" />
+ <opencaching site_url="http://opencaching.pl/" id="86" />
+ <opencaching site_url="http://opencaching.de/" id="52" />
+ <name lang="en">Requires a boat</name>
+ <desc lang="en">
+ This cache can usually be found only when using a watercraft.
+ Swimming is difficult or impossible because of the distance or currents.
+ See the cache description for more details.
+ </desc>
+ <name lang="pl">Wymaga sprzętu pływającego</name>
+ <desc lang="pl">
+ Skrzynka z tym atrybutem najczęściej może być zdobyta jedynie przy
+ użyciu sprzętu pływającego (łodzi, pontonu, kajaka itp.) Dopłynięcie
+ wpław jest trudne lub niemożliwe, ze względu na dystans, silne
+ prądy itp.
+ </desc>
+ <name lang="de">Wasserfahrzeug</name>
+ <desc lang="de">
+ Der Geocache kann – normalerweise – nicht ohne ein Wasserfahrzeug gefunden
+ werden. Zum Geocache kann wegen der Entfernung oder Strömung nicht
+ geschwommen werden. Details dazu sind in der Beschreibung des Geocaches
+ angegeben.
+ </desc>
+ <name lang="es">Barca</name>
+ <desc lang="es">
+ Este cache por lo general sólo se puede encontrar con una moto de agua.
+ Nadando es imposible debido a la distancia o la corriente. Véase la
+ descripción del cache para obtener más detalles.
+ </desc>
+ <name lang="it">Barca</name>
+ <desc lang="it">
+ Questa cache di solito può essere trovata solo con una moto d'acqua. Il
+ nuoto è impossibile a causa della distanza o delle correnti. Vedi la
+ descrizione della cache per maggiori dettagli.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="nogps" categories="de-tools">
+ <opencaching site_url="http://opencaching.de/" id="35" />
+ <name lang="en">No GPS required</name>
+ <desc lang="en">
+ This cache can be found without a GPS device. No additional coordinates
+ are used besides of the starting coordinates.
+ </desc>
+ <name lang="de">ohne GPS findbar</name>
+ <desc lang="de">
+ Dieser Cache lässt sich auch ohne GPS-Empfänger finden. Die Aufgaben
+ sind so gestellt, dass man außer den Startkoordinaten keine weiteren
+ Koordinaten verwenden muss.
+ </desc>
+ <name lang="es">Sin GPS</name>
+ <desc lang="es">
+ Esta cache se puede encuentra sin un dispositivo GPS. Detalles adicionales
+ no se utilizan, además de las coordenadas iniciales.
+ </desc>
+ <name lang="it">Senza GPS</name>
+ <desc lang="it">
+ Questa cache può essere trovata senza un dispositivo GPS. Non sono
+ utilizzate coordinate addizionali oltre alle coordinate iniziali.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="dangerous-area" categories="de-dangers">
+ <groundspeak id="23" inc="true" name="Dangerous area" />
+ <opencaching site_url="http://opencaching.de/" id="9" />
+ <opencaching site_url="http://opencaching.pl/" id="90" />
+ <name lang="en">Dangerous area</name>
+ <desc lang="en">
+ The cache is located within a dangerous area, and danges may not be
+ obvious, e.g. like high-traffic roads, steep ground or falling rocks.
+ Safety measures should be taken, especially when geocaching with
+ children, large groups of people or during bad weather conditions.
+ </desc>
+ <name lang="pl">Skrzynka niebezpieczna</name>
+ <desc lang="pl">
+ Skrzynka jest ukryta w niebezpiecznym terenie. Jej poszukiwania mogą
+ narazić na niebezpieczeństwo wypadku lub urazu.
+ </desc>
+ <name lang="de">gefährliches Gebiet</name>
+ <desc lang="de">
+ In dem Gebiet, wo der Geocache versteckt wurde, ist mit Gefahren zu
+ rechnen, die unter Umständen nicht auf den ersten Blick erkennbar sind.
+ Das können z.B. stark befahrene Straßen, steile Abhänge oder Steinschlag
+ sein. Deshalb sollte man bei Geocaching-Touren mit Kindern oder größeren
+ Gruppen entsprechende Vorsichtsmaßnahmen ergreifen und je nachdem auch
+ auf die Witterung achten (z.B. Regen bei steilen Abhängen).
+ Näheres zu den Gefahren ist in der Cachebeschreibung erläutert.
+ </desc>
+ <name lang="es">Zona Peligrosa</name>
+ <desc lang="es">
+ El cache está situado en una zona peligrosa, como tales como carreteras
+ con mucho tráfico, terreno empinado o caída de rocas. Usted debe tomar
+ medidas de seguridad o evitar ir a buscar el caché, sobre todo con niños,
+ con grupos grandes o en condiciones meteorológicas adversas.
+ </desc>
+ <name lang="it">Area pericolosa</name>
+ <desc lang="it">
+ La cache è situata in un'area pericolosa come strade ad alto traffico,
+ terreno ripido o caduta sassi. Si dovrebbero adottare misure di sicurezza
+ o evitare di andare a cercare la cache, in particolare nel geocaching con
+ bambini, con gruppi numerosi o in condizioni climatiche sfavorevoli.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="railway" categories="de-dangers">
+ <opencaching site_url="http://opencaching.de/" id="10" />
+ <name lang="en">Active railway nearby</name>
+ <desc lang="en">
+ There are active railroads nearby. Please be careful, keep a safe
+ distance and cross the rails only at level crossings etc.!
+ </desc>
+ <name lang="de">aktive Eisenbahnlinie in der Nähe</name>
+ <desc lang="de">
+ In der Nähe dieses Caches gibt es genutzte Eisenbahnlinien. Bitte seid
+ entsprechend vorsichtig und achtet darauf, abseits von Bahnübergängen keine
+ Gleise zu betreten.
+ </desc>
+ <name lang="es">Cerca del ferrocarril activo</name>
+ <desc lang="es">
+ ¡Hay ferrocarriles activos en las proximidades. Por favor, tenga
+ cuidado, manteniendo una distancia segura y cruzar los rieles sólo
+ en los cruces de ferrocarril, etc.!
+ </desc>
+ <name lang="it">Ferrovia attiva nei pressi</name>
+ <desc lang="it">
+ Ci sono ferrovie attive nelle vicinanze. Per favore usate cautela,
+ tenendo una distanza di sicurezza e attraversando le rotaie solo ai
+ passaggi a livello ecc.!
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="cliff" categories="de-dangers">
+ <groundspeak id="21" inc="true" name="Cliff / falling rocks" />
+ <opencaching site_url="http://opencaching.de/" id="11" />
+ <name lang="en">Cliff / Rocks</name>
+ <desc lang="en">
+ There are cliffs or dangerous rocks nearby. Beware of falling rocks
+ at the lower side, and be careful at the upper side of cliffs -
+ especially with children and while mountain biking. It can be very
+ dangerous to take a steep slope towards a cliff, because you may not
+ notice in time where the former ends and the latter starts.
+ </desc>
+ <name lang="de">Klippen / Felsen</name>
+ <desc lang="de">
+ In der Nähe des Caches gibt es Klippen oder Felsen. Unterhalb von
+ Felsen sollte man auf Steinschlag achten, von der Oberseite der Klippen
+ sollte man sich entsprechend vorsichtig nähern (insbesondere mit Kindern
+ oder Mountainbikes). Besonders gefährlich - und nicht immer erkennbar -
+ ist es, sich über einen Steilhang von oben an eine Klippe zu nähern.
+ </desc>
+ <name lang="es">Acantilado / Rocas</name>
+ <desc lang="es">
+ Hay acantilados o rocas peligrososas en las cercanas. Tenga cuidado
+ cuando esté bajo las piedras caídas, y tenga cuidado cuando esté sobre
+ el acantilado - especialmente con los niños y el ciclismo. Puede ser
+ muy peligroso tomar un camino empinado para subir el acantilado porque
+ no se puede saber de antemano cuando el primero termina y comienza otra.
+ </desc>
+ <name lang="it">Scogliera / Rocce</name>
+ <desc lang="it">
+ Ci sono scogliere o rocce pericolose nelle vicinanze. Fate attenzione
+ alla caduta pietre quando siete sotto, e siate cauti quando siete sopra
+ la scogliera - specialmente con bambini e in bicicletta. Può essere molto
+ pericoloso prendere un sentiero ripido per salire la scogliera, poiché
+ non potete sapere in anticipo quando la prima termina e inizia l'altra.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="hunting" categories="de-dangers">
+ <groundspeak id="22" inc="true" name="Hunting" />
+ <opencaching site_url="http://opencaching.de/" id="12" />
+ <name lang="en">Hunting</name>
+ <desc lang="en">
+ The geocache is placed within a hunting ground. At twilight and in the
+ dark, a flashlight or headlight should always be used for security
+ reasons. Be considerate when meeting hunters.
+ </desc>
+ <name lang="de">Jagdgebiet</name>
+ <desc lang="de">
+ Der Geocache liegt in einem Jagdgebiet. Bei Dämmerung oder Dunkelheit
+ sollte man aus Sicherheitsgründen immer eine Taschenlampe oder
+ Stirnlampe verwenden. Bei Begegnungen mit Jägern ist gegenseitige
+ Rücksichtnahme angebracht.
+ </desc>
+ <name lang="es">Zona de Caza</name>
+ <desc lang="es">
+ El geocache se coloca dentro de un coto de caza. Al caer la tarde y en
+ la oscuridad, una linterna o faro siempre debe utilizarse por razones
+ de seguridad.
+ </desc>
+ <name lang="it">Caccia</name>
+ <desc lang="it">
+ La geocache è situata nei pressi di una area di caccia. Al crepuscolo
+ e al buio, dovrebbe sempre essere usata una torcia portatile o frontale
+ per ragioni di sicurezza. Incontrando i cacciatori è opportuna una
+ reciproca gentilezza.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="thorns" categories="de-dangers">
+ <groundspeak id="39" inc="true" name="Thorns" />
+ <opencaching site_url="http://opencaching.de/" id="13" />
+ <name lang="en">Thorns</name>
+ <desc lang="en">
+ There are thorns near the cache. Wear appropriate clothes.
+ </desc>
+ <name lang="de">Dornen</name>
+ <desc lang="de">
+ In er Nähe des Geocaches gibt es Dornen. Entsprechende Kleidung und
+ evtl. Handschuhe sind zu empfehlen.
+ </desc>
+ <name lang="es">Espinas</name>
+ <desc lang="es">
+ Hay espinas cerca de la caché. Use ropa apropiada.
+ </desc>
+ <name lang="it">Spine</name>
+ <desc lang="it">
+ Ci sono spine nei pressi della cache. Indossare indumenti appropriati.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="ticks" categories="de-dangers">
+ <groundspeak id="19" inc="true" name="Ticks" />
+ <opencaching site_url="http://opencaching.de/" id="14" />
+ <name lang="en">Ticks</name>
+ <desc lang="en">
+ There are seasonably many ticks in this area. It is recommended to wear
+ long trousers and to check yourself for ticks after geocaching.
+ There are regional risk maps for <i>tick-borne encephalitis</i> on the
+ internet.
+ </desc>
+ <name lang="de">Zecken</name>
+ <desc lang="de">
+ Je nach Saison gibt es in dem Gebiet besonders viele Zecken. Es wird
+ daher empfohlen, entsprechend lange Kleidung zu tragen und nach der
+ Cachetour nach Zecken Ausschau zu halten. FSME-Risikogebiete und
+ weitere Informationen zum Thema Zecken könnt ihr z.B. auf
+ <a href='http://www.meningitis.de'>www.meningitis.de</a> nachsehen.
+ </desc>
+ <name lang="es">Garrapatas</name>
+ <desc lang="es">
+ Cada temporada hay un montón de garrapatas en este lubar. Y es
+ recomendable llevar pantalón largo y examinarse en busca de garrapatas
+ después de encontrar el cache.
+ </desc>
+ <name lang="it">Zecche</name>
+ <desc lang="it">
+ Stagionalmente ci sono molte zecche in questa area. E' raccomandabile
+ indossare pantaloni lunghi e ispezionarsi alla ricerca di zecche dopo
+ il geocaching. In internet ci sono mappe di rischio per <i>encefalite
+ e borelliosi da morso di zecca</i>.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="mines" categories="de-dangers">
+ <groundspeak id="20" inc="true" name="Abandoned mines" />
+ <opencaching site_url="http://opencaching.de/" id="15" />
+ <name lang="en">Abandoned mines</name>
+ <desc lang="en">
+ This cache leads into a (former) mining area. There may be dangers by
+ collapsing adits, or you may need to enter adits. Be careful and use
+ appropriate equipment, especially in the dark. Old mines may be covered
+ by historic preservation.
+ </desc>
+ <name lang="de">Folgen des Bergbaus</name>
+ <desc lang="de">
+ Der Cache führt in eine (ehemalige) Bergbauregion. Möglicherweise
+ bestehen Gefahren durch verstürzte Stollenmundlöcher oder es müssen
+ Stollen betreten werden. Entsprechende Ausrüstung und Vorsicht,
+ besonders bei Dunkelheit, wird empfohlen. Historische Bergwerke stehen
+ möglicherweise unter Denkmalschutz.
+ </desc>
+ <name lang="es">Mina abandonada</name>
+ <desc lang="es">
+ Esta cache le llevará a un área de la mina (abandonado). Puede haber
+ peligro con el colapso de túneles o galerías que puede ser necesario para
+ cruzar. Tenga cuidado y use de equipo adecuado, especialmente en la
+ oscuridad. Las minas antiguas pueden ser objeto de preservación histórica.
+ </desc>
+ <name lang="it">Miniere abbandonate</name>
+ <desc lang="it">
+ Questa cache vi porta in una area di miniera (abbandonata). Ci possono
+ essere pericoli per crollo di gallerie, o potrebbe essere necessario
+ attraversare gallerie. Fare attenzione e utilizzate attrezzature adeguate,
+ soprattutto al buio. Le vecchie miniere possono essere oggetto di
+ conservazione storica.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="poisonous-plants" categories="de-dangers">
+ <groundspeak id="17" inc="true" name="Poisonous plants" />
+ <opencaching site_url="http://opencaching.de/" id="16" />
+ <name lang="en">Poisonous plants</name>
+ <desc lang="en">
+ There are poisonous plants near the cache. Take care and prevent
+ children and dogs from touching or eating them.
+ </desc>
+ <name lang="de">giftige Pflanzen</name>
+ <desc lang="de">
+ In der Nähe des Caches gibt es giftige Pflanzen. Achtet also insbesondere
+ darauf, dass Kinder und Hunde diese nicht anfassen oder essen.
+ </desc>
+ <name lang="es">Planta venenosa</name>
+ <desc lang="es">
+ Hay plantas venenosas en las cercanías. Tenga cuidado y asegúrese de que
+ los niños o los perros no las toquen ni tragarlas.
+ </desc>
+ <name lang="it">Piante velenose</name>
+ <desc lang="it">
+ Ci sono piante velenose nelle vicinanze. Fate attenzione e controllate
+ che bambini o cani non le tocchino o le ingoino.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="dangerous-animals" categories="de-dangers">
+ <groundspeak id="18" inc="true" name="Dangerous animals" />
+ <opencaching site_url="http://opencaching.de/" id="17" />
+ <name lang="en">Dangerous animals</name>
+ <desc lang="en">
+ The area is inhabited by possibly dangerous animals, e.g. rabies areas,
+ venomous snakes, scorpions or bears.
+ </desc>
+ <name lang="de">giftige/gefährliche Tiere</name>
+ <desc lang="de">
+ In dem Gebiet sind Wildtiere angesiedelt, die für Menschen eine Gefahr
+ darstellen können, z.B. Tollwutgebiete, giftige Schlangen, Skorpione
+ oder Bären.
+ </desc>
+ <name lang="es">Animales Peligrosos</name>
+ <desc lang="es">
+ Esta zona es frecuentada por los animales potencialmente peligrosos,
+ por ejemplo. zorros rabiosos, serpientes venenosas, escorpiones, osos.
+ </desc>
+ <name lang="it">Animali pericolosi</name>
+ <desc lang="it">
+ Quest area è frequentata da animali potenzialmente pericolosi, ad es.
+ volpi rabide, serpenti velenosi, scorpioni, orsi.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="quick" categories="de-rating">
+ <!-- TODO: There is a groundspeak attribute called "Takes less than
+ 1 hour", is it applicable here? I'm not sure... In OCPL this attribute
+ was called "One-minute cache", and the official description includes
+ a 15 minutes limit. -->
+ <opencaching site_url="http://opencaching.pl/" id="40" />
+ <name lang="en">Quick cache</name>
+ <desc lang="en">
+ It shouldn't take more than 15 minutes to find this cache. Also,
+ there should be a parking nearby.
+ </desc>
+ <name lang="pl">Szybka skrzynka</name>
+ <desc lang="pl">
+ Jej znalezienie nie powinno zająć więcej niż 15 minut oraz jest
+ łatwy dojazd w pobliże skrzynki samochodem.
+ </desc>
+ <desc lang="de">schnell findbar</desc>
+ </attr>
+
+ <attr okapi_attr_id="overnight" categories="de-rating">
+ <opencaching site_url="http://opencaching.de/" id="37" />
+ <name lang="en">Overnight stay necessary</name>
+ <desc lang="en">
+ This cache cannot be done within a single day or a single night.
+ You will have to visit the location for more than one time,
+ or you must stay overnight. Preparation time is not included in this
+ calculation, but only the time on site.
+ </desc>
+ <name lang="de">Übernachtung erforderlich</name>
+ <desc lang="de">
+ Der Geocache kann nicht mit einer einzigen Tages- oder Nachttour gelöst
+ werden. Er muss entweder mehrmals angefahren werden oder es muss vor Ort
+ übernachtet werden. Zeit für Recherchen vorab sind dabei nicht
+ berücksicht, sondern nur die Zeit vor Ort.
+ </desc>
+ <name lang="es">Necesario pernoctar</name>
+ <desc lang="es">
+ No puedrá encontrar este cache en un solo día o durante la noche. Usted
+ tendrá que visitar el lugar más de una vez, o necesitará pasar la noche.
+ El tiempo de preparación no está incluido en este cálculo, sólo el tiempo
+ en el sitio.
+ </desc>
+ <name lang="it">Necessario pernottamento</name>
+ <desc lang="it">
+ Non è possibile trovare questa cache in un solo giorno o una sola notte.
+ Dovrete visitare il percorso per più di una volta, oppure è necessario il
+ pernottamento. Il tempo di preparazione non è incluso in questo calcolo,
+ ma solo il tempo sul sito.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="children6" categories="de-rating">
+ <groundspeak id="6" inc="true" name="Recommended for kids" /> <!-- Not sure if this is correct. -->
+ <opencaching site_url="http://opencaching.pl/" id="41" />
+ <name lang="en">Take your children</name>
+ <desc lang="pl">
+ This search if simple and safe. It's okay to take small children
+ with you.
+ </desc>
+ <name lang="pl">Można zabrać dzieci</name>
+ <desc lang="pl">
+ Jej poszukiwanie jest przyjemne, bezpieczne i można bez obaw
+ wybrać się z małymi dziećmi.
+ </desc>
+ </attr>
+
+ <attr okapi_attr_id="children10" categories="de-rating">
+ <groundspeak id="6" inc="true" name="Recommended for kids" />
+ <opencaching site_url="http://opencaching.de/" id="59" />
+ <name lang="en">Suited for children (10-12 yo)</name>
+ <desc lang="en">
+ This geocache is suitable for children. All challenges can be solved by
+ child in the age of 10 to 12 years and the terrain has no risks
+ (like highways, abysms). There should be a large geocache container with
+ trading items inside and the challenges be interesting.
+ </desc>
+ <name lang="de">kindgerecht (10-12 Jahre)</name>
+ <desc lang="de">
+ Der Geocache ist kindgerecht aufgebaut: Alle Aufgaben sind von Kindern
+ im Alter von 10 bis 12 Jahren selbstständig lösbar und das Gelände ist
+ nicht gefährlich (keine Haupstraßen, Klippen o.ä.). Am Ende des
+ Geocaches sollte sich eine Box mit Tauschgegenständen befinden, und
+ die Aufgaben sollten interessant aufgebaut sein.
+ </desc>
+ <name lang="es">Apto para niños (10-12 años)</name>
+ <desc lang="es">
+ Este geocache se creó para los niños. Todas las tareas se puede
+ completar por los niños entre los años 10 y 12 y el terrno no está
+ exenta de riesgo (tales como carreteras, acantilados). Hay un gra
+ contenedor con intercambio final y las tareas son interesantes.
+ </desc>
+ <name lang="it">Suited for children (10-12 anni)</name>
+ <desc lang="it">
+ Questa geocache è stata creata per i bambini. Tutte i compiti possono
+ essere portati a termine da bambini tra 10 e 12 anni e il terreno non
+ presenta rischi (come autostrade, abissi). C'e un grande contenitore
+ finale con oggetti di scambio e i compiti sono interessanti.
+ </desc>
+ </attr>
+
+</xml>
diff --git a/main/project/attributes_okapi/genattr.jar b/main/project/attributes_okapi/genattr.jar
new file mode 100644
index 0000000..7ee5bc4
--- /dev/null
+++ b/main/project/attributes_okapi/genattr.jar
Binary files differ
diff --git a/main/project/attributes_okapi/genattr.sh b/main/project/attributes_okapi/genattr.sh
new file mode 100644
index 0000000..7cc23be
--- /dev/null
+++ b/main/project/attributes_okapi/genattr.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+java -jar genattr.jar attributes.xml > AttributeParser.java
diff --git a/main/project/attributes_okapi/readme.txt b/main/project/attributes_okapi/readme.txt
new file mode 100644
index 0000000..5382ebe
--- /dev/null
+++ b/main/project/attributes_okapi/readme.txt
@@ -0,0 +1,10 @@
+In the current version of OKAPI (rev. 798) are attributes not returned with a unified id but only with the localized text.
+Luckily a metadata file to prepare the unification of these attributes has already been prepared by the OKAPI project
+(http://code.google.com/p/opencaching-api/source/browse/trunk/etc/attributes.xml), which do not officially publish as a stable definition,
+but which can serve as an easier starting point for the generation of a parser class.
+To allow the representation with icons we need to map these localized texts to our internal ids which is done with a parser
+generated from the aforementioned file. Soo the AttrGen project for more details.
+
+If attributes.xml will be updated, we need of course to check first if it is structurally compatible to the previous version.
+If present it seems to be necessary to remove the BOM at the beginning of the file. Then you can run genattr.sh
+and copy the generated AttributeParser.java to the appropriate location (connector.oc).
diff --git a/main/res/layout/twitter_authorization_activity.xml b/main/res/layout/authorization_activity.xml
index ec9aa8a..931441d 100644
--- a/main/res/layout/twitter_authorization_activity.xml
+++ b/main/res/layout/authorization_activity.xml
@@ -19,6 +19,7 @@
android:orientation="vertical" >
<TextView
+ android:id="@+id/auth_1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="left|center_vertical"
@@ -28,11 +29,11 @@
android:drawableLeft="@drawable/cgeo"
android:drawablePadding="15dip"
android:gravity="left|center_vertical"
- android:text="@string/about_auth_1"
android:textColor="?text_color"
android:textSize="14sp" />
<TextView
+ android:id="@+id/auth_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left|center_vertical"
@@ -40,7 +41,6 @@
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
android:gravity="left|center_vertical"
- android:text="@string/about_auth_2"
android:textColor="?text_color"
android:textSize="14sp" />
@@ -52,20 +52,17 @@
<Button
android:id="@+id/start"
- style="@style/button_full"
- android:text="@string/auth_authorize" />
+ style="@style/button_full" />
<EditText
android:id="@+id/pin"
style="@style/edittext_full"
- android:hint="@string/auth_pin_hint"
android:inputType="number"
android:visibility="gone" />
<Button
android:id="@+id/pin_button"
style="@style/button_full"
- android:text="@string/auth_finish"
android:visibility="visible" />
</LinearLayout>
</LinearLayout>
diff --git a/main/res/layout/init.xml b/main/res/layout/init.xml
index 2ee8a6b..e784b21 100644
--- a/main/res/layout/init.xml
+++ b/main/res/layout/init.xml
@@ -28,7 +28,32 @@
style="@style/separator_horizontal_headline"
android:text="@string/init_geocaching" />
</RelativeLayout>
-
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="3dip"
+ android:orientation="horizontal"
+ android:padding="3dip" >
+
+ <CheckBox android:id="@+id/gc_option"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left"
+ android:padding="1dip"
+ android:gravity="center" />
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:gravity="left"
+ android:paddingRight="3dip"
+ android:textSize="14sp"
+ android:textColor="?text_color"
+ android:text="@string/init_gc_activate" />
+ </LinearLayout>
+
<TextView
android:id="@+id/legal_note"
android:layout_width="wrap_content"
@@ -85,14 +110,14 @@
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
android:padding="3dip"
- android:text="@string/init_oc_username_description"
+ android:text="@string/init_oc_de_description"
android:textColor="?text_color"
android:textSize="14sp" />
- <EditText
- android:id="@+id/oc_username"
- style="@style/edittext_full"
- android:hint="@string/init_oc_username" />
+ <Button
+ android:id="@+id/register_oc_de"
+ style="@style/button_full"
+ android:text="@string/init_register_oc_de" />
<!-- ** -->
<RelativeLayout style="@style/separator_horizontal_layout" >
@@ -824,4 +849,4 @@
</LinearLayout>
</ScrollView>
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/main/res/values-cs/strings.xml b/main/res/values-cs/strings.xml
index db5ad9d..f29f7e6 100644
--- a/main/res/values-cs/strings.xml
+++ b/main/res/values-cs/strings.xml
@@ -364,8 +364,6 @@
<string name="init_gc_activate">Aktivace Geocaching.com na Aktivní mapě a ve vyhledávání</string>
<string name="init_oc">opencaching.de</string>
<string name="init_oc_activate">Aktivace opencaching.de na Aktivní mapě a ve vyhledávání</string>
- <string name="init_oc_username_description">Zadej své uživatelské jméno pro opencaching.de kvůli možnosti označovat nálezy.</string>
- <string name="init_oc_username">Zadej své uživatelské jméno</string>
<string name="init_gcvote">GCvote.com</string>
<string name="init_twitter">Twitter</string>
<string name="init_username">Uživatelské jméno</string>
diff --git a/main/res/values-de/strings.xml b/main/res/values-de/strings.xml
index 266e6ce..69838db 100644
--- a/main/res/values-de/strings.xml
+++ b/main/res/values-de/strings.xml
@@ -372,8 +372,8 @@
<string name="init_gc_activate">Geocaching.com auf Live-Karte und für die Suche aktivieren</string>
<string name="init_oc">opencaching.de</string>
<string name="init_oc_activate">Opencaching.de auf Live-Karte und für die Suche aktivieren</string>
- <string name="init_oc_username_description">Benutzername für opencaching.de eingeben damit gefundene Caches markiert werden können.</string>
- <string name="init_oc_username">Benutzername</string>
+ <string name="init_oc_de_description">Autorisiere c:geo dazu auf opencaching.de zuzugreifen um Caches zu suchen und nach deinen Funden zu filtern.</string>
+ <string name="init_register_oc_de">c:geo autorisieren</string>
<string name="init_gcvote">GCvote.com</string>
<string name="init_twitter">Twitter</string>
<string name="init_username">Benutzername</string>
@@ -486,7 +486,7 @@
<string name="sendToCgeo_download_fail">c:geo konnte Caches nicht laden. Entweder besteht keine Internetverbindung oder send2c:geo funktioniert nicht.</string>
<string name="sendToCgeo_no_registration">c:geo konnte Caches nicht laden. Registrierung für send2c:geo ungültig. Bitte in Einstellungen neu registrieren.</string>
- <!-- auth -->
+ <!-- auth twitter -->
<string name="auth_twitter">Twitter</string>
<string name="auth_authorize">c:geo autorisieren</string>
<string name="auth_start">Starte Autorisierung</string>
@@ -497,6 +497,22 @@
<string name="auth_dialog_pin_title">PIN Code</string>
<string name="auth_dialog_pin_message">Bitte den Twitter-PIN-Code eingeben, dies ist notwendig um die Autorisierung abzuschließen.</string>
<string name="auth_dialog_completed">c:geo ist nun autorisiert, Tweets bei Twitter zu erstellen.</string>
+ <string name="about_auth_1">Der folgende Prozess erlaubt es <b>c:geo</b> auf den persönlichen Twitter-Account zuzugreifen, wenn zugestimmt wird.</string>
+ <string name="about_auth_2">Ein Klick auf \"Starte Autorisierung\" öffnet eine Twitter-Seite in einem Browserfenster. Durch die Anmeldung und die Bestätigung wird <b>c:geo</b> ermöglicht, auf den persönlichen Twitter-Account zuzugreifen. Wird dies bestätigt, nennt Twitter eine numerische PIN, diese muss kopiert und in <b>c:geo</b> eingefügt werden. Das ist alles.</string>
+
+ <!-- auth opencaching -->
+ <string name="auth_ocde">opencaching.de</string>
+ <string name="auth_authorize_oc">c:geo autorisieren</string>
+ <string name="auth_start_oc">Starte Autorisierung</string>
+ <string name="auth_again_oc">Neustarten</string>
+ <string name="auth_pin_hint_oc">%s-PIN</string>
+ <string name="auth_finish_oc">Fertig</string>
+ <string name="auth_dialog_wait_oc">Warten auf %s…</string>
+ <string name="auth_dialog_pin_title_oc">PIN Code</string>
+ <string name="auth_dialog_pin_message_oc">Bitte den %s-PIN-Code eingeben, dies ist notwendig um die Autorisierung abzuschließen.</string>
+ <string name="auth_dialog_completed_oc">c:geo ist nun autorisiert, caches zu laden und auf %s zu loggen.</string>
+ <string name="about_auth_1_oc">Der folgende Prozess erlaubt es <b>c:geo</b> auf den persönlichen %s-Account zuzugreifen, wenn zugestimmt wird.</string>
+ <string name="about_auth_2_oc">Ein Klick auf \"Starte Autorisierung\" öffnet eine %s-Seite in einem Browserfenster. Durch die Anmeldung und die Bestätigung wird <b>c:geo</b> ermöglicht, auf den persönlichen Account zuzugreifen. Wird dies bestätigt, nennt %s eine numerische PIN, diese muss kopiert und in <b>c:geo</b> eingefügt werden. Das ist alles.</string>
<!-- cache -->
<plurals name="cache_counts">
@@ -1066,8 +1082,6 @@
<string name="twitter">Twitter: <a href="">@android_GC</a></string>
<string name="nutshellmanual">Benutzung: <a href="">c:geo Kurzanleitung</a></string>
<string name="about_twitter">Soll jeder neue Fund auf Twitter veröffentlicht werden, wenn er über <b>c:geo</b> geloggt wird?</string>
- <string name="about_auth_1">Der folgende Prozess erlaubt es <b>c:geo</b> auf den persönlichen Twitter-Account zuzugreifen, wenn zugestimmt wird.</string>
- <string name="about_auth_2">Ein Klick auf \"Starte Autorisierung\" öffnet eine Twitter-Seite in einem Browserfenster. Durch die Anmeldung und die Bestätigung wird <b>c:geo</b> ermöglicht, auf den persönlichen Twitter-Account zuzugreifen. Wird dies bestätigt, nennt Twitter eine numerische PIN, diese muss kopiert und in <b>c:geo</b> eingefügt werden. Das ist alles.</string>
<!-- status -->
<string name="status_new_release" tools:ignore="UnusedResources">Neuer Release verfügbar.\nKlicken zum Installieren.</string>
diff --git a/main/res/values-fr/strings.xml b/main/res/values-fr/strings.xml
index 12d59a7..9f25083 100644
--- a/main/res/values-fr/strings.xml
+++ b/main/res/values-fr/strings.xml
@@ -371,8 +371,6 @@
<string name="init_gc_activate">Utiliser Geocaching.com pour la carte active et les recherches.</string>
<string name="init_oc">opencaching.de</string>
<string name="init_oc_activate">Utiliser opencaching.de pour la carte active et les recherches.</string>
- <string name="init_oc_username_description">Entrer votre nom d\'utilisateur opencaching.de afin de pouvoir marquer vos trouvailles.</string>
- <string name="init_oc_username">Entrer votre nom d\'utilisateur</string>
<string name="init_gcvote">GCvote.com</string>
<string name="init_twitter">Twitter</string>
<string name="init_username">Identifiant</string>
diff --git a/main/res/values-it/strings.xml b/main/res/values-it/strings.xml
index 00551f5..98fdf5f 100644
--- a/main/res/values-it/strings.xml
+++ b/main/res/values-it/strings.xml
@@ -367,8 +367,6 @@
<string name="init_gc_activate">Attiva Geocaching.com su mappa live e nelle ricerche</string>
<string name="init_oc">opencaching.de</string>
<string name="init_oc_activate">Attiva opencaching.de su mappa live e nelle ricerche</string>
- <string name="init_oc_username_description">Inserisci il tuo utente opencaching.de per marcare i tuoi ritrovamenti.</string>
- <string name="init_oc_username">Inserisci nome utente</string>
<string name="init_gcvote">GCvote.com</string>
<string name="init_twitter">Twitter</string>
<string name="init_username">Utente</string>
diff --git a/main/res/values-ja/strings.xml b/main/res/values-ja/strings.xml
index 4bb15a2..b88577b 100644
--- a/main/res/values-ja/strings.xml
+++ b/main/res/values-ja/strings.xml
@@ -345,8 +345,6 @@
<string name="init_geocaching">Geocaching.com</string>
<string name="init_oc">opencaching.de</string>
<string name="init_oc_activate">オンライン地図や検索でopencaching.deも使用する</string>
- <string name="init_oc_username_description">見つけたキャッシュなどをマークするにはopencaching.deのユーザ名を入力してください。</string>
- <string name="init_oc_username">ユーザ名</string>
<string name="init_gcvote">GCvote.com</string>
<string name="init_twitter">Twitter</string>
<string name="init_username">ユーザ名</string>
diff --git a/main/res/values-nl/strings.xml b/main/res/values-nl/strings.xml
index 4eb1bc6..c827848 100644
--- a/main/res/values-nl/strings.xml
+++ b/main/res/values-nl/strings.xml
@@ -347,8 +347,6 @@
<string name="init_geocaching">Geocaching.com</string>
<string name="init_oc">opencaching.de</string>
<string name="init_oc_activate">Activeer opencaching.de op live-kaart en in zoekopdrachten.</string>
- <string name="init_oc_username_description">Vul je opencaching.de gebruikersnaam in op caches als gevonden te markeren.</string>
- <string name="init_oc_username">Vul gebruikersnaam in</string>
<string name="init_gcvote">GCvote.com</string>
<string name="init_twitter">Twitter</string>
<string name="init_username">Gebruikersnaam</string>
diff --git a/main/res/values-pl/strings.xml b/main/res/values-pl/strings.xml
index 50a383f..e736bbb 100644
--- a/main/res/values-pl/strings.xml
+++ b/main/res/values-pl/strings.xml
@@ -364,8 +364,6 @@
<string name="init_gc_activate">Aktywuj Geocaching.com na aktualnej mapie i w wyszukiwaniu</string>
<string name="init_oc">opencaching.de</string>
<string name="init_oc_activate">Aktywuj opencaching.de na mapie Live i w wyszukiwaniu</string>
- <string name="init_oc_username_description">Wpisz swoją nazwę użytkownika opencaching.de w celu umożliwienia zaznaczania znalezionych przez Ciebie skrzynek.</string>
- <string name="init_oc_username">Wpisz swoją nazwę użytkownika</string>
<string name="init_gcvote">GCvote.com</string>
<string name="init_twitter">Twitter</string>
<string name="init_username">Nazwa użytkownika</string>
diff --git a/main/res/values-sv/strings.xml b/main/res/values-sv/strings.xml
index 51bee26..8501cf7 100644
--- a/main/res/values-sv/strings.xml
+++ b/main/res/values-sv/strings.xml
@@ -371,8 +371,6 @@
<string name="init_gc_activate">Aktivera Geocaching.com i live-kartor och sökningar</string>
<string name="init_oc">opencaching.de</string>
<string name="init_oc_activate">Aktivera opencaching.de i live-kartor och sökningar</string>
- <string name="init_oc_username_description">Ange användarnamn till opencaching.de för att kunna markera dina hittade cacher.</string>
- <string name="init_oc_username">Ange ditt användarnamn</string>
<string name="init_gcvote">GCvote.com</string>
<string name="init_twitter">Twitter</string>
<string name="init_username">Användare</string>
diff --git a/main/res/values/.gitignore b/main/res/values/.gitignore
new file mode 100644
index 0000000..1f65bef
--- /dev/null
+++ b/main/res/values/.gitignore
@@ -0,0 +1 @@
+/ocde_okapi.xml
diff --git a/main/res/values/strings.xml b/main/res/values/strings.xml
index 4d9202e..43f87ea 100644
--- a/main/res/values/strings.xml
+++ b/main/res/values/strings.xml
@@ -371,8 +371,8 @@
<string name="init_gc_activate">Activate Geocaching.com on live-map and in searches</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_oc_de_description">Authorize c:geo with opencaching.de to search for caches and access/filter your found caches.</string>
+ <string name="init_register_oc_de">Authorize c:geo</string>
<string name="init_gcvote">GCvote.com</string>
<string name="init_twitter">Twitter</string>
<string name="init_username">Username</string>
@@ -486,8 +486,7 @@
<string name="sendToCgeo_download_fail">c:geo failed to download caches. No internet connection or send2c:geo is down.</string>
<string name="sendToCgeo_no_registration">c:geo failed to download caches. Registration for send2c:geo expired. Please register in settings.</string>
-
- <!-- auth -->
+ <!-- auth twitter -->
<string name="auth_twitter">Twitter</string>
<string name="auth_authorize">Authorize c:geo</string>
<string name="auth_start">Start authorization</string>
@@ -498,6 +497,22 @@
<string name="auth_dialog_pin_title">PIN code</string>
<string name="auth_dialog_pin_message">Please type in PIN code provided by the Twitter website. It is mandatory to complete the authorization.</string>
<string name="auth_dialog_completed">c:geo is now authorized to post on Twitter.</string>
+ <string name="about_auth_1">The following process allows <b>c:geo</b> to access Twitter - if agreed.</string>
+ <string name="about_auth_2">A click on the \"authorize c:geo\" button will start the process. This process will open up a web browser with a Twitter page. Login on this page and allow <b>c:geo</b> to access your account. If this is accepted, Twitter will show up a numeric PIN code. This PIN must be pasted into <b>c:geo</b> and confirmed. That\'s all.</string>
+
+ <!-- auth opencaching -->
+ <string name="auth_ocde">opencaching.de</string>
+ <string name="auth_authorize_oc">Authorize c:geo</string>
+ <string name="auth_start_oc">Start authorization</string>
+ <string name="auth_again_oc">Start again</string>
+ <string name="auth_pin_hint_oc">PIN assigned by %s</string>
+ <string name="auth_finish_oc">Finish</string>
+ <string name="auth_dialog_wait_oc">Waiting for %s…</string>
+ <string name="auth_dialog_pin_title_oc">PIN code</string>
+ <string name="auth_dialog_pin_message_oc">Please type in PIN code provided by the %s website. It is mandatory to complete the authorization.</string>
+ <string name="auth_dialog_completed_oc">c:geo is now authorized to interact with %s.</string>
+ <string name="about_auth_1_oc">The following process allows <b>c:geo</b> to access %s - if agreed.</string>
+ <string name="about_auth_2_oc">A click on the \"authorize c:geo\" button will start the process. This process will open up a web browser with a %s page. Login on this page and allow <b>c:geo</b> to access your account. If this is accepted, %s will show up a numeric PIN code. This PIN must be pasted into <b>c:geo</b> and confirmed. That\'s all.</string>
<!-- cache -->
<plurals name="cache_counts">
@@ -1070,8 +1085,6 @@
<string name="nutshellmanual">Manual: <a href="">c:geo in a Nutshell</a></string>
<string name="market">Android: <a href="">c:geo on Google Play</a></string>
<string name="about_twitter">Should <b>c:geo</b> publish a new status on Twitter every time you log a cache?</string>
- <string name="about_auth_1">The following process allows <b>c:geo</b> to access Twitter - if agreed.</string>
- <string name="about_auth_2">A click on the \"authorize c:geo\" button will start the process. This process will open up a web browser with a Twitter page. Login on this page and allow <b>c:geo</b> to access your account. If this is accepted, Twitter will show up a numeric PIN code. This PIN must be pasted into <b>c:geo</b> and confirmed. That\'s all.</string>
<!-- status (used via string based resource loading) -->
<string name="status_new_release" tools:ignore="UnusedResources">New release available.\nClick to install.</string>
diff --git a/main/src/cgeo/geocaching/CacheDetailActivity.java b/main/src/cgeo/geocaching/CacheDetailActivity.java
index fe6693b..33a6504 100644
--- a/main/src/cgeo/geocaching/CacheDetailActivity.java
+++ b/main/src/cgeo/geocaching/CacheDetailActivity.java
@@ -117,7 +117,7 @@ import java.util.regex.Pattern;
* e.g. details, description, logs, waypoints, inventory...
*/
public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailActivity.Page>
- implements EditNoteDialogListener {
+ implements EditNoteDialogListener {
private static final int MENU_FIELD_COPY = 1;
private static final int MENU_FIELD_TRANSLATE = 2;
@@ -259,6 +259,23 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
finish();
return;
}
+ } else if (uriHost.contains("opencaching.de")) {
+ if (uriPath != null && uriPath.startsWith("/oc")) {
+ geocode = uriPath.substring(1).toUpperCase(Locale.US);
+ } else {
+ geocode = uri.getQueryParameter("wp");
+ if (StringUtils.isNotBlank(geocode)) {
+ geocode = geocode.toUpperCase(Locale.US);
+ } else {
+ showToast(res.getString(R.string.err_detail_open));
+ finish();
+ return;
+ }
+ }
+ } else {
+ showToast(res.getString(R.string.err_detail_open));
+ finish();
+ return;
}
}
@@ -1472,7 +1489,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
@Override
public void run() {
- handler.sendEmptyMessage(GCConnector.addToWatchlist(cache) ? 1 : -1);
+ handler.sendEmptyMessage(ConnectorFactory.getConnector(cache).addToWatchlist(cache) ? 1 : -1);
}
}
@@ -1486,7 +1503,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
@Override
public void run() {
- handler.sendEmptyMessage(GCConnector.removeFromWatchlist(cache) ? 1 : -1);
+ handler.sendEmptyMessage(ConnectorFactory.getConnector(cache).removeFromWatchlist(cache) ? 1 : -1);
}
}
@@ -2242,8 +2259,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
}
/**
- * Read the position of the cursor pointed to by this holder.
- * <br/>
+ * Read the position of the cursor pointed to by this holder. <br/>
* This must be called by the UI thread.
*
* @return the cursor position
@@ -2253,11 +2269,11 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
}
/**
- * Set the position of the cursor pointed to by this holder.
- * <br/>
+ * Set the position of the cursor pointed to by this holder. <br/>
* This must be called by the UI thread.
*
- * @param position the cursor position
+ * @param position
+ * the cursor position
*/
public void setPosition(final int position) {
this.position = position;
diff --git a/main/src/cgeo/geocaching/Geocache.java b/main/src/cgeo/geocaching/Geocache.java
index 304f940..ac1c9b1 100644
--- a/main/src/cgeo/geocaching/Geocache.java
+++ b/main/src/cgeo/geocaching/Geocache.java
@@ -5,6 +5,7 @@ import cgeo.geocaching.activity.ActivityMixin;
import cgeo.geocaching.activity.IAbstractActivity;
import cgeo.geocaching.connector.ConnectorFactory;
import cgeo.geocaching.connector.IConnector;
+import cgeo.geocaching.connector.ILoggingManager;
import cgeo.geocaching.connector.capability.ISearchByCenter;
import cgeo.geocaching.connector.capability.ISearchByGeocode;
import cgeo.geocaching.connector.gc.GCConnector;
@@ -455,7 +456,7 @@ public class Geocache implements ICache, IWaypoint {
}
public void logVisit(final IAbstractActivity fromActivity) {
- if (StringUtils.isBlank(cacheId)) {
+ if (!getConnector().canLog(this)) {
fromActivity.showToast(((Activity) fromActivity).getResources().getString(R.string.err_cannot_log_visit));
return;
}
@@ -562,6 +563,10 @@ public class Geocache implements ICache, IWaypoint {
return getConnector().supportsLogging();
}
+ public boolean supportsLogImages() {
+ return getConnector().supportsLogImages();
+ }
+
public boolean supportsOwnCoordinates() {
return getConnector().supportsOwnCoordinates();
}
@@ -570,6 +575,10 @@ public class Geocache implements ICache, IWaypoint {
return getConnector().getCacheRealm();
}
+ public ILoggingManager getLoggingManager(Activity activity) {
+ return getConnector().getLoggingManager(activity, this);
+ }
+
@Override
public float getDifficulty() {
return difficulty;
diff --git a/main/src/cgeo/geocaching/MainActivity.java b/main/src/cgeo/geocaching/MainActivity.java
index 9b23554..4393eda 100644
--- a/main/src/cgeo/geocaching/MainActivity.java
+++ b/main/src/cgeo/geocaching/MainActivity.java
@@ -697,6 +697,10 @@ public class MainActivity extends AbstractActivity {
return;
}
+ if (!Settings.isGCConnectorActive()) {
+ return;
+ }
+
// login
final StatusCode status = Login.login();
diff --git a/main/src/cgeo/geocaching/Settings.java b/main/src/cgeo/geocaching/Settings.java
index b5c8a6e..65283da 100644
--- a/main/src/cgeo/geocaching/Settings.java
+++ b/main/src/cgeo/geocaching/Settings.java
@@ -40,8 +40,8 @@ import java.util.Locale;
*/
public final class Settings {
- private static final String KEY_TEMP_TOKEN_SECRET = "temp-token-secret";
- private static final String KEY_TEMP_TOKEN_PUBLIC = "temp-token-public";
+ private static final String KEY_TEMP_TWITTER_TOKEN_SECRET = "temp-token-secret";
+ private static final String KEY_TEMP_TWITTER_TOKEN_PUBLIC = "temp-token-public";
private static final String KEY_HELP_SHOWN = "helper";
private static final String KEY_ANYLONGITUDE = "anylongitude";
private static final String KEY_ANYLATITUDE = "anylatitude";
@@ -110,9 +110,14 @@ 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_GC_ACTIVE = "connectorGCActive";
private static final String KEY_CONNECTOR_OC_ACTIVE = "connectorOCActive";
- private static final String KEY_CONNECTOR_OC_USER = "connectorOCUser";
private static final String KEY_LOG_IMAGE_SCALE = "logImageScale";
+ private static final String KEY_OCDE_TOKEN_SECRET = "ocde_tokensecret";
+ private static final String KEY_OCDE_TOKEN_PUBLIC = "ocde_tokenpublic";
+ private static final String KEY_TEMP_OCDE_TOKEN_SECRET = "ocde-temp-token-secret";
+ private static final String KEY_TEMP_OCDE_TOKEN_PUBLIC = "ocde-temp-token-public";
+
private final static int unitsMetric = 1;
@@ -163,8 +168,8 @@ public final class Settings {
final SharedPreferences old = cgeoapplication.getInstance().getSharedPreferences(oldPreferencesName, Context.MODE_PRIVATE);
final Editor e = sharedPrefs.edit();
- e.putString(KEY_TEMP_TOKEN_SECRET, old.getString(KEY_TEMP_TOKEN_SECRET, null));
- e.putString(KEY_TEMP_TOKEN_PUBLIC, old.getString(KEY_TEMP_TOKEN_PUBLIC, null));
+ e.putString(KEY_TEMP_TWITTER_TOKEN_SECRET, old.getString(KEY_TEMP_TWITTER_TOKEN_SECRET, null));
+ e.putString(KEY_TEMP_TWITTER_TOKEN_PUBLIC, old.getString(KEY_TEMP_TWITTER_TOKEN_PUBLIC, null));
e.putBoolean(KEY_HELP_SHOWN, old.getInt(KEY_HELP_SHOWN, 0) != 0);
e.putFloat(KEY_ANYLONGITUDE, old.getFloat(KEY_ANYLONGITUDE, 0));
e.putFloat(KEY_ANYLATITUDE, old.getFloat(KEY_ANYLATITUDE, 0));
@@ -284,6 +289,20 @@ public final class Settings {
});
}
+ public static boolean isGCConnectorActive() {
+ return sharedPrefs.getBoolean(KEY_CONNECTOR_GC_ACTIVE, true);
+ }
+
+ public static boolean setGCConnectorActive(final boolean isActive) {
+ return editSharedSettings(new PrefRunnable() {
+
+ @Override
+ public void edit(Editor edit) {
+ edit.putBoolean(KEY_CONNECTOR_GC_ACTIVE, isActive);
+ }
+ });
+ }
+
public static boolean isPremiumMember() {
// Basic Member, Premium Member, ???
String memberStatus = Settings.getMemberStatus();
@@ -325,26 +344,44 @@ public final class Settings {
});
}
- public static String getOCConnectorUserName() {
- String ocConnectorUser = sharedPrefs.getString(KEY_CONNECTOR_OC_USER, null);
- if (StringUtils.isBlank(ocConnectorUser)) {
- return StringUtils.EMPTY;
- }
- return ocConnectorUser;
+ public static String getOCDETokenPublic() {
+ return sharedPrefs.getString(KEY_OCDE_TOKEN_PUBLIC, "");
}
- public static boolean setOCConnectorUserName(final String userName) {
- return editSharedSettings(new PrefRunnable() {
+ public static String getOCDETokenSecret() {
+ return sharedPrefs.getString(KEY_OCDE_TOKEN_SECRET, "");
+ }
+
+ public static void setOCDETokens(final String tokenPublic, final String tokenSecret, boolean enableOcDe) {
+ 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);
+ edit.putString(KEY_OCDE_TOKEN_PUBLIC, tokenPublic);
+ edit.putString(KEY_OCDE_TOKEN_SECRET, tokenSecret);
+ if (tokenPublic != null) {
+ edit.remove(KEY_TEMP_OCDE_TOKEN_PUBLIC);
+ edit.remove(KEY_TEMP_OCDE_TOKEN_SECRET);
}
}
});
+ setOCConnectorActive(enableOcDe);
+ }
+
+ public static void setOCDETempTokens(final String tokenPublic, final String tokenSecret) {
+ editSharedSettings(new PrefRunnable() {
+ @Override
+ public void edit(Editor edit) {
+ edit.putString(KEY_TEMP_OCDE_TOKEN_PUBLIC, tokenPublic);
+ edit.putString(KEY_TEMP_OCDE_TOKEN_SECRET, tokenSecret);
+ }
+ });
+ }
+
+ public static ImmutablePair<String, String> getTempOCDEToken() {
+ String tokenPublic = sharedPrefs.getString(KEY_TEMP_OCDE_TOKEN_PUBLIC, null);
+ String tokenSecret = sharedPrefs.getString(KEY_TEMP_OCDE_TOKEN_SECRET, null);
+ return new ImmutablePair<String, String>(tokenPublic, tokenSecret);
}
public static boolean isGCvoteLogin() {
@@ -1122,8 +1159,8 @@ public final class Settings {
edit.putString(KEY_TWITTER_TOKEN_PUBLIC, tokenPublic);
edit.putString(KEY_TWITTER_TOKEN_SECRET, tokenSecret);
if (tokenPublic != null) {
- edit.remove(KEY_TEMP_TOKEN_PUBLIC);
- edit.remove(KEY_TEMP_TOKEN_SECRET);
+ edit.remove(KEY_TEMP_TWITTER_TOKEN_PUBLIC);
+ edit.remove(KEY_TEMP_TWITTER_TOKEN_SECRET);
}
}
});
@@ -1134,15 +1171,15 @@ public final class Settings {
editSharedSettings(new PrefRunnable() {
@Override
public void edit(Editor edit) {
- edit.putString(KEY_TEMP_TOKEN_PUBLIC, tokenPublic);
- edit.putString(KEY_TEMP_TOKEN_SECRET, tokenSecret);
+ edit.putString(KEY_TEMP_TWITTER_TOKEN_PUBLIC, tokenPublic);
+ edit.putString(KEY_TEMP_TWITTER_TOKEN_SECRET, tokenSecret);
}
});
}
public static ImmutablePair<String, String> getTempToken() {
- String tokenPublic = sharedPrefs.getString(KEY_TEMP_TOKEN_PUBLIC, null);
- String tokenSecret = sharedPrefs.getString(KEY_TEMP_TOKEN_SECRET, null);
+ String tokenPublic = sharedPrefs.getString(KEY_TEMP_TWITTER_TOKEN_PUBLIC, null);
+ String tokenSecret = sharedPrefs.getString(KEY_TEMP_TWITTER_TOKEN_SECRET, null);
return new ImmutablePair<String, String>(tokenPublic, tokenSecret);
}
diff --git a/main/src/cgeo/geocaching/SettingsActivity.java b/main/src/cgeo/geocaching/SettingsActivity.java
index f09f7d6..aa163ad 100644
--- a/main/src/cgeo/geocaching/SettingsActivity.java
+++ b/main/src/cgeo/geocaching/SettingsActivity.java
@@ -5,6 +5,7 @@ import cgeo.geocaching.apps.cache.navi.NavigationAppFactory;
import cgeo.geocaching.apps.cache.navi.NavigationAppFactory.NavigationAppsEnum;
import cgeo.geocaching.compatibility.Compatibility;
import cgeo.geocaching.connector.gc.Login;
+import cgeo.geocaching.connector.oc.OCAuthorizationActivity;
import cgeo.geocaching.enumerations.StatusCode;
import cgeo.geocaching.files.SimpleDirChooser;
import cgeo.geocaching.maps.MapProviderFactory;
@@ -169,7 +170,6 @@ public class SettingsActivity extends AbstractActivity {
((EditText) findViewById(R.id.username)).setText("");
((EditText) findViewById(R.id.password)).setText("");
((EditText) findViewById(R.id.passvote)).setText("");
- ((EditText) findViewById(R.id.oc_username)).setText("");
if (saveValues()) {
showToast(res.getString(R.string.init_cleared));
@@ -213,6 +213,15 @@ public class SettingsActivity extends AbstractActivity {
public void init() {
// geocaching.com settings
+ final CheckBox gcCheck = (CheckBox) findViewById(R.id.gc_option);
+ gcCheck.setChecked(Settings.isGCConnectorActive());
+ gcCheck.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Settings.setGCConnectorActive(gcCheck.isChecked());
+ }
+ });
final ImmutablePair<String, String> login = Settings.getLogin();
if (login != null) {
((EditText) findViewById(R.id.username)).setText(login.left);
@@ -242,10 +251,9 @@ public class SettingsActivity extends AbstractActivity {
Settings.setOCConnectorActive(ocCheck.isChecked());
}
});
- EditText ocUserEdit = (EditText) findViewById(R.id.oc_username);
- if (ocUserEdit.getText().length() == 0) {
- ocUserEdit.setText(Settings.getOCConnectorUserName());
- }
+
+ Button checkOCUser = (Button) findViewById(R.id.register_oc_de);
+ checkOCUser.setOnClickListener(new OCDEAuthorizeCgeoListener());
// gcvote settings
final ImmutablePair<String, String> gcvoteLogin = Settings.getGCvoteLogin();
@@ -829,7 +837,6 @@ 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);
@@ -843,7 +850,6 @@ 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());
@@ -851,7 +857,7 @@ public class SettingsActivity extends AbstractActivity {
Settings.setGpxImportDir(importNew);
Settings.setGpxExportDir(exportNew);
- return status1 && status2 && status3 && status4 && status5 && status6 && status7;
+ return status1 && status2 && status3 && status4 && status5 && status6;
}
/**
@@ -920,6 +926,15 @@ public class SettingsActivity extends AbstractActivity {
}
}
+ private class OCDEAuthorizeCgeoListener implements View.OnClickListener {
+
+ @Override
+ public void onClick(View v) {
+ Intent authIntent = new Intent(SettingsActivity.this, OCAuthorizationActivity.class);
+ startActivity(authIntent);
+ }
+ }
+
private class WebAuthListener implements View.OnClickListener {
@Override
diff --git a/main/src/cgeo/geocaching/VisitCacheActivity.java b/main/src/cgeo/geocaching/VisitCacheActivity.java
index c19cceb..e99da8d 100644
--- a/main/src/cgeo/geocaching/VisitCacheActivity.java
+++ b/main/src/cgeo/geocaching/VisitCacheActivity.java
@@ -1,14 +1,13 @@
package cgeo.geocaching;
-import cgeo.geocaching.connector.gc.GCParser;
-import cgeo.geocaching.connector.gc.Login;
+import cgeo.geocaching.connector.ILoggingManager;
+import cgeo.geocaching.connector.ImageResult;
+import cgeo.geocaching.connector.LogResult;
import cgeo.geocaching.enumerations.LoadFlags;
import cgeo.geocaching.enumerations.LogType;
import cgeo.geocaching.enumerations.LogTypeTrackable;
import cgeo.geocaching.enumerations.StatusCode;
import cgeo.geocaching.gcvote.GCVote;
-import cgeo.geocaching.loaders.UrlLoader;
-import cgeo.geocaching.network.Parameters;
import cgeo.geocaching.twitter.Twitter;
import cgeo.geocaching.ui.Formatter;
import cgeo.geocaching.ui.dialog.DateDialog;
@@ -20,7 +19,6 @@ import cgeo.geocaching.utils.LogTemplateProvider.LogContext;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.tuple.ImmutablePair;
import android.app.Activity;
import android.app.AlertDialog;
@@ -31,8 +29,6 @@ import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.support.v4.app.LoaderManager;
-import android.support.v4.content.Loader;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -51,7 +47,7 @@ import java.util.Date;
import java.util.List;
import java.util.Locale;
-public class VisitCacheActivity extends AbstractLoggingActivity implements DateDialog.DateDialogParent, LoaderManager.LoaderCallbacks<String> {
+public class VisitCacheActivity extends AbstractLoggingActivity implements DateDialog.DateDialogParent {
static final String EXTRAS_GEOCODE = "geocode";
static final String EXTRAS_ID = "id";
@@ -71,7 +67,6 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
private String geocode = null;
private String text = null;
private List<LogType> possibleLogTypes = new ArrayList<LogType>();
- private String[] viewstates = null;
private List<TrackableLog> trackables = null;
private Button postButton = null;
private CheckBox tweetCheck = null;
@@ -79,6 +74,8 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
private boolean tbChanged = false;
private SparseArray<TrackableLog> actionButtons;
+ private ILoggingManager loggingManager;
+
// Data to be saved while reconfiguring
private double rating;
private LogType typeSelected;
@@ -87,30 +84,16 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
private String imageDescription;
private Uri imageUri;
- @Override
- public Loader<String> onCreateLoader(final int id, final Bundle args) {
- if (!Settings.isLogin()) { // allow offline logging
- showToast(res.getString(R.string.err_login));
- return null;
- }
- return new UrlLoader(getBaseContext(), "http://www.geocaching.com/seek/log.aspx", new Parameters("ID", cacheid));
- }
- @Override
- public void onLoaderReset(final Loader<String> loader) {
- // Nothing to do
- }
+ public void onLoadFinished() {
- @Override
- public void onLoadFinished(final Loader<String> loader, final String page) {
- if (page == null) {
+ if (loggingManager.hasLoaderError()) {
showErrorLoadingData();
return;
}
- viewstates = Login.getViewstates(page);
- trackables = GCParser.parseTrackableLog(page);
- possibleLogTypes = GCParser.parseTypes(page);
+ trackables = loggingManager.getTrackables();
+ possibleLogTypes = loggingManager.getPossibleLogTypes();
if (possibleLogTypes.isEmpty()) {
showErrorLoadingData();
@@ -229,7 +212,7 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
if (!postButton.isEnabled()) {
return res.getString(R.string.log_post_not_possible);
}
- if (typeSelected != LogType.FOUND_IT || !Settings.isGCvoteLogin()) {
+ if (typeSelected != LogType.FOUND_IT || !Settings.isGCvoteLogin() || !cache.supportsGCVote()) {
return res.getString(R.string.log_post);
}
if (rating == 0) {
@@ -295,7 +278,7 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
}
}
updatePostButtonText();
- setImageButtonText();
+ updateImageButton();
enablePostButton(false);
final Button typeButton = (Button) findViewById(R.id.type);
@@ -347,7 +330,9 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
}
});
- getSupportLoaderManager().initLoader(0, null, this);
+ loggingManager = cache.getLoggingManager(this);
+
+ loggingManager.init();
}
private void setDefaultValues() {
@@ -393,7 +378,7 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
final EditText logView = (EditText) findViewById(R.id.log);
logView.setText(StringUtils.EMPTY);
- setImageButtonText();
+ updateImageButton();
showToast(res.getString(R.string.info_log_cleared));
}
@@ -433,7 +418,7 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- final boolean voteAvailable = Settings.isGCvoteLogin() && typeSelected == LogType.FOUND_IT && StringUtils.isNotBlank(cache.getGuid());
+ final boolean voteAvailable = Settings.isGCvoteLogin() && typeSelected == LogType.FOUND_IT && StringUtils.isNotBlank(cache.getGuid()) && cache.supportsGCVote();
menu.findItem(SUBMENU_VOTE).setVisible(voteAvailable);
return true;
@@ -536,11 +521,9 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
protected StatusCode doInBackgroundInternal(final String[] logTexts) {
final String log = logTexts[0];
try {
- final ImmutablePair<StatusCode, String> postResult = GCParser.postLog(geocode, cacheid, viewstates, typeSelected,
- date.get(Calendar.YEAR), (date.get(Calendar.MONTH) + 1), date.get(Calendar.DATE),
- log, trackables);
+ final LogResult logResult = loggingManager.postLog(cache, typeSelected, date, log, trackables);
- if (postResult.left == StatusCode.NO_ERROR) {
+ if (logResult.getPostLogResult() == StatusCode.NO_ERROR) {
final LogEntry logNow = new LogEntry(date, typeSelected, log);
cache.getLogs().add(0, logNow);
@@ -560,17 +543,17 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
}
if (StringUtils.isNotBlank(imageUri.getPath())) {
- ImmutablePair<StatusCode, String> imageResult = GCParser.uploadLogImage(postResult.right, imageCaption, imageDescription, imageUri);
- final String uploadedImageUrl = imageResult.right;
+ ImageResult imageResult = loggingManager.postLogImage(logResult.getLogId(), imageCaption, imageDescription, imageUri);
+ final String uploadedImageUrl = imageResult.getImageUri();
if (StringUtils.isNotEmpty(uploadedImageUrl)) {
logNow.addLogImage(new Image(uploadedImageUrl, imageCaption, imageDescription));
cgData.saveChangedCache(cache);
}
- return imageResult.left;
+ return imageResult.getPostResult();
}
}
- return postResult.left;
+ return logResult.getPostLogResult();
} catch (Exception e) {
Log.e("cgeovisit.postLogFn", e);
}
@@ -705,14 +688,19 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
// Image capture failed, advise user
showToast(getResources().getString(R.string.err_select_logimage_failed));
}
- setImageButtonText();
+ updateImageButton();
}
}
- private void setImageButtonText() {
+ private void updateImageButton() {
final Button imageButton = (Button) findViewById(R.id.image_btn);
- imageButton.setText(StringUtils.isNotBlank(imageUri.getPath()) ?
+ if (cache.supportsLogImages()) {
+ imageButton.setVisibility(View.VISIBLE);
+ imageButton.setText(StringUtils.isNotBlank(imageUri.getPath()) ?
res.getString(R.string.log_image_edit) : res.getString(R.string.log_image_attach));
+ } else {
+ imageButton.setVisibility(View.GONE);
+ }
}
}
diff --git a/main/src/cgeo/geocaching/connector/AbstractConnector.java b/main/src/cgeo/geocaching/connector/AbstractConnector.java
index 413291c..905382f 100644
--- a/main/src/cgeo/geocaching/connector/AbstractConnector.java
+++ b/main/src/cgeo/geocaching/connector/AbstractConnector.java
@@ -6,6 +6,8 @@ import cgeo.geocaching.geopoint.Geopoint;
import org.apache.commons.lang3.StringUtils;
+import android.app.Activity;
+
public abstract class AbstractConnector implements IConnector {
@Override
@@ -19,6 +21,16 @@ public abstract class AbstractConnector implements IConnector {
}
@Override
+ public boolean addToWatchlist(Geocache cache) {
+ return false;
+ }
+
+ @Override
+ public boolean removeFromWatchlist(Geocache cache) {
+ return false;
+ }
+
+ @Override
public boolean supportsOwnCoordinates() {
return false;
}
@@ -54,6 +66,21 @@ public abstract class AbstractConnector implements IConnector {
}
@Override
+ public boolean supportsLogImages() {
+ return false;
+ }
+
+ @Override
+ public boolean canLog(Geocache cache) {
+ return false;
+ }
+
+ @Override
+ public ILoggingManager getLoggingManager(Activity activity, Geocache cache) {
+ return new NoLoggingManager();
+ }
+
+ @Override
public String getLicenseText(final Geocache cache) {
return null;
}
diff --git a/main/src/cgeo/geocaching/connector/ConnectorFactory.java b/main/src/cgeo/geocaching/connector/ConnectorFactory.java
index 561bae2..50f56af 100644
--- a/main/src/cgeo/geocaching/connector/ConnectorFactory.java
+++ b/main/src/cgeo/geocaching/connector/ConnectorFactory.java
@@ -1,14 +1,15 @@
package cgeo.geocaching.connector;
import cgeo.geocaching.ICache;
+import cgeo.geocaching.R;
import cgeo.geocaching.SearchResult;
import cgeo.geocaching.Trackable;
import cgeo.geocaching.connector.capability.ISearchByCenter;
import cgeo.geocaching.connector.capability.ISearchByViewPort;
import cgeo.geocaching.connector.gc.GCConnector;
import cgeo.geocaching.connector.oc.OCApiConnector;
+import cgeo.geocaching.connector.oc.OCApiLiveConnector;
import cgeo.geocaching.connector.oc.OCConnector;
-import cgeo.geocaching.connector.oc.OCXMLApiConnector;
import cgeo.geocaching.connector.ox.OXConnector;
import cgeo.geocaching.geopoint.Viewport;
@@ -21,7 +22,7 @@ public final class ConnectorFactory {
private static final UnknownConnector UNKNOWN_CONNECTOR = new UnknownConnector();
private static final IConnector[] connectors = new IConnector[] {
GCConnector.getInstance(),
- new OCXMLApiConnector("OpenCaching.DE", "www.opencaching.de", "OC"),
+ new OCApiLiveConnector("Opencaching.de", "www.opencaching.de", "OC", R.string.oc_de_okapi_consumer_key, R.string.oc_de_okapi_consumer_secret),
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"),
diff --git a/main/src/cgeo/geocaching/connector/IConnector.java b/main/src/cgeo/geocaching/connector/IConnector.java
index 9169b4a..011c507 100644
--- a/main/src/cgeo/geocaching/connector/IConnector.java
+++ b/main/src/cgeo/geocaching/connector/IConnector.java
@@ -5,6 +5,8 @@ import cgeo.geocaching.ICache;
import cgeo.geocaching.enumerations.CacheRealm;
import cgeo.geocaching.geopoint.Geopoint;
+import android.app.Activity;
+
public interface IConnector {
/**
* get name for display (currently only used in links)
@@ -45,6 +47,22 @@ public interface IConnector {
public boolean supportsWatchList();
/**
+ * Add the cache to the watchlist
+ *
+ * @param cache
+ * @return True - success/False - failure
+ */
+ public boolean addToWatchlist(Geocache cache);
+
+ /**
+ * Remove the cache from the watchlist
+ *
+ * @param cache
+ * @return True - success/False - failure
+ */
+ public boolean removeFromWatchlist(Geocache cache);
+
+ /**
* enable/disable favorite points controls in cache details
*
* @return
@@ -59,6 +77,20 @@ public interface IConnector {
public boolean supportsLogging();
/**
+ * enable/disable attaching image to log
+ *
+ * @return
+ */
+ public boolean supportsLogImages();
+
+ /**
+ * Get an ILoggingManager to guide the logging process.
+ *
+ * @return
+ */
+ public ILoggingManager getLoggingManager(Activity activity, Geocache cache);
+
+ /**
* get host name of the connector server for dynamic loading of data
*
* @return
@@ -159,4 +191,13 @@ public interface IConnector {
* @return <code>true</code> if the current user is the cache owner, <code>false</code> otherwise
*/
public boolean isOwner(final ICache cache);
+
+ /**
+ * Check if the cache information is complete enough to be
+ * able to log online.
+ *
+ * @param geocache
+ * @return
+ */
+ public boolean canLog(Geocache geocache);
}
diff --git a/main/src/cgeo/geocaching/connector/ILoggingManager.java b/main/src/cgeo/geocaching/connector/ILoggingManager.java
new file mode 100644
index 0000000..f0029f9
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/ILoggingManager.java
@@ -0,0 +1,32 @@
+package cgeo.geocaching.connector;
+
+import cgeo.geocaching.Geocache;
+import cgeo.geocaching.TrackableLog;
+import cgeo.geocaching.enumerations.LogType;
+
+import android.net.Uri;
+
+import java.util.Calendar;
+import java.util.List;
+
+public interface ILoggingManager {
+
+ LogResult postLog(Geocache cache,
+ LogType logType,
+ Calendar date,
+ String log,
+ List<TrackableLog> trackableLogs);
+
+ ImageResult postLogImage(String logId,
+ String imageCaption,
+ String imageDescription,
+ Uri imageUri);
+
+ public boolean hasLoaderError();
+
+ public List<TrackableLog> getTrackables();
+
+ public List<LogType> getPossibleLogTypes();
+
+ public void init();
+}
diff --git a/main/src/cgeo/geocaching/connector/ImageResult.java b/main/src/cgeo/geocaching/connector/ImageResult.java
new file mode 100644
index 0000000..9314cad
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/ImageResult.java
@@ -0,0 +1,23 @@
+package cgeo.geocaching.connector;
+
+import cgeo.geocaching.enumerations.StatusCode;
+
+public class ImageResult {
+
+ private final StatusCode postResult;
+ private final String imageUri;
+
+ public ImageResult(StatusCode postResult, String imageUri) {
+ this.postResult = postResult;
+ this.imageUri = imageUri;
+ }
+
+ public StatusCode getPostResult() {
+ return postResult;
+ }
+
+ public String getImageUri() {
+ return imageUri;
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/connector/LogResult.java b/main/src/cgeo/geocaching/connector/LogResult.java
new file mode 100644
index 0000000..62111a4
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/LogResult.java
@@ -0,0 +1,23 @@
+package cgeo.geocaching.connector;
+
+import cgeo.geocaching.enumerations.StatusCode;
+
+public class LogResult {
+
+ private final StatusCode postLogResult;
+ private final String logId;
+
+ public LogResult(StatusCode postLogResult, String logId) {
+ this.postLogResult = postLogResult;
+ this.logId = logId;
+ }
+
+ public StatusCode getPostLogResult() {
+ return postLogResult;
+ }
+
+ public String getLogId() {
+ return logId;
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/connector/NoLoggingManager.java b/main/src/cgeo/geocaching/connector/NoLoggingManager.java
new file mode 100644
index 0000000..bfea4ca
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/NoLoggingManager.java
@@ -0,0 +1,46 @@
+package cgeo.geocaching.connector;
+
+import cgeo.geocaching.Geocache;
+import cgeo.geocaching.TrackableLog;
+import cgeo.geocaching.enumerations.LogType;
+import cgeo.geocaching.enumerations.StatusCode;
+
+import android.net.Uri;
+
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.List;
+
+public class NoLoggingManager implements ILoggingManager {
+
+ @Override
+ public void init() {
+ // nothing to do
+ }
+
+ @Override
+ public LogResult postLog(Geocache cache, LogType logType, Calendar date, String log, List<TrackableLog> trackableLogs) {
+ return new LogResult(StatusCode.LOG_POST_ERROR, "");
+ }
+
+ @Override
+ public ImageResult postLogImage(String logId, String imageCaption, String imageDescription, Uri imageUri) {
+ return new ImageResult(StatusCode.LOG_POST_ERROR, "");
+ }
+
+ @Override
+ public boolean hasLoaderError() {
+ return true;
+ }
+
+ @Override
+ public List<TrackableLog> getTrackables() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List<LogType> getPossibleLogTypes() {
+ return Collections.emptyList();
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java
index a2df060..82bd52a 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java
@@ -7,6 +7,7 @@ import cgeo.geocaching.SearchResult;
import cgeo.geocaching.Settings;
import cgeo.geocaching.cgData;
import cgeo.geocaching.connector.AbstractConnector;
+import cgeo.geocaching.connector.ILoggingManager;
import cgeo.geocaching.connector.capability.ISearchByCenter;
import cgeo.geocaching.connector.capability.ISearchByGeocode;
import cgeo.geocaching.connector.capability.ISearchByViewPort;
@@ -20,6 +21,8 @@ import cgeo.geocaching.utils.Log;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
+import android.app.Activity;
+
import java.util.regex.Pattern;
public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort {
@@ -82,6 +85,21 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
}
@Override
+ public boolean supportsLogImages() {
+ return true;
+ }
+
+ @Override
+ public ILoggingManager getLoggingManager(Activity activity, Geocache cache) {
+ return new GCLoggingManager(activity, cache);
+ }
+
+ @Override
+ public boolean canLog(Geocache cache) {
+ return StringUtils.isNotBlank(cache.getCacheId());
+ }
+
+ @Override
public String getName() {
return "GeoCaching.com";
}
@@ -153,7 +171,8 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
}
- public static boolean addToWatchlist(Geocache cache) {
+ @Override
+ public boolean addToWatchlist(Geocache cache) {
final boolean added = GCParser.addToWatchlist(cache);
if (added) {
cgData.saveChangedCache(cache);
@@ -161,7 +180,8 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
return added;
}
- public static boolean removeFromWatchlist(Geocache cache) {
+ @Override
+ public boolean removeFromWatchlist(Geocache cache) {
final boolean removed = GCParser.removeFromWatchlist(cache);
if (removed) {
cgData.saveChangedCache(cache);
@@ -244,6 +264,6 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
@Override
public boolean isActivated() {
- return true;
+ return Settings.isGCConnectorActive();
}
}
diff --git a/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java
new file mode 100644
index 0000000..0fbd718
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java
@@ -0,0 +1,132 @@
+package cgeo.geocaching.connector.gc;
+
+import cgeo.geocaching.Geocache;
+import cgeo.geocaching.R;
+import cgeo.geocaching.Settings;
+import cgeo.geocaching.TrackableLog;
+import cgeo.geocaching.VisitCacheActivity;
+import cgeo.geocaching.activity.ActivityMixin;
+import cgeo.geocaching.connector.ILoggingManager;
+import cgeo.geocaching.connector.ImageResult;
+import cgeo.geocaching.connector.LogResult;
+import cgeo.geocaching.enumerations.LogType;
+import cgeo.geocaching.enumerations.StatusCode;
+import cgeo.geocaching.loaders.UrlLoader;
+import cgeo.geocaching.network.Parameters;
+import cgeo.geocaching.utils.Log;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+
+import android.app.Activity;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.Loader;
+
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.List;
+
+public class GCLoggingManager implements ILoggingManager, LoaderManager.LoaderCallbacks<String> {
+
+ private final VisitCacheActivity activity;
+ private final Geocache cache;
+
+ private String[] viewstates;
+ private List<TrackableLog> trackables;
+ private List<LogType> possibleLogTypes;
+ private boolean hasLoaderError = true;
+
+ public GCLoggingManager(Activity activity, Geocache cache) {
+ this.activity = (VisitCacheActivity) activity;
+ this.cache = cache;
+ }
+
+ @Override
+ public Loader<String> onCreateLoader(int arg0, Bundle arg1) {
+ if (!Settings.isLogin()) { // allow offline logging
+ ActivityMixin.showToast(activity, activity.getResources().getString(R.string.err_login));
+ return null;
+ }
+ return new UrlLoader(activity.getBaseContext(), "http://www.geocaching.com/seek/log.aspx", new Parameters("ID", cache.getCacheId()));
+ }
+
+ @Override
+ public void onLoadFinished(Loader<String> arg0, String page) {
+
+ if (page == null) {
+ hasLoaderError = true;
+ } else {
+
+ viewstates = Login.getViewstates(page);
+ trackables = GCParser.parseTrackableLog(page);
+ possibleLogTypes = GCParser.parseTypes(page);
+
+ hasLoaderError = possibleLogTypes.isEmpty();
+ }
+
+ activity.onLoadFinished();
+ }
+
+ @Override
+ public void onLoaderReset(Loader<String> arg0) {
+ // nothing to do
+ }
+
+ @Override
+ public void init() {
+ activity.getSupportLoaderManager().initLoader(0, null, this);
+ }
+
+ @Override
+ public LogResult postLog(Geocache cache, LogType logType, Calendar date, String log, List<TrackableLog> trackableLogs) {
+
+ try {
+ final ImmutablePair<StatusCode, String> postResult = GCParser.postLog(cache.getGeocode(), cache.getCacheId(), viewstates, logType,
+ date.get(Calendar.YEAR), (date.get(Calendar.MONTH) + 1), date.get(Calendar.DATE),
+ log, trackableLogs);
+
+ return new LogResult(postResult.left, postResult.right);
+ } catch (Exception e) {
+ Log.e("GCLoggingManager.postLog", e);
+ }
+
+ return new LogResult(StatusCode.LOG_POST_ERROR, "");
+ }
+
+ @Override
+ public ImageResult postLogImage(String logId, String imageCaption, String imageDescription, Uri imageUri) {
+
+ if (StringUtils.isNotBlank(imageUri.getPath())) {
+
+ ImmutablePair<StatusCode, String> imageResult = GCParser.uploadLogImage(logId, imageCaption, imageDescription, imageUri);
+
+ return new ImageResult(imageResult.left, imageResult.right);
+ }
+
+ return new ImageResult(StatusCode.LOGIMAGE_POST_ERROR, "");
+ }
+
+ @Override
+ public boolean hasLoaderError() {
+ return hasLoaderError;
+ }
+
+ @Override
+ public List<TrackableLog> getTrackables() {
+ if (hasLoaderError) {
+ return Collections.emptyList();
+ }
+ return trackables;
+ }
+
+ @Override
+ public List<LogType> getPossibleLogTypes() {
+ if (hasLoaderError) {
+ return Collections.emptyList();
+ }
+ return possibleLogTypes;
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/connector/oc/AttributeParser.java b/main/src/cgeo/geocaching/connector/oc/AttributeParser.java
new file mode 100644
index 0000000..63bee77
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/oc/AttributeParser.java
@@ -0,0 +1,327 @@
+// This is a generated file, do not change manually!
+
+package cgeo.geocaching.connector.oc;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class AttributeParser {
+
+ private final static Map<String, Integer> attrMapDe;
+ private final static Map<String, Integer> attrMapPl;
+
+ static {
+ attrMapDe = new HashMap<String, Integer>();
+ attrMapPl = new HashMap<String, Integer>();
+
+ // last header line
+ attrMapDe.put("Listed at Opencaching only", 6);
+ attrMapDe.put("Dostępna tylko na Opencaching", 6);
+ attrMapDe.put("Nur bei Opencaching logbar", 6);
+ attrMapDe.put("Solo loggeable en Opencaching", 6);
+ attrMapDe.put("Loggabile solo su Opencaching", 6);
+ attrMapPl.put("Near a Survey Marker", 54);
+ attrMapPl.put("W pobliżu punktu geodezyjnego", 54);
+ attrMapPl.put("Whereigo Cache", 55);
+ attrMapPl.put("Whereigo Cache", 55);
+ attrMapPl.put("Whereigo Cache", 55);
+ attrMapDe.put("Letterbox Cache", 8);
+ attrMapPl.put("Letterbox Cache", 56);
+ attrMapDe.put("Skrzynka typu Letterbox", 8);
+ attrMapPl.put("Skrzynka typu Letterbox", 56);
+ attrMapDe.put("Letterbox (benötigt Stempel)", 8);
+ attrMapPl.put("Letterbox (benötigt Stempel)", 56);
+ attrMapDe.put("Letterbox (necesita un estampador)", 8);
+ attrMapPl.put("Letterbox (necesita un estampador)", 56);
+ attrMapDe.put("Letterbox (richiede un timbro)", 8);
+ attrMapPl.put("Letterbox (richiede un timbro)", 56);
+ attrMapPl.put("GeoHotel", 43);
+ attrMapPl.put("GeoHotel", 43);
+ attrMapPl.put("GeoHotel", 43);
+ attrMapPl.put("Magnetic cache", 49);
+ attrMapPl.put("Przyczepiona magnesem", 49);
+ attrMapPl.put("magnetischer Cache", 49);
+ attrMapPl.put("Description contains an audio file", 50);
+ attrMapPl.put("Opis zawiera plik audio", 50);
+ attrMapPl.put("Offset cache", 51);
+ attrMapPl.put("Offset cache", 51);
+ attrMapPl.put("Peilungscache", 51);
+ attrMapPl.put("Garmin's wireless beacon", 52);
+ attrMapPl.put("Beacon - Garmin Chirp", 52);
+ attrMapPl.put("Funksignal – Garmin Chirp", 52);
+ attrMapPl.put("Dead Drop USB cache", 53);
+ attrMapPl.put("Dead Drop USB skrzynka", 53);
+ attrMapDe.put("Has a moving target", 31);
+ attrMapDe.put("bewegliches Ziel", 31);
+ attrMapDe.put("Objetivo en movimiento", 31);
+ attrMapDe.put("Oggetto in movimento", 31);
+ attrMapDe.put("Webcam Cache", 32);
+ attrMapDe.put("Webcam Cache", 32);
+ attrMapDe.put("Webcam Cache", 32);
+ attrMapDe.put("Webcam Cache", 32);
+ attrMapDe.put("Other cache type", 57);
+ attrMapDe.put("sonstiger Cachetyp", 57);
+ attrMapDe.put("Otro tipo de cache", 57);
+ attrMapDe.put("Altro tipo di cache", 57);
+ attrMapDe.put("Investigation required", 54);
+ attrMapDe.put("Recherche", 54);
+ attrMapDe.put("Investigación", 54);
+ attrMapDe.put("Ricerca", 54);
+ attrMapDe.put("Puzzle / Mystery", 55);
+ attrMapDe.put("Rätsel", 55);
+ attrMapDe.put("Puzzle / Misterio", 55);
+ attrMapDe.put("Puzzle / Mystery", 55);
+ attrMapDe.put("Arithmetical problem", 56);
+ attrMapDe.put("Rechenaufgabe", 56);
+ attrMapDe.put("Problema matemático", 56);
+ attrMapDe.put("Problema matematico", 56);
+ attrMapDe.put("Ask owner for start conditions", 58);
+ attrMapDe.put("Startbedingungen beim Owner erfragen", 58);
+ attrMapDe.put("Ask owner for start conditions", 58);
+ attrMapDe.put("Ask owner for start conditions", 58);
+ attrMapPl.put("Wheelchair accessible", 44);
+ attrMapPl.put("Dostępna dla niepełnosprawnych", 44);
+ attrMapPl.put("rollstuhltauglich", 44);
+ attrMapDe.put("Near the parking area", 24);
+ attrMapDe.put("nahe beim Auto", 24);
+ attrMapDe.put("Cerca de un Parking", 24);
+ attrMapDe.put("Vicino all'area di parcheggio", 24);
+ attrMapPl.put("Access only by walk", 84);
+ attrMapPl.put("Dostępna tylko pieszo", 84);
+ attrMapDe.put("Long walk", 25);
+ attrMapDe.put("längere Wanderung", 25);
+ attrMapDe.put("Larga caminata", 25);
+ attrMapDe.put("Lunga camminata", 25);
+ attrMapDe.put("Swamp, marsh or wading", 26);
+ attrMapDe.put("sumpfig/matschiges Gelände / waten", 26);
+ attrMapDe.put("Pantano / terreno fangoso", 26);
+ attrMapDe.put("Palude o marcita", 26);
+ attrMapDe.put("Hilly area", 27);
+ attrMapDe.put("hügeliges Gelände", 27);
+ attrMapDe.put("Terreno montañoso", 27);
+ attrMapDe.put("Area collinare", 27);
+ attrMapDe.put("Some climbing (no gear needed)", 28);
+ attrMapDe.put("leichtes Klettern (ohne Ausrüstung)", 28);
+ attrMapDe.put("fácil de subir (sin equipo)", 28);
+ attrMapDe.put("Arrampicata (attrezzatura non necessaria)", 28);
+ attrMapDe.put("Swimming required", 29);
+ attrMapDe.put("Schwimmen erforderlich", 29);
+ attrMapDe.put("Requiere nadar", 29);
+ attrMapDe.put("Nuoto necessario", 29);
+ attrMapDe.put("Access or parking fee", 36);
+ attrMapDe.put("Zugangs- bzw. Parkentgelt", 36);
+ attrMapDe.put("Acceso o parking pagando", 36);
+ attrMapDe.put("Tassa di ingresso o di parcheggio", 36);
+ attrMapPl.put("Bikes allowed", 85);
+ attrMapPl.put("Dostępna rowerem", 85);
+ attrMapPl.put("Hidden in natural surroundings (forests, mountains, etc.)", 60);
+ attrMapPl.put("Umiejscowiona na łonie natury (lasy, góry, itp.)", 60);
+ attrMapPl.put("Historic site", 61);
+ attrMapPl.put("Miejsce historyczne", 61);
+ attrMapDe.put("Point of interest", 30);
+ attrMapDe.put("interessanter Ort", 30);
+ attrMapDe.put("Punto de interes", 30);
+ attrMapDe.put("Punto di interesse", 30);
+ attrMapDe.put("Hidden wihin enclosed rooms (caves, buildings etc.)", 33);
+ attrMapDe.put("in geschlossenen Räumen (Höhle, Gebäude, etc.)", 33);
+ attrMapDe.put("en espacios confinados (cuevas, edificios, etc)", 33);
+ attrMapDe.put("All'interno di stanze chiuse (caverne, edifici, ecc.)", 33);
+ attrMapDe.put("Hidden under water", 34);
+ attrMapDe.put("Im Wasser versteckt", 34);
+ attrMapDe.put("En el agua", 34);
+ attrMapDe.put("Nell'acqua", 34);
+ attrMapDe.put("Parking area nearby", 18);
+ attrMapDe.put("Parkplatz in der Nähe", 18);
+ attrMapDe.put("Parking cercano", 18);
+ attrMapDe.put("Parcheggio nei pressi", 18);
+ attrMapDe.put("Public transportation", 19);
+ attrMapDe.put("erreichbar mit ÖVM", 19);
+ attrMapDe.put("Transporte Público", 19);
+ attrMapDe.put("Trasporto pubblico", 19);
+ attrMapDe.put("Drinking water nearby", 20);
+ attrMapDe.put("Trinkwasser in der Nähe", 20);
+ attrMapDe.put("Agua potable en las cercanias", 20);
+ attrMapDe.put("Acqua potabile nei pressi", 20);
+ attrMapDe.put("Public restrooms nearby", 21);
+ attrMapDe.put("öffentliche Toilette in der Nähe", 21);
+ attrMapDe.put("Aseos públicos cercanos", 21);
+ attrMapDe.put("Bagni pubblici nei pressi", 21);
+ attrMapDe.put("Public phone nearby", 22);
+ attrMapDe.put("Telefon in der Nähe", 22);
+ attrMapDe.put("Teléfono Público en las cercanias", 22);
+ attrMapDe.put("Telefono pubblico nei pressi", 22);
+ attrMapDe.put("First aid available", 23);
+ attrMapDe.put("Erste Hilfe verfügbar", 23);
+ attrMapDe.put("Disponible socorro rapido", 23);
+ attrMapDe.put("Disponibile pronto soccorso", 23);
+ attrMapDe.put("Available 24/7", 38);
+ attrMapDe.put("rund um die Uhr machbar", 38);
+ attrMapDe.put("Disponible las 24 horas", 38);
+ attrMapDe.put("Disponibile 24 ore", 38);
+ attrMapDe.put("Not 24/7", 39);
+ attrMapPl.put("Not 24/7", 80);
+ attrMapDe.put("Dostępna w określonych godzinach", 39);
+ attrMapPl.put("Dostępna w określonych godzinach", 80);
+ attrMapDe.put("nur zu bestimmten Uhrzeiten", 39);
+ attrMapPl.put("nur zu bestimmten Uhrzeiten", 80);
+ attrMapDe.put("Sólo disponible a ciertas horas", 39);
+ attrMapPl.put("Sólo disponible a ciertas horas", 80);
+ attrMapDe.put("Disponibile solo in certi orari", 39);
+ attrMapPl.put("Disponibile solo in certi orari", 80);
+ attrMapDe.put("Not recommended at night", 40);
+ attrMapDe.put("nur tagüber", 40);
+ attrMapDe.put("solo por el día", 40);
+ attrMapDe.put("solo di giorno", 40);
+ attrMapPl.put("Recommended at night", 91);
+ attrMapPl.put("Zalecane szukanie nocą", 91);
+ attrMapPl.put("am besten nachts findbar", 91);
+ attrMapDe.put("Only at night", 1);
+ attrMapDe.put("nur bei Nacht", 1);
+ attrMapDe.put("Sólo por la noche", 1);
+ attrMapDe.put("Solo di notte", 1);
+ attrMapDe.put("All seasons", 42);
+ attrMapDe.put("ganzjähig zugänglich", 42);
+ attrMapDe.put("Todas las temporadas", 42);
+ attrMapDe.put("Tutte le stagioni", 42);
+ attrMapDe.put("Only available during specified seasons", 60);
+ attrMapDe.put("Nur zu bestimmten Zeiten im Jahr", 60);
+ attrMapDe.put("Sólo disponible durante las estaciones especificadas", 60);
+ attrMapDe.put("Disponibile solo in certe stagioni", 60);
+ attrMapDe.put("Breeding season / protected nature", 43);
+ attrMapDe.put("Brutsaison / Naturschutz", 43);
+ attrMapDe.put("Temporada de reproducción / protección de la naturaleza", 43);
+ attrMapDe.put("Stagione di riproduzione / natura protetta", 43);
+ attrMapDe.put("Available during winter", 44);
+ attrMapDe.put("schneesicheres Versteck", 44);
+ attrMapDe.put("Nieve en el escondite", 44);
+ attrMapDe.put("Luogo a prova di neve", 44);
+ attrMapDe.put("Not at high water level", 41);
+ attrMapDe.put("nicht bei Hochwasser oder Flut", 41);
+ attrMapDe.put("Compass required", 47);
+ attrMapPl.put("Compass required", 47);
+ attrMapDe.put("Potrzebny kompas", 47);
+ attrMapPl.put("Potrzebny kompas", 47);
+ attrMapDe.put("Kompass", 47);
+ attrMapPl.put("Kompass", 47);
+ attrMapDe.put("Brújula", 47);
+ attrMapPl.put("Brújula", 47);
+ attrMapDe.put("Bussola", 47);
+ attrMapPl.put("Bussola", 47);
+ attrMapPl.put("Take something to write", 48);
+ attrMapPl.put("Weź coś do pisania", 48);
+ attrMapPl.put("You may need a shovel", 81);
+ attrMapPl.put("Potrzebna łopatka", 81);
+ attrMapDe.put("Flashlight required", 48);
+ attrMapPl.put("Flashlight required", 82);
+ attrMapDe.put("Potrzebna latarka", 48);
+ attrMapPl.put("Potrzebna latarka", 82);
+ attrMapDe.put("Taschenlampe", 48);
+ attrMapPl.put("Taschenlampe", 82);
+ attrMapDe.put("Linterna", 48);
+ attrMapPl.put("Linterna", 82);
+ attrMapDe.put("Lampada tascabile", 48);
+ attrMapPl.put("Lampada tascabile", 82);
+ attrMapDe.put("Climbing gear required", 49);
+ attrMapDe.put("Kletterzeug", 49);
+ attrMapDe.put("Equipo de escalada", 49);
+ attrMapDe.put("Attrezzatura per arrampicata", 49);
+ attrMapDe.put("Cave equipment required", 50);
+ attrMapDe.put("Höhlenzeug", 50);
+ attrMapDe.put("Equipación para cuevas", 50);
+ attrMapDe.put("Attrezzatura per grotta", 50);
+ attrMapDe.put("Diving equipment required", 51);
+ attrMapDe.put("Taucherausrüstung", 51);
+ attrMapDe.put("Diving equipment", 51);
+ attrMapDe.put("Equipo de buceo", 51);
+ attrMapDe.put("Special tools required", 46);
+ attrMapPl.put("Special tools required", 83);
+ attrMapDe.put("Wymagany dodatkowy sprzęt", 46);
+ attrMapPl.put("Wymagany dodatkowy sprzęt", 83);
+ attrMapDe.put("spezielle Ausrüstung", 46);
+ attrMapPl.put("spezielle Ausrüstung", 83);
+ attrMapDe.put("Equipamiento especial", 46);
+ attrMapPl.put("Equipamiento especial", 83);
+ attrMapDe.put("Equipaggiamento speciale", 46);
+ attrMapPl.put("Equipaggiamento speciale", 83);
+ attrMapDe.put("Requires a boat", 52);
+ attrMapPl.put("Requires a boat", 86);
+ attrMapDe.put("Wymaga sprzętu pływającego", 52);
+ attrMapPl.put("Wymaga sprzętu pływającego", 86);
+ attrMapDe.put("Wasserfahrzeug", 52);
+ attrMapPl.put("Wasserfahrzeug", 86);
+ attrMapDe.put("Barca", 52);
+ attrMapPl.put("Barca", 86);
+ attrMapDe.put("Barca", 52);
+ attrMapPl.put("Barca", 86);
+ attrMapDe.put("No GPS required", 35);
+ attrMapDe.put("ohne GPS findbar", 35);
+ attrMapDe.put("Sin GPS", 35);
+ attrMapDe.put("Senza GPS", 35);
+ attrMapDe.put("Dangerous area", 9);
+ attrMapPl.put("Dangerous area", 90);
+ attrMapDe.put("Skrzynka niebezpieczna", 9);
+ attrMapPl.put("Skrzynka niebezpieczna", 90);
+ attrMapDe.put("gefährliches Gebiet", 9);
+ attrMapPl.put("gefährliches Gebiet", 90);
+ attrMapDe.put("Zona Peligrosa", 9);
+ attrMapPl.put("Zona Peligrosa", 90);
+ attrMapDe.put("Area pericolosa", 9);
+ attrMapPl.put("Area pericolosa", 90);
+ attrMapDe.put("Active railway nearby", 10);
+ attrMapDe.put("aktive Eisenbahnlinie in der Nähe", 10);
+ attrMapDe.put("Cerca del ferrocarril activo", 10);
+ attrMapDe.put("Ferrovia attiva nei pressi", 10);
+ attrMapDe.put("Cliff / Rocks", 11);
+ attrMapDe.put("Klippen / Felsen", 11);
+ attrMapDe.put("Acantilado / Rocas", 11);
+ attrMapDe.put("Scogliera / Rocce", 11);
+ attrMapDe.put("Hunting", 12);
+ attrMapDe.put("Jagdgebiet", 12);
+ attrMapDe.put("Zona de Caza", 12);
+ attrMapDe.put("Caccia", 12);
+ attrMapDe.put("Thorns", 13);
+ attrMapDe.put("Dornen", 13);
+ attrMapDe.put("Espinas", 13);
+ attrMapDe.put("Spine", 13);
+ attrMapDe.put("Ticks", 14);
+ attrMapDe.put("Zecken", 14);
+ attrMapDe.put("Garrapatas", 14);
+ attrMapDe.put("Zecche", 14);
+ attrMapDe.put("Abandoned mines", 15);
+ attrMapDe.put("Folgen des Bergbaus", 15);
+ attrMapDe.put("Mina abandonada", 15);
+ attrMapDe.put("Miniere abbandonate", 15);
+ attrMapDe.put("Poisonous plants", 16);
+ attrMapDe.put("giftige Pflanzen", 16);
+ attrMapDe.put("Planta venenosa", 16);
+ attrMapDe.put("Piante velenose", 16);
+ attrMapDe.put("Dangerous animals", 17);
+ attrMapDe.put("giftige/gefährliche Tiere", 17);
+ attrMapDe.put("Animales Peligrosos", 17);
+ attrMapDe.put("Animali pericolosi", 17);
+ attrMapPl.put("Quick cache", 40);
+ attrMapPl.put("Szybka skrzynka", 40);
+ attrMapDe.put("Overnight stay necessary", 37);
+ attrMapDe.put("Übernachtung erforderlich", 37);
+ attrMapDe.put("Necesario pernoctar", 37);
+ attrMapDe.put("Necessario pernottamento", 37);
+ attrMapPl.put("Take your children", 41);
+ attrMapPl.put("Można zabrać dzieci", 41);
+ attrMapDe.put("Suited for children (10-12 yo)", 59);
+ attrMapDe.put("kindgerecht (10-12 Jahre)", 59);
+ attrMapDe.put("Apto para niños (10-12 años)", 59);
+ attrMapDe.put("Suited for children (10-12 anni)", 59);
+ // first trailer line
+
+ }
+
+ public static int getOcDeId(final String name) {
+
+ int result = 0;
+
+ if (attrMapDe.containsKey(name)) {
+ result = attrMapDe.get(name);
+ }
+ return result;
+ }
+}
diff --git a/main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java b/main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java
deleted file mode 100644
index d03062f..0000000
--- a/main/src/cgeo/geocaching/connector/oc/OC11XMLParser.java
+++ /dev/null
@@ -1,762 +0,0 @@
-package cgeo.geocaching.connector.oc;
-
-import cgeo.geocaching.Geocache;
-import cgeo.geocaching.Image;
-import cgeo.geocaching.LogEntry;
-import cgeo.geocaching.R;
-import cgeo.geocaching.Settings;
-import cgeo.geocaching.cgeoapplication;
-import cgeo.geocaching.connector.ConnectorFactory;
-import cgeo.geocaching.connector.IConnector;
-import cgeo.geocaching.connector.gc.GCConnector;
-import cgeo.geocaching.enumerations.CacheAttribute;
-import cgeo.geocaching.enumerations.CacheSize;
-import cgeo.geocaching.enumerations.CacheType;
-import cgeo.geocaching.enumerations.LogType;
-import cgeo.geocaching.geopoint.Geopoint;
-import cgeo.geocaching.utils.Log;
-
-import org.apache.commons.lang3.StringUtils;
-import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-
-import android.content.res.Resources;
-import android.sax.Element;
-import android.sax.EndElementListener;
-import android.sax.EndTextElementListener;
-import android.sax.RootElement;
-import android.sax.StartElementListener;
-import android.util.Xml;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.TimeZone;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class OC11XMLParser {
-
- private static final String[] MARKUP = new String[] { "p", "span" };
- private static Pattern STRIP_DATE = Pattern.compile("\\+0([0-9]){1}\\:00");
- private static Pattern LOCAL_URL = Pattern.compile("href=\"(.*)\"");
- private static final int CACHE_PARSE_LIMIT = 250;
- private static final Resources res = cgeoapplication.getInstance().getResources();
- private static final Pattern WHITESPACE = Pattern.compile("<p>(\\s|&nbsp;)*</p>");
-
-
- private static ImageHolder imageHolder = null;
-
- private static class CacheHolder {
- public Geocache cache;
- public String latitude;
- public String longitude;
- }
-
- private static class CacheLog {
- public String id;
- public String cacheId;
- public LogEntry logEntry;
- }
-
- private static class CacheDescription {
- public String cacheId;
- public String shortDesc;
- public String desc;
- public String hint;
- }
-
- private static class ImageHolder {
- public String url;
- public String objectId;
- protected String title;
- protected boolean isSpoiler = false;
- }
-
- private static Date parseFullDate(final String date) {
- final SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
- ISO8601DATEFORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
- final String strippedDate = STRIP_DATE.matcher(date).replaceAll("+0$100");
- try {
- return ISO8601DATEFORMAT.parse(strippedDate);
- } catch (ParseException e) {
- Log.e("OC11XMLParser.parseFullDate", e);
- }
- return null;
- }
-
- private static Date parseDayDate(final String date) {
- final SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
- ISO8601DATEFORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
- final String strippedDate = STRIP_DATE.matcher(date).replaceAll("+0$100");
- try {
- return ISO8601DATEFORMAT.parse(strippedDate);
- } catch (ParseException e) {
- Log.e("OC11XMLParser.parseDayDate", e);
- }
- return null;
- }
-
- private static CacheSize getCacheSize(final String sizeId) {
- try {
- int size = Integer.parseInt(sizeId);
-
- switch (size) {
- case 1:
- return CacheSize.OTHER;
- case 2:
- return CacheSize.MICRO;
- case 3:
- return CacheSize.SMALL;
- case 4:
- return CacheSize.REGULAR;
- case 5:
- case 6:
- return CacheSize.LARGE;
- case 8:
- return CacheSize.VIRTUAL;
- default:
- break;
- }
- } catch (NumberFormatException e) {
- Log.e("OC11XMLParser.getCacheSize", e);
- }
- return CacheSize.NOT_CHOSEN;
- }
-
- private static CacheType getCacheType(final String typeId) {
- try {
- int type = Integer.parseInt(typeId);
- switch (type) {
- case 1: // Other/unbekannter Cachetyp
- return CacheType.UNKNOWN;
- case 2: // Trad./normaler Cache
- return CacheType.TRADITIONAL;
- case 3: // Multi/Multicache
- return CacheType.MULTI;
- case 4: // Virt./virtueller Cache
- return CacheType.VIRTUAL;
- case 5: // ICam./Webcam-Cache
- return CacheType.WEBCAM;
- case 6: // Event/Event-Cache
- return CacheType.EVENT;
- case 7: // Quiz/Rätselcache
- return CacheType.MYSTERY;
- case 8: // Math/Mathe-/Physikcache
- return CacheType.MYSTERY;
- case 9: // Moving/beweglicher Cache
- return CacheType.VIRTUAL;
- case 10: // Driv./Drive-In
- return CacheType.TRADITIONAL;
- default:
- return CacheType.UNKNOWN;
- }
- } catch (NumberFormatException e) {
- Log.e("OC11XMLParser.getCacheType", e);
- }
- return CacheType.UNKNOWN;
- }
-
- private static LogType getLogType(final int typeId) {
- switch (typeId) {
- case 1:
- return LogType.FOUND_IT;
- case 2:
- return LogType.DIDNT_FIND_IT;
- case 3:
- return LogType.NOTE;
- case 7:
- return LogType.ATTENDED;
- case 8:
- return LogType.WILL_ATTEND;
- default:
- return LogType.UNKNOWN;
- }
- }
-
- private static void setCacheStatus(final int statusId, final Geocache cache) {
- switch (statusId) {
- case 1:
- cache.setArchived(false);
- cache.setDisabled(false);
- break;
- case 2:
- cache.setArchived(false);
- cache.setDisabled(true);
- break;
- default:
- cache.setArchived(true);
- cache.setDisabled(false);
- break;
- }
- }
-
- private static void resetCache(final CacheHolder cacheHolder) {
- cacheHolder.cache = new Geocache(null);
- cacheHolder.cache.setReliableLatLon(true);
- cacheHolder.cache.setDescription(StringUtils.EMPTY);
- cacheHolder.latitude = "0.0";
- cacheHolder.longitude = "0.0";
- }
-
- private static void resetLog(final CacheLog log) {
- log.cacheId = StringUtils.EMPTY;
- log.logEntry = new LogEntry("", 0, LogType.UNKNOWN, "");
- }
-
- private static void resetDesc(final CacheDescription desc) {
- desc.cacheId = StringUtils.EMPTY;
- desc.shortDesc = StringUtils.EMPTY;
- desc.desc = StringUtils.EMPTY;
- desc.hint = StringUtils.EMPTY;
- }
-
- private static int attributeId;
-
- public static Collection<Geocache> parseCaches(final InputStream stream) throws IOException {
- // parse and return caches without filtering
- return parseCaches(stream, true);
- }
-
- public static Collection<Geocache> parseCachesFiltered(final InputStream stream) throws IOException {
- // parse caches and filter result
- return parseCaches(stream, false);
- }
-
- private static Collection<Geocache> parseCaches(final InputStream stream, boolean ignoreFiltersIn) throws IOException {
-
- final Map<String, Geocache> caches = new HashMap<String, Geocache>();
- final Map<String, LogEntry> logs = new HashMap<String, LogEntry>();
-
- final CacheHolder cacheHolder = new CacheHolder();
- final CacheLog logHolder = new CacheLog();
- final CacheDescription descHolder = new CacheDescription();
-
- final RootElement root = new RootElement("oc11xml");
- final Element cacheNode = root.getChild("cache");
-
- final boolean ignoreFilters = ignoreFiltersIn;
-
- // cache
- cacheNode.setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attributes) {
- resetCache(cacheHolder);
- }
-
- });
-
- cacheNode.setEndElementListener(new EndElementListener() {
-
- @Override
- public void end() {
- Geocache cache = cacheHolder.cache;
- Geopoint coords = new Geopoint(cacheHolder.latitude, cacheHolder.longitude);
- cache.setCoords(coords);
- if (caches.size() < CACHE_PARSE_LIMIT && isValid(cache) && (ignoreFilters || !isExcluded(cache))) {
- cache.setDetailedUpdatedNow();
- caches.put(cache.getCacheId(), cache);
- }
- }
-
- private boolean isExcluded(Geocache cache) {
- if (cache.isArchived()) {
- return true;
- }
- if (cache.isDisabled() && Settings.isExcludeDisabledCaches()) {
- return true;
- }
- if ((cache.isFound() || cache.isOwner()) && Settings.isExcludeMyCaches()) {
- return true;
- }
- return !Settings.getCacheType().contains(cache);
- }
-
- private boolean isValid(Geocache cache) {
- return StringUtils.isNotBlank(cache.getGeocode()) && !cache.getCoords().equals(Geopoint.ZERO);
- }
- });
-
- // cache.id
- cacheNode.getChild("id").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- cacheHolder.cache.setCacheId(body);
- }
- });
-
- // cache.longitude
- cacheNode.getChild("longitude").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- String longitude = body.trim();
- if (StringUtils.isNotBlank(longitude)) {
- cacheHolder.longitude = longitude;
- }
- }
- });
-
- // cache.latitude
- cacheNode.getChild("latitude").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- String latitude = body.trim();
- if (StringUtils.isNotBlank(latitude)) {
- cacheHolder.latitude = latitude;
- }
- }
- });
-
- // cache.name
- cacheNode.getChild("name").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- final String content = body.trim();
- cacheHolder.cache.setName(content);
- }
- });
-
- // cache.waypoints[oc]
- cacheNode.getChild("waypoints").setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attrs) {
- if (attrs.getIndex("oc") > -1) {
- cacheHolder.cache.setGeocode(attrs.getValue("oc"));
- }
- if (attrs.getIndex("gccom") > -1) {
- String gccode = attrs.getValue("gccom");
- if (!StringUtils.isBlank(gccode)) {
- cacheHolder.cache.setDescription(res.getString(R.string.cache_listed_on, GCConnector.getInstance().getName()) + ": <a href=\"http://coord.info/" + gccode + "\">" + gccode + "</a><br /><br />");
- }
- }
- }
- });
-
- // cache.type[id]
- cacheNode.getChild("type").setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attrs) {
- if (attrs.getIndex("id") > -1) {
- final String typeId = attrs.getValue("id");
- cacheHolder.cache.setType(getCacheType(typeId));
- }
- }
- });
-
- // cache.status[id]
- cacheNode.getChild("status").setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attrs) {
- if (attrs.getIndex("id") > -1) {
- try {
- final int statusId = Integer.parseInt(attrs.getValue("id"));
- setCacheStatus(statusId, cacheHolder.cache);
- } catch (NumberFormatException e) {
- Log.w(String.format("Failed to parse status of cache '%s'.", cacheHolder.cache.getGeocode()));
- }
- }
- }
- });
-
- // cache.size[id]
- cacheNode.getChild("size").setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attrs) {
- if (attrs.getIndex("id") > -1) {
- final String typeId = attrs.getValue("id");
- cacheHolder.cache.setSize(getCacheSize(typeId));
- }
- }
- });
-
- // cache.difficulty
- cacheNode.getChild("difficulty").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- final String content = body.trim();
- try {
- cacheHolder.cache.setDifficulty(Float.valueOf(content));
- } catch (NumberFormatException e) {
- Log.e("OC11XMLParser: unknown difficulty " + content, e);
- }
- }
- });
-
- // cache.terrain
- cacheNode.getChild("terrain").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- final String content = body.trim();
- try {
- cacheHolder.cache.setTerrain(Float.valueOf(content));
- } catch (NumberFormatException e) {
- Log.e("OC11XMLParser: unknown terrain " + content, e);
- }
- }
- });
-
- // cache.datehidden
- cacheNode.getChild("datehidden").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- final String content = body.trim();
- cacheHolder.cache.setHidden(parseFullDate(content));
- }
- });
-
- // cache.userid
- final Element useridNode = cacheNode.getChild("userid");
-
- useridNode.setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attributes) {
- if (attributes.getIndex("id") > -1) {
- cacheHolder.cache.setOwnerUserId(attributes.getValue("id"));
- }
- }
- });
-
- useridNode.setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- cacheHolder.cache.setOwnerDisplayName(body);
- }
- });
-
- // cache.attributes.attribute
- final Element attributeNode = cacheNode.getChild("attributes").getChild("attribute");
-
- attributeNode.setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attributes) {
- if (attributes.getIndex("id") > -1) {
- try {
- attributeId = Integer.parseInt(attributes.getValue("id"));
- } catch (NumberFormatException e) {
- Log.w(String.format("Failed to parse attribute id of cache '%s'.", cacheHolder.cache.getGeocode()));
- }
- }
- }
- });
-
- attributeNode.setEndTextElementListener(new EndTextElementListener() {
- @Override
- public void end(String body) {
- CacheAttribute attribute = CacheAttribute.getByOcId(attributeId);
- if (attribute != null) {
- // semantic of attributes on opencaching is always "yes"
- cacheHolder.cache.getAttributes().add(attribute.getAttributeName(true));
- }
- else {
- if (StringUtils.isNotBlank(body)) {
- cacheHolder.cache.getAttributes().add(body.trim());
- }
- }
- }
- });
-
- // cachedesc
- final Element cacheDesc = root.getChild("cachedesc");
-
- cacheDesc.setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attributes) {
- resetDesc(descHolder);
- }
- });
-
- cacheDesc.setEndElementListener(new EndElementListener() {
-
- @Override
- public void end() {
- final Geocache cache = caches.get(descHolder.cacheId);
- if (cache != null) {
- cache.setShortDescription(descHolder.shortDesc);
- cache.setDescription(cache.getDescription() + descHolder.desc);
- cache.setHint(descHolder.hint);
- }
- }
- });
-
- // cachedesc.cacheid
- cacheDesc.getChild("cacheid").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- descHolder.cacheId = body;
- }
- });
-
- // cachedesc.desc
- cacheDesc.getChild("shortdesc").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- final String content = body.trim();
- descHolder.shortDesc = linkify(stripEmptyText(content));
- }
- });
-
- // cachedesc.desc
- cacheDesc.getChild("desc").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- final String content = body.trim();
- descHolder.desc = linkify(stripEmptyText(content));
- }
- });
-
- // cachedesc.hint
- cacheDesc.getChild("hint").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- descHolder.hint = body.trim();
- }
- });
-
- // cachelog
- final Element cacheLog = root.getChild("cachelog");
-
- cacheLog.setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attrs) {
- resetLog(logHolder);
- }
- });
-
- cacheLog.setEndElementListener(new EndElementListener() {
-
- @Override
- public void end() {
- final Geocache cache = caches.get(logHolder.cacheId);
- if (cache != null && logHolder.logEntry.type != LogType.UNKNOWN) {
- logs.put(logHolder.id, logHolder.logEntry);
- cache.getLogs().add(0, logHolder.logEntry);
- if ((logHolder.logEntry.type == LogType.FOUND_IT || logHolder.logEntry.type == LogType.ATTENDED)
- && StringUtils.equalsIgnoreCase(logHolder.logEntry.author, Settings.getOCConnectorUserName())) {
- cache.setFound(true);
- cache.setVisitedDate(logHolder.logEntry.date);
- }
- }
- }
- });
-
- // cachelog.id
- cacheLog.getChild("id").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- logHolder.id = StringUtils.trim(body);
- }
- });
-
- // cachelog.cacheid
- cacheLog.getChild("cacheid").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- logHolder.cacheId = body;
- }
- });
-
- // cachelog.date
- cacheLog.getChild("date").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- try {
- logHolder.logEntry.date = parseDayDate(body).getTime();
- } catch (NullPointerException e) {
- Log.w("Failed to parse log date", e);
- }
- }
- });
-
- // cachelog.logtype
- cacheLog.getChild("logtype").setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attrs) {
- if (attrs.getIndex("id") > -1) {
- final String id = attrs.getValue("id");
- try {
- final int typeId = Integer.parseInt(id);
- logHolder.logEntry.type = getLogType(typeId);
- } catch (NumberFormatException e) {
- Log.e("OC11XMLParser, unknown logtype " + id, e);
- }
- }
- }
- });
-
- // cachelog.userid
- cacheLog.getChild("userid").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String finderName) {
- logHolder.logEntry.author = finderName;
- }
- });
-
- // cachelog.text
- cacheLog.getChild("text").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String logText) {
- logHolder.logEntry.log = stripEmptyText(logText);
- }
- });
-
- // pictures
- final Element picture = root.getChild("picture");
-
- picture.setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attrs) {
- imageHolder = new ImageHolder();
- }
- });
-
- picture.setEndElementListener(new EndElementListener() {
-
- @Override
- public void end() {
- if (imageHolder.isSpoiler) {
- final Geocache cache = caches.get(imageHolder.objectId);
- if (cache != null) {
- Image spoiler = new Image(imageHolder.url, imageHolder.title);
- cache.addSpoiler(spoiler);
- }
- }
- else {
- final LogEntry log = logs.get(imageHolder.objectId);
- if (log != null) {
- log.addLogImage(new Image(imageHolder.url, imageHolder.title));
- }
- }
- }
- });
-
- // picture.object
- picture.getChild("object").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- imageHolder.objectId = StringUtils.trim(body);
- }
- });
-
- // picture.title
- picture.getChild("title").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- imageHolder.title = StringUtils.trim(body);
- }
- });
-
- // picture.url
- picture.getChild("url").setEndTextElementListener(new EndTextElementListener() {
-
- @Override
- public void end(String body) {
- imageHolder.url = StringUtils.trim(body);
- }
- });
-
- // picture.attributes
- picture.getChild("attributes").setStartElementListener(new StartElementListener() {
-
- @Override
- public void start(Attributes attributes) {
- if (attributes.getIndex("spoiler") > -1) {
- String spoiler = attributes.getValue("spoiler");
- imageHolder.isSpoiler = ("1".equals(spoiler));
- }
- }
- });
-
- try {
- Xml.parse(stream, Xml.Encoding.UTF_8, root.getContentHandler());
- return caches.values();
- } catch (SAXException e) {
- Log.e("Cannot parse .gpx file as oc11xml: could not parse XML", e);
- return null;
- }
- }
-
- /**
- * Converts local links to absolute links targeting the OC website.
- */
- private static String linkify(String input) {
- String result = input;
- Matcher matcher = LOCAL_URL.matcher(result);
- while (matcher.find()) {
- String url = matcher.group(1);
- if (!url.contains(":/")) {
- IConnector ocConnector = ConnectorFactory.getConnector("OCXXX");
- String prefix = "http://" + ocConnector.getHost() + "/";
- result = StringUtils.replace(result, url, prefix + url);
- matcher = LOCAL_URL.matcher(result);
- }
- }
- return result;
- }
-
- /**
- * Removes some unneeded markup and whitespace. Log texts are typically encapsulated in paragraph tags which lead to
- * more empty space on rendering.
- */
- protected static String stripEmptyText(String input) {
- final Matcher matcher = WHITESPACE.matcher(input);
- String result = matcher.replaceAll("").trim();
- if (!StringUtils.startsWith(result, "<")) {
- return result;
- }
- return stripMarkup(result);
- }
-
- private static String stripMarkup(final String input) {
- String result = input;
- for (String tagName : MARKUP) {
- final String startTag = "<" + tagName + ">";
- if (StringUtils.startsWith(result, startTag)) {
- final String endTag = "</" + tagName + ">";
- if (StringUtils.endsWith(result, endTag)) {
- String inner = result.substring(startTag.length(), result.length() - endTag.length()).trim();
- String nested = stripMarkup(inner);
- if (!nested.contains(startTag)) {
- result = nested;
- }
- }
- }
- }
- return result;
- }
-} \ No newline at end of file
diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java
index 69cf8a4..3780c4d 100644
--- a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java
+++ b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java
@@ -23,7 +23,7 @@ public class OCApiConnector extends OCConnector implements ISearchByGeocode {
@Override
public String getLicenseText(final Geocache cache) {
// NOT TO BE TRANSLATED
- return "<a href=\"" + getCacheUrl(cache) + "\">" + getName() + "</a> data licensed under the Creative Commons BY-SA 3.0 License";
+ return "© " + cache.getOwnerDisplayName() + ", <a href=\"" + getCacheUrl(cache) + "\">" + getName() + "</a>, CC-BY-NC-ND, alle Logeinträge © jeweiliger Autor";
}
@Override
@@ -40,4 +40,8 @@ public class OCApiConnector extends OCConnector implements ISearchByGeocode {
// currently always active, but only for details download
return true;
}
+
+ public String getCK() {
+ return CryptUtils.rot13(cK);
+ }
}
diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java
new file mode 100644
index 0000000..4e57831
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java
@@ -0,0 +1,82 @@
+package cgeo.geocaching.connector.oc;
+
+import cgeo.geocaching.Geocache;
+import cgeo.geocaching.SearchResult;
+import cgeo.geocaching.cgData;
+import cgeo.geocaching.cgeoapplication;
+import cgeo.geocaching.connector.ILoggingManager;
+import cgeo.geocaching.connector.capability.ISearchByCenter;
+import cgeo.geocaching.connector.capability.ISearchByViewPort;
+import cgeo.geocaching.geopoint.Geopoint;
+import cgeo.geocaching.geopoint.Viewport;
+import cgeo.geocaching.utils.CryptUtils;
+
+import android.app.Activity;
+
+public class OCApiLiveConnector extends OCApiConnector implements ISearchByCenter, ISearchByViewPort {
+
+ private String cS;
+
+ public OCApiLiveConnector(String name, String host, String prefix, int cKResId, int cSResId) {
+ super(name, host, prefix, CryptUtils.rot13(cgeoapplication.getInstance().getResources().getString(cKResId)));
+
+ cS = CryptUtils.rot13(cgeoapplication.getInstance().getResources().getString(cSResId));
+ }
+
+ @Override
+ public SearchResult searchByViewport(Viewport viewport, String[] tokens) {
+ return new SearchResult(OkapiClient.getCachesBBox(viewport, this));
+ }
+
+ @Override
+ public SearchResult searchByCenter(Geopoint center) {
+
+ return new SearchResult(OkapiClient.getCachesAround(center, this));
+ }
+
+ public String getCS() {
+ return CryptUtils.rot13(cS);
+ }
+
+ @Override
+ public boolean supportsWatchList() {
+ return true;
+ }
+
+ @Override
+ public boolean addToWatchlist(Geocache cache) {
+ final boolean added = OkapiClient.setWatchState(cache, true, this);
+
+ if (added) {
+ cgData.saveChangedCache(cache);
+ }
+
+ return added;
+ }
+
+ @Override
+ public boolean removeFromWatchlist(Geocache cache) {
+ final boolean removed = OkapiClient.setWatchState(cache, false, this);
+
+ if (removed) {
+ cgData.saveChangedCache(cache);
+ }
+
+ return removed;
+ }
+
+ @Override
+ public boolean supportsLogging() {
+ return true;
+ }
+
+ @Override
+ public ILoggingManager getLoggingManager(Activity activity, Geocache cache) {
+ return new OkapiLoggingManager(activity, this, cache);
+ }
+
+ @Override
+ public boolean canLog(Geocache cache) {
+ return true;
+ }
+}
diff --git a/main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java b/main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java
new file mode 100644
index 0000000..779c1c5
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java
@@ -0,0 +1,109 @@
+package cgeo.geocaching.connector.oc;
+
+import cgeo.geocaching.R;
+import cgeo.geocaching.Settings;
+import cgeo.geocaching.cgeoapplication;
+import cgeo.geocaching.network.OAuthAuthorizationActivity;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+
+public class OCAuthorizationActivity extends OAuthAuthorizationActivity {
+
+ private final int siteResId = R.string.auth_ocde;
+
+ public OCAuthorizationActivity() {
+ super("www.opencaching.de",
+ "/okapi/services/oauth/request_token",
+ "/okapi/services/oauth/authorize",
+ "/okapi/services/oauth/access_token",
+ false,
+ cgeoapplication.getInstance().getResources().getString(R.string.oc_de_okapi_consumer_key),
+ cgeoapplication.getInstance().getResources().getString(R.string.oc_de_okapi_consumer_secret));
+ }
+
+ @Override
+ protected ImmutablePair<String, String> getTempToken() {
+ return Settings.getTempOCDEToken();
+ }
+
+ @Override
+ protected void setTempTokens(String tokenPublic, String tokenSecret) {
+ Settings.setOCDETempTokens(tokenPublic, tokenSecret);
+ }
+
+ @Override
+ protected void setTokens(String tokenPublic, String tokenSecret, boolean enable) {
+ Settings.setOCDETokens(tokenPublic, tokenSecret, enable);
+ }
+
+ @Override
+ protected String getAuthTitle() {
+ return res.getString(siteResId);
+ }
+
+ @Override
+ protected String getAuthAgain() {
+ return res.getString(R.string.auth_again_oc);
+ }
+
+ @Override
+ protected String getErrAuthInitialize() {
+ return res.getString(R.string.err_auth_initialize);
+ }
+
+ @Override
+ protected String getAuthStart() {
+ return res.getString(R.string.auth_start_oc);
+ }
+
+ @Override
+ protected String getAuthDialogCompleted() {
+ return res.getString(R.string.auth_dialog_completed_oc, getAuthTitle());
+ }
+
+ @Override
+ protected String getErrAuthProcess() {
+ return res.getString(R.string.err_auth_process);
+ }
+
+ @Override
+ protected String getAuthDialogWait() {
+ return res.getString(R.string.auth_dialog_wait_oc, getAuthTitle());
+ }
+
+ @Override
+ protected String getAuthDialogPinTitle() {
+ return res.getString(R.string.auth_dialog_pin_title_oc);
+ }
+
+ @Override
+ protected String getAuthDialogPinMessage() {
+ return res.getString(R.string.auth_dialog_pin_message_oc, getAuthTitle());
+ }
+
+ @Override
+ protected String getAboutAuth1() {
+ return res.getString(R.string.about_auth_1_oc, getAuthTitle());
+ }
+
+ @Override
+ protected String getAboutAuth2() {
+ return res.getString(R.string.about_auth_2_oc, getAuthTitle(), getAuthTitle());
+ }
+
+ @Override
+ protected String getAuthAuthorize() {
+ return res.getString(R.string.auth_authorize_oc);
+ }
+
+ @Override
+ protected String getAuthPinHint() {
+ return res.getString(R.string.auth_pin_hint_oc, getAuthTitle());
+ }
+
+ @Override
+ protected String getAuthFinish() {
+ return res.getString(R.string.auth_finish_oc);
+ }
+
+}
diff --git a/main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java b/main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java
deleted file mode 100644
index 43fdcfc..0000000
--- a/main/src/cgeo/geocaching/connector/oc/OCXMLApiConnector.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package cgeo.geocaching.connector.oc;
-
-import cgeo.geocaching.Geocache;
-import cgeo.geocaching.ICache;
-import cgeo.geocaching.SearchResult;
-import cgeo.geocaching.Settings;
-import cgeo.geocaching.connector.capability.ISearchByCenter;
-import cgeo.geocaching.connector.capability.ISearchByGeocode;
-import cgeo.geocaching.connector.capability.ISearchByViewPort;
-import cgeo.geocaching.geopoint.Geopoint;
-import cgeo.geocaching.geopoint.Viewport;
-import cgeo.geocaching.ui.Formatter;
-import cgeo.geocaching.utils.CancellableHandler;
-
-import org.apache.commons.lang3.StringUtils;
-
-public class OCXMLApiConnector extends OCConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort {
-
- private final static double SEARCH_DISTANCE_LIMIT = 15.0;
- private final static double NEARBY_SEARCH_DISTANCE = 5.0;
-
- public OCXMLApiConnector(String name, String host, String prefix) {
- super(name, host, prefix);
- }
-
- @Override
- public SearchResult searchByGeocode(final String geocode, final String guid, CancellableHandler handler) {
- final Geocache cache = OCXMLClient.getCache(geocode);
- if (cache == null) {
- return null;
- }
- return new SearchResult(cache);
- }
-
- @Override
- public SearchResult searchByCenter(final Geopoint center) {
- return new SearchResult(OCXMLClient.getCachesAround(center, NEARBY_SEARCH_DISTANCE));
- }
-
- @Override
- public SearchResult searchByViewport(final Viewport viewport, final String[] tokens) {
- final Geopoint center = viewport.getCenter();
- double distance = center.distanceTo(viewport.bottomLeft) * 1.15;
- if (distance > SEARCH_DISTANCE_LIMIT) {
- distance = SEARCH_DISTANCE_LIMIT;
- }
- return new SearchResult(OCXMLClient.getCachesAround(center, distance));
- }
-
- @Override
- public boolean isActivated() {
- // currently only tested and working with oc.de
- return Settings.isOCConnectorActive();
- }
-
- @Override
- public boolean isOwner(ICache cache) {
- return StringUtils.equalsIgnoreCase(cache.getOwnerDisplayName(), Settings.getOCConnectorUserName());
- }
-
- @Override
- public String getLicenseText(Geocache cache) {
- // not to be translated
- return "© " + cache.getOwnerDisplayName() + ", " + "<a href=\"" + getCacheUrl(cache) + "\">www.opencaching.de</a>, CC-BY-NC-ND, Stand: " + Formatter.formatFullDate(cache.getUpdated());
- }
-
-}
diff --git a/main/src/cgeo/geocaching/connector/oc/OCXMLClient.java b/main/src/cgeo/geocaching/connector/oc/OCXMLClient.java
deleted file mode 100644
index df75682..0000000
--- a/main/src/cgeo/geocaching/connector/oc/OCXMLClient.java
+++ /dev/null
@@ -1,122 +0,0 @@
-package cgeo.geocaching.connector.oc;
-
-import cgeo.geocaching.Geocache;
-import cgeo.geocaching.cgData;
-import cgeo.geocaching.connector.ConnectorFactory;
-import cgeo.geocaching.connector.IConnector;
-import cgeo.geocaching.enumerations.LoadFlags;
-import cgeo.geocaching.geopoint.Geopoint;
-import cgeo.geocaching.geopoint.GeopointFormatter;
-import cgeo.geocaching.network.Network;
-import cgeo.geocaching.network.Parameters;
-import cgeo.geocaching.utils.IOUtils;
-import cgeo.geocaching.utils.Log;
-
-import ch.boye.httpclientandroidlib.HttpResponse;
-
-import org.apache.commons.lang3.StringUtils;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Locale;
-import java.util.zip.GZIPInputStream;
-
-public class OCXMLClient {
-
- private static final String SERVICE_CACHE = "/xml/ocxml11.php";
-
- // Url for single cache requests
- // http://www.opencaching.de/xml/ocxml11.php?modifiedsince=20060320000000&user=0&cache=1&cachedesc=1&cachelog=1&picture=1&removedobject=0&session=0&doctype=0&charset=utf-8&wp=OCC9BE
-
- public static Geocache getCache(final String geoCode) {
- try {
- final Parameters params = getOCXmlQueryParameters(true, true, true);
- params.put("wp", geoCode);
- final InputStream data = request(ConnectorFactory.getConnector(geoCode), SERVICE_CACHE, params);
-
- if (data == null) {
- return null;
- }
-
- final BufferedInputStream stream = new BufferedInputStream(new GZIPInputStream(data));
- Collection<Geocache> caches = OC11XMLParser.parseCaches(stream);
- if (caches.iterator().hasNext()) {
- Geocache cache = caches.iterator().next();
- cgData.saveCache(cache, LoadFlags.SAVE_ALL);
- IOUtils.closeQuietly(stream);
- return cache;
- }
- return null;
- } catch (IOException e) {
- Log.e("Error parsing cache '" + geoCode + "'", e);
- return null;
- }
- }
-
- public static Collection<Geocache> getCachesAround(final Geopoint center, final double distance) {
- try {
- final Parameters params = getOCXmlQueryParameters(false, false, false);
- params.put("lat", GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, center));
- params.put("lon", GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, center));
- params.put("distance", String.format(Locale.US, "%f", distance));
- final InputStream data = request(ConnectorFactory.getConnector("OCXXX"), SERVICE_CACHE, params);
-
- if (data == null) {
- return Collections.emptyList();
- }
-
- final BufferedInputStream stream = new BufferedInputStream(new GZIPInputStream(data));
- final Collection<Geocache> result = OC11XMLParser.parseCachesFiltered(stream);
- IOUtils.closeQuietly(stream);
- return result;
- } catch (IOException e) {
- Log.e("Error parsing nearby search result", e);
- return Collections.emptyList();
- }
- }
-
- private static InputStream request(final IConnector connector, final String service, final Parameters params) {
- if (connector == null) {
- return null;
- }
- if (!(connector instanceof OCXMLApiConnector)) {
- return null;
- }
-
- final String host = connector.getHost();
- if (StringUtils.isBlank(host)) {
- return null;
- }
-
- final String uri = "http://" + host + service;
- HttpResponse resp = Network.getRequest(uri, params);
- if (resp != null) {
- try {
- return resp.getEntity().getContent();
- } catch (IllegalStateException e) {
- // fall through and return null
- } catch (IOException e) {
- // fall through and return null
- }
- }
- return null;
- }
-
- private static Parameters getOCXmlQueryParameters(final boolean withDescription, final boolean withLogs, final boolean withImages) {
- return new Parameters("modifiedsince", "20000101000000",
- "user", "0",
- "cache", "1",
- "cachedesc", withDescription ? "1" : "0",
- "cachelog", withLogs ? "1" : "0",
- "picture", withImages ? "1" : "0",
- "removedobject", "0",
- "session", "0",
- "doctype", "0",
- "charset", "utf-8",
- "zip", "gzip",
- "picturefromcachelog", withImages ? "1" : "0");
- }
-}
diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
index 0673605..d8d92e1 100644
--- a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
+++ b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
@@ -3,16 +3,27 @@ package cgeo.geocaching.connector.oc;
import cgeo.geocaching.Geocache;
import cgeo.geocaching.Image;
import cgeo.geocaching.LogEntry;
+import cgeo.geocaching.R;
+import cgeo.geocaching.Settings;
+import cgeo.geocaching.Waypoint;
import cgeo.geocaching.cgData;
+import cgeo.geocaching.cgeoapplication;
import cgeo.geocaching.connector.ConnectorFactory;
import cgeo.geocaching.connector.IConnector;
+import cgeo.geocaching.connector.LogResult;
+import cgeo.geocaching.connector.gc.GCConnector;
+import cgeo.geocaching.enumerations.CacheAttribute;
import cgeo.geocaching.enumerations.CacheSize;
import cgeo.geocaching.enumerations.CacheType;
import cgeo.geocaching.enumerations.LoadFlags.SaveFlag;
import cgeo.geocaching.enumerations.LogType;
+import cgeo.geocaching.enumerations.StatusCode;
+import cgeo.geocaching.enumerations.WaypointType;
import cgeo.geocaching.geopoint.Geopoint;
import cgeo.geocaching.geopoint.GeopointFormatter;
+import cgeo.geocaching.geopoint.Viewport;
import cgeo.geocaching.network.Network;
+import cgeo.geocaching.network.OAuth;
import cgeo.geocaching.network.Parameters;
import cgeo.geocaching.utils.Log;
@@ -22,17 +33,40 @@ import org.json.JSONException;
import org.json.JSONObject;
import android.net.Uri;
-import android.text.Html;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
final public class OkapiClient {
+
+ private static final SimpleDateFormat logDateFormat;
+
+ static {
+ logDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ", Locale.US);
+ logDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ private static final String CACHE_ATTRNAMES = "attrnames";
+ private static final String WPT_LOCATION = "location";
+ private static final String WPT_DESCRIPTION = "description";
+ private static final String WPT_TYPE = "type";
+ private static final String WPT_NAME = "name";
+ private static final String CACHE_IS_WATCHED = "is_watched";
+ private static final String CACHE_WPTS = "alt_wpts";
+ private static final String CACHE_STATUS_ARCHIVED = "Archived";
+ private static final String CACHE_STATUS_DISABLED = "Temporarily unavailable";
+ private static final String CACHE_IS_FOUND = "is_found";
private static final String CACHE_SIZE = "size";
private static final String CACHE_VOTES = "rating_votes";
private static final String CACHE_NOTFOUNDS = "notfounds";
@@ -64,12 +98,22 @@ final public class OkapiClient {
private static final String USER_USERNAME = "username";
private static final String SERVICE_CACHE = "/okapi/services/caches/geocache";
- private static final String SERVICE_CACHE_FIELDS = "code|name|location|type|status|owner|founds|notfounds|size|difficulty|terrain|rating|rating_votes|recommendations|description|hint|images|latest_logs|date_hidden";
- private static final String SERVICE_NEAREST = "/okapi/services/caches/search/nearest";
+ private static final String SERVICE_CACHE_CORE_FIELDS = "code|name|location|type|status|difficulty|terrain|size|is_found";
+ private static final String SERVICE_CACHE_FIELDS = SERVICE_CACHE_CORE_FIELDS + "|owner|founds|notfounds|rating|rating_votes|recommendations|description|hint|images|latest_logs|date_hidden|attribution_note|alt_wpts|is_watched|attrnames|gc_code";
+
+ private static final String SERVICE_SEARCH_AND_RETRIEVE = "/okapi/services/caches/shortcuts/search_and_retrieve";
+
+ private static final String METHOD_SEARCH_NEAREST = "services/caches/search/nearest";
+ private static final String METHOD_SEARCH_BBOX = "services/caches/search/bbox";
+ private static final String METHOD_RETRIEVE_CACHES = "services/caches/geocaches";
+
+ private static final String SERVICE_MARK_CACHE = "/okapi/services/caches/mark";
+
+ private static final String SERVICE_SUBMIT_LOG = "/okapi/services/logs/submit";
public static Geocache getCache(final String geoCode) {
- final Parameters params = new Parameters("cache_code", geoCode, "fields", SERVICE_CACHE_FIELDS);
+ final Parameters params = new Parameters("cache_code", geoCode, "fields", SERVICE_CACHE_FIELDS, "attribution_append", "none");
final JSONObject data = request(ConnectorFactory.getConnector(geoCode), SERVICE_CACHE, params);
if (data == null) {
@@ -81,30 +125,115 @@ final public class OkapiClient {
public static List<Geocache> getCachesAround(final Geopoint center, IConnector connector) {
String centerString = GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, center) + "|" + GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, center);
- final Parameters params = new Parameters("center", centerString);
- final JSONObject data = request(connector, SERVICE_NEAREST, params);
+ final Parameters params = new Parameters("search_method", METHOD_SEARCH_NEAREST);
+ final Map<String, String> valueMap = new LinkedHashMap<String, String>();
+ valueMap.put("center", centerString);
+ valueMap.put("limit", "20");
+
+ addFilterParams(valueMap);
+
+ params.add("search_params", new JSONObject(valueMap).toString());
+
+ addRetrieveParams(params);
+
+ final JSONObject data = request(connector, SERVICE_SEARCH_AND_RETRIEVE, params);
if (data == null) {
- return null;
+ return Collections.emptyList();
+ }
+
+ return parseCaches(data);
+ }
+
+ public static List<Geocache> getCachesBBox(final Viewport viewport, IConnector connector) {
+
+ if (viewport.getLatitudeSpan() == 0 || viewport.getLongitudeSpan() == 0) {
+ return Collections.emptyList();
+ }
+
+ String bboxString = GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, viewport.bottomLeft)
+ + "|" + GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, viewport.bottomLeft)
+ + "|" + GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, viewport.topRight)
+ + "|" + GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, viewport.topRight);
+ final Parameters params = new Parameters("search_method", METHOD_SEARCH_BBOX);
+ final Map<String, String> valueMap = new LinkedHashMap<String, String>();
+ valueMap.put("bbox", bboxString);
+
+ addFilterParams(valueMap);
+
+ params.add("search_params", new JSONObject(valueMap).toString());
+
+ addRetrieveParams(params);
+
+ final JSONObject data = request(connector, SERVICE_SEARCH_AND_RETRIEVE, params);
+
+ if (data == null) {
+ return Collections.emptyList();
}
return parseCaches(data);
}
+ public static boolean setWatchState(final Geocache cache, final boolean watched, IConnector connector) {
+ final Parameters params = new Parameters("cache_code", cache.getGeocode());
+ params.add("watched", watched ? "true" : "false");
+
+ final JSONObject data = request(connector, SERVICE_MARK_CACHE, params);
+
+ if (data == null) {
+ return false;
+ }
+
+ cache.setOnWatchlist(watched);
+
+ return true;
+ }
+
+ public static LogResult postLog(final Geocache cache, LogType logType, Calendar date, String log, IConnector connector) {
+ final Parameters params = new Parameters("cache_code", cache.getGeocode());
+ params.add("logtype", logType.oc_type);
+ params.add("comment", log);
+ params.add("comment_format", "plaintext");
+ params.add("when", logDateFormat.format(date.getTime()));
+ if (logType.equals(LogType.NEEDS_MAINTENANCE)) {
+ params.add("needs_maintenance", "true");
+ }
+
+ final JSONObject data = request(connector, SERVICE_SUBMIT_LOG, params);
+
+ if (data == null) {
+ return new LogResult(StatusCode.LOG_POST_ERROR, "");
+ }
+
+ try {
+ if (data.getBoolean("success")) {
+ return new LogResult(StatusCode.NO_ERROR, data.getString("log_uuid"));
+ }
+
+ return new LogResult(StatusCode.LOG_POST_ERROR, "");
+ } catch (JSONException e) {
+ Log.e("OkapiClient.postLog", e);
+ }
+ return new LogResult(StatusCode.LOG_POST_ERROR, "");
+ }
+
private static List<Geocache> parseCaches(final JSONObject response) {
try {
- final JSONArray cachesResponse = response.getJSONArray("results");
+ // Check for empty result
+ final String result = response.getString("results");
+ if (StringUtils.isBlank(result) || StringUtils.equals(result, "[]")) {
+ return Collections.emptyList();
+ }
+
+ // Get and iterate result list
+ final JSONObject cachesResponse = response.getJSONObject("results");
if (cachesResponse != null) {
- ArrayList<String> geocodes = new ArrayList<String>(cachesResponse.length());
- for (int i = 0; i < cachesResponse.length(); i++) {
- String geocode = cachesResponse.getString(i);
- if (StringUtils.isNotBlank(geocode)) {
- geocodes.add(geocode);
- }
- }
- List<Geocache> caches = new ArrayList<Geocache>(geocodes.size());
- for (String geocode : geocodes) {
- Geocache cache = getCache(geocode);
+ List<Geocache> caches = new ArrayList<Geocache>(cachesResponse.length());
+ @SuppressWarnings("unchecked")
+ Iterator<String> keys = cachesResponse.keys();
+ while (keys.hasNext()) {
+ String key = keys.next();
+ Geocache cache = parseSmallCache(cachesResponse.getJSONObject(key));
if (cache != null) {
caches.add(cache);
}
@@ -112,24 +241,31 @@ final public class OkapiClient {
return caches;
}
} catch (JSONException e) {
- Log.e("OkapiClient.parseCaches", e);
+ Log.e("OkapiClient.parseCachesResult", e);
}
- return null;
+ return Collections.emptyList();
+ }
+
+ private static Geocache parseSmallCache(final JSONObject response) {
+ final Geocache cache = new Geocache();
+ cache.setReliableLatLon(true);
+ try {
+
+ parseCoreCache(response, cache);
+
+ cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_CACHE));
+ } catch (JSONException e) {
+ Log.e("OkapiClient.parseSmallCache", e);
+ }
+ return cache;
}
private static Geocache parseCache(final JSONObject response) {
final Geocache cache = new Geocache();
cache.setReliableLatLon(true);
try {
- cache.setGeocode(response.getString(CACHE_CODE));
- cache.setName(response.getString(CACHE_NAME));
- // not used: names
- setLocation(cache, response.getString(CACHE_LOCATION));
- cache.setType(getCacheType(response.getString(CACHE_TYPE)));
- final String status = response.getString(CACHE_STATUS);
- cache.setDisabled(status.equalsIgnoreCase("Temporarily unavailable"));
- cache.setArchived(status.equalsIgnoreCase("Archived"));
+ parseCoreCache(response, cache);
// not used: url
final JSONObject owner = response.getJSONObject(CACHE_OWNER);
@@ -137,9 +273,7 @@ final public class OkapiClient {
cache.getLogCounts().put(LogType.FOUND_IT, response.getInt(CACHE_FOUNDS));
cache.getLogCounts().put(LogType.DIDNT_FIND_IT, response.getInt(CACHE_NOTFOUNDS));
- cache.setSize(getCacheSize(response));
- cache.setDifficulty((float) response.getDouble(CACHE_DIFFICULTY));
- cache.setTerrain((float) response.getDouble(CACHE_TERRAIN));
+
if (!response.isNull(CACHE_RATING)) {
cache.setRating((float) response.getDouble(CACHE_RATING));
}
@@ -147,8 +281,22 @@ final public class OkapiClient {
cache.setFavoritePoints(response.getInt(CACHE_RECOMMENDATIONS));
// not used: req_password
- cache.setDescription(response.getString(CACHE_DESCRIPTION));
- cache.setHint(Html.fromHtml(response.getString(CACHE_HINT)).toString());
+ // Prepend gc-link to description if available
+ StringBuilder description = new StringBuilder(500);
+ if (!response.isNull("gc_code")) {
+ String gccode = response.getString("gc_code");
+ description.append(cgeoapplication.getInstance().getResources()
+ .getString(R.string.cache_listed_on, GCConnector.getInstance().getName()))
+ .append(": <a href=\"http://coord.info/")
+ .append(gccode)
+ .append("\">")
+ .append(gccode)
+ .append("</a><br /><br />");
+ }
+ description.append(response.getString(CACHE_DESCRIPTION));
+ cache.setDescription(description.toString());
+
+ cache.setHint(response.getString(CACHE_HINT));
// not used: hints
final JSONArray images = response.getJSONArray(CACHE_IMAGES);
@@ -163,9 +311,13 @@ final public class OkapiClient {
}
}
- // not used: attrnames
+ cache.setAttributes(parseAttributes(response.getJSONArray(CACHE_ATTRNAMES)));
cache.setLogs(parseLogs(response.getJSONArray(CACHE_LATEST_LOGS)));
cache.setHidden(parseDate(response.getString(CACHE_HIDDEN)));
+ //TODO: Store license per cache
+ //cache.setLicense(response.getString("attribution_note"));
+ cache.setWaypoints(parseWaypoints(response.getJSONArray(CACHE_WPTS)), false);
+ cache.setOnWatchlist(response.getBoolean(CACHE_IS_WATCHED));
cache.setDetailedUpdatedNow();
// save full detailed caches
@@ -176,6 +328,24 @@ final public class OkapiClient {
return cache;
}
+ private static void parseCoreCache(final JSONObject response, final Geocache cache) throws JSONException {
+ cache.setGeocode(response.getString(CACHE_CODE));
+ cache.setName(response.getString(CACHE_NAME));
+ // not used: names
+ setLocation(cache, response.getString(CACHE_LOCATION));
+ cache.setType(getCacheType(response.getString(CACHE_TYPE)));
+
+ final String status = response.getString(CACHE_STATUS);
+ cache.setDisabled(status.equalsIgnoreCase(CACHE_STATUS_DISABLED));
+ cache.setArchived(status.equalsIgnoreCase(CACHE_STATUS_ARCHIVED));
+
+ cache.setSize(getCacheSize(response));
+ cache.setDifficulty((float) response.getDouble(CACHE_DIFFICULTY));
+ cache.setTerrain((float) response.getDouble(CACHE_TERRAIN));
+
+ cache.setFound(response.getBoolean(CACHE_IS_FOUND));
+ }
+
private static String absoluteUrl(String url, String geocode) {
final Uri uri = Uri.parse(url);
@@ -214,6 +384,30 @@ final public class OkapiClient {
return result;
}
+ private static List<Waypoint> parseWaypoints(JSONArray wptsJson) {
+ List<Waypoint> result = null;
+ for (int i = 0; i < wptsJson.length(); i++) {
+ try {
+ JSONObject wptResponse = wptsJson.getJSONObject(i);
+ Waypoint wpt = new Waypoint(wptResponse.getString(WPT_NAME),
+ parseWptType(wptResponse.getString(WPT_TYPE)),
+ false);
+ wpt.setNote(wptResponse.getString(WPT_DESCRIPTION));
+ Geopoint pt = parseCoords(wptResponse.getString(WPT_LOCATION));
+ if (pt != null) {
+ wpt.setCoords(pt);
+ }
+ if (result == null) {
+ result = new ArrayList<Waypoint>();
+ }
+ result.add(wpt);
+ } catch (JSONException e) {
+ Log.e("OkapiClient.parseWaypoints", e);
+ }
+ }
+ return result;
+ }
+
private static LogType parseLogType(String logType) {
if ("Found it".equalsIgnoreCase(logType)) {
return LogType.FOUND_IT;
@@ -224,6 +418,31 @@ final public class OkapiClient {
return LogType.NOTE;
}
+ private static WaypointType parseWptType(String wptType) {
+ if ("parking".equalsIgnoreCase(wptType)) {
+ return WaypointType.PARKING;
+ }
+ if ("path".equalsIgnoreCase(wptType)) {
+ return WaypointType.TRAILHEAD;
+ }
+ if ("stage".equalsIgnoreCase(wptType)) {
+ return WaypointType.STAGE;
+ }
+ if ("physical-stage".equalsIgnoreCase(wptType)) {
+ return WaypointType.STAGE;
+ }
+ if ("virtual-stage".equalsIgnoreCase(wptType)) {
+ return WaypointType.PUZZLE;
+ }
+ if ("final".equalsIgnoreCase(wptType)) {
+ return WaypointType.FINAL;
+ }
+ if ("poi".equalsIgnoreCase(wptType)) {
+ return WaypointType.TRAILHEAD;
+ }
+ return WaypointType.WAYPOINT;
+ }
+
private static Date parseDate(final String date) {
final SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault());
final String strippedDate = date.replaceAll("\\+0([0-9]){1}\\:00", "+0$100");
@@ -235,6 +454,36 @@ final public class OkapiClient {
return null;
}
+ private static Geopoint parseCoords(final String location) {
+ final String latitude = StringUtils.substringBefore(location, "|");
+ final String longitude = StringUtils.substringAfter(location, "|");
+ if (StringUtils.isNotBlank(latitude) && StringUtils.isNotBlank(longitude)) {
+ return new Geopoint(latitude, longitude);
+ }
+
+ return null;
+ }
+
+ private static List<String> parseAttributes(JSONArray nameList) {
+
+ List<String> result = new ArrayList<String>();
+
+ for (int i = 0; i < nameList.length(); i++) {
+ try {
+ String name = nameList.getString(i);
+ CacheAttribute attr = CacheAttribute.getByOcId(AttributeParser.getOcDeId(name));
+
+ if (attr != null) {
+ result.add(attr.rawName);
+ }
+ } catch (JSONException e) {
+ Log.e("OkapiClient.parseAttributes", e);
+ }
+ }
+
+ return result;
+ }
+
private static void setLocation(final Geocache cache, final String location) {
final String latitude = StringUtils.substringBefore(location, "|");
final String longitude = StringUtils.substringAfter(location, "|");
@@ -281,6 +530,18 @@ final public class OkapiClient {
if (cacheType.equalsIgnoreCase("Virtual")) {
return CacheType.VIRTUAL;
}
+ if (cacheType.equalsIgnoreCase("Event")) {
+ return CacheType.EVENT;
+ }
+ if (cacheType.equalsIgnoreCase("Webcam")) {
+ return CacheType.WEBCAM;
+ }
+ if (cacheType.equalsIgnoreCase("Math/Physics")) {
+ return CacheType.MYSTERY;
+ }
+ if (cacheType.equalsIgnoreCase("Drive-In")) {
+ return CacheType.TRADITIONAL;
+ }
return CacheType.UNKNOWN;
}
@@ -297,9 +558,10 @@ final public class OkapiClient {
return null;
}
- ((OCApiConnector) connector).addAuthentication(params);
params.add("langpref", getPreferredLanguage());
+ OAuth.signOAuth(host, service, "GET", false, params, Settings.getOCDETokenPublic(), Settings.getOCDETokenSecret(), ((OCApiLiveConnector) connector).getCK(), ((OCApiLiveConnector) connector).getCS());
+
final String uri = "http://" + host + service;
return Network.requestJSON(uri, params);
}
@@ -311,4 +573,43 @@ final public class OkapiClient {
}
return "en";
}
+
+ private static void addFilterParams(final Map<String, String> valueMap) {
+ if (!Settings.isExcludeDisabledCaches()) {
+ valueMap.put("status", "Available|Temporarily unavailable");
+ }
+ if (Settings.isExcludeMyCaches()) {
+ valueMap.put("exclude_my_own", "true");
+ valueMap.put("found_status", "notfound_only");
+ }
+ if (Settings.getCacheType() != CacheType.ALL) {
+ valueMap.put("type", getFilterFromType(Settings.getCacheType()));
+ }
+ }
+
+ private static void addRetrieveParams(final Parameters params) {
+ params.add("retr_method", METHOD_RETRIEVE_CACHES);
+ params.add("retr_params", "{\"fields\": \"" + SERVICE_CACHE_CORE_FIELDS + "\"}");
+ params.add("wrap", "true");
+ }
+
+ private static String getFilterFromType(CacheType cacheType) {
+ switch (cacheType) {
+ case EVENT:
+ return "Event";
+ case MULTI:
+ return "Multi";
+ case MYSTERY:
+ return "Quiz";
+ case TRADITIONAL:
+ return "Traditional";
+ case VIRTUAL:
+ return "Virtual";
+ case WEBCAM:
+ return "Webcam";
+ default:
+ return "";
+ }
+ }
+
}
diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java b/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java
new file mode 100644
index 0000000..24c6b79
--- /dev/null
+++ b/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java
@@ -0,0 +1,69 @@
+package cgeo.geocaching.connector.oc;
+
+import cgeo.geocaching.Geocache;
+import cgeo.geocaching.TrackableLog;
+import cgeo.geocaching.VisitCacheActivity;
+import cgeo.geocaching.connector.IConnector;
+import cgeo.geocaching.connector.ILoggingManager;
+import cgeo.geocaching.connector.ImageResult;
+import cgeo.geocaching.connector.LogResult;
+import cgeo.geocaching.enumerations.LogType;
+import cgeo.geocaching.enumerations.StatusCode;
+
+import android.app.Activity;
+import android.net.Uri;
+
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.List;
+
+public class OkapiLoggingManager implements ILoggingManager {
+
+ private final IConnector connector;
+ private final Geocache cache;
+ private VisitCacheActivity activity;
+
+ private final static List<LogType> standardLogTypes = Arrays.asList(LogType.FOUND_IT, LogType.DIDNT_FIND_IT, LogType.NOTE, LogType.NEEDS_MAINTENANCE);
+ private final static List<LogType> eventLogTypes = Arrays.asList(LogType.WILL_ATTEND, LogType.ATTENDED, LogType.NOTE);
+
+ public OkapiLoggingManager(Activity activity, IConnector connector, Geocache cache) {
+ this.connector = connector;
+ this.cache = cache;
+ this.activity = (VisitCacheActivity) activity;
+ }
+
+ @Override
+ public void init() {
+ activity.onLoadFinished();
+ }
+
+ @Override
+ public LogResult postLog(Geocache cache, LogType logType, Calendar date, String log, List<TrackableLog> trackableLogs) {
+ return OkapiClient.postLog(cache, logType, date, log, connector);
+ }
+
+ @Override
+ public ImageResult postLogImage(String logId, String imageCaption, String imageDescription, Uri imageUri) {
+ return new ImageResult(StatusCode.LOG_POST_ERROR, "");
+ }
+
+ @Override
+ public boolean hasLoaderError() {
+ return false;
+ }
+
+ @Override
+ public List<TrackableLog> getTrackables() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List<LogType> getPossibleLogTypes() {
+ if (cache.isEventCache()) {
+ return eventLogTypes;
+ }
+
+ return standardLogTypes;
+ }
+}
diff --git a/main/src/cgeo/geocaching/enumerations/LogType.java b/main/src/cgeo/geocaching/enumerations/LogType.java
index 71a5146..9902d3f 100644
--- a/main/src/cgeo/geocaching/enumerations/LogType.java
+++ b/main/src/cgeo/geocaching/enumerations/LogType.java
@@ -15,50 +15,52 @@ import java.util.Map;
*/
public enum LogType {
- FOUND_IT(2, "2", "found it", R.string.log_found, R.drawable.mark_green),
- DIDNT_FIND_IT(3, "3", "didn't find it", R.string.log_dnf, R.drawable.mark_red),
- NOTE(4, "4", "write note", R.string.log_note),
- PUBLISH_LISTING(1003, "24", "publish listing", R.string.log_published, R.drawable.mark_green_more),
- ENABLE_LISTING(23, "23", "enable listing", R.string.log_enabled, R.drawable.mark_green_more),
- ARCHIVE(5, "5", "archive", R.string.log_archived, R.drawable.mark_red_more),
- UNARCHIVE(12, "12", "unarchive", R.string.log_unarchived, R.drawable.mark_green_more),
- TEMP_DISABLE_LISTING(22, "22", "temporarily disable listing", R.string.log_disabled, R.drawable.mark_red_more),
- NEEDS_ARCHIVE(7, "7", "needs archived", R.string.log_needs_archived, R.drawable.mark_red),
- WILL_ATTEND(9, "9", "will attend", R.string.log_attend),
- ATTENDED(10, "10", "attended", R.string.log_attended, R.drawable.mark_green),
- RETRIEVED_IT(13, "13", "retrieved it", R.string.log_retrieved, R.drawable.mark_green_more),
- PLACED_IT(14, "14", "placed it", R.string.log_placed, R.drawable.mark_green_more),
- GRABBED_IT(19, "19", "grabbed it", R.string.log_grabbed, R.drawable.mark_green_more),
- NEEDS_MAINTENANCE(45, "45", "needs maintenance", R.string.log_maintenance_needed, R.drawable.mark_red),
- OWNER_MAINTENANCE(46, "46", "owner maintenance", R.string.log_maintained, R.drawable.mark_green_more),
- UPDATE_COORDINATES(47, "47", "update coordinates", R.string.log_update),
- DISCOVERED_IT(48, "48", "discovered it", R.string.log_discovered, R.drawable.mark_green),
- POST_REVIEWER_NOTE(18, "68", "post reviewer note", R.string.log_reviewer),
- VISIT(1001, "75", "visit", R.string.log_tb_visit, R.drawable.mark_green),
- WEBCAM_PHOTO_TAKEN(11, "11", "webcam photo taken", R.string.log_webcam, R.drawable.mark_green),
- ANNOUNCEMENT(74, "74", "announcement", R.string.log_announcement),
- MOVE_COLLECTION(69, "69", "unused_collection", R.string.log_movecollection),
- MOVE_INVENTORY(70, "70", "unused_inventory", R.string.log_moveinventory),
- RETRACT(25, "25", "retract listing", R.string.log_retractlisting),
- MARKED_MISSING(16, "16", "marked missing", R.string.log_marked_missing, R.drawable.mark_red),
- UNKNOWN(0, "unknown", "", R.string.err_unknown, R.drawable.mark_red); // LogType not init. yet
+ FOUND_IT(2, "2", "found it", "Found it", R.string.log_found, R.drawable.mark_green),
+ DIDNT_FIND_IT(3, "3", "didn't find it", "Didn't find it", R.string.log_dnf, R.drawable.mark_red),
+ NOTE(4, "4", "write note", "Comment", R.string.log_note),
+ PUBLISH_LISTING(1003, "24", "publish listing", "", R.string.log_published, R.drawable.mark_green_more),
+ ENABLE_LISTING(23, "23", "enable listing", "", R.string.log_enabled, R.drawable.mark_green_more),
+ ARCHIVE(5, "5", "archive", "", R.string.log_archived, R.drawable.mark_red_more),
+ UNARCHIVE(12, "12", "unarchive", "", R.string.log_unarchived, R.drawable.mark_green_more),
+ TEMP_DISABLE_LISTING(22, "22", "temporarily disable listing", "", R.string.log_disabled, R.drawable.mark_red_more),
+ NEEDS_ARCHIVE(7, "7", "needs archived", "", R.string.log_needs_archived, R.drawable.mark_red),
+ WILL_ATTEND(9, "9", "will attend", "Will attend", R.string.log_attend),
+ ATTENDED(10, "10", "attended", "Attended", R.string.log_attended, R.drawable.mark_green),
+ RETRIEVED_IT(13, "13", "retrieved it", "", R.string.log_retrieved, R.drawable.mark_green_more),
+ PLACED_IT(14, "14", "placed it", "", R.string.log_placed, R.drawable.mark_green_more),
+ GRABBED_IT(19, "19", "grabbed it", "", R.string.log_grabbed, R.drawable.mark_green_more),
+ NEEDS_MAINTENANCE(45, "45", "needs maintenance", "Comment", R.string.log_maintenance_needed, R.drawable.mark_red),
+ OWNER_MAINTENANCE(46, "46", "owner maintenance", "", R.string.log_maintained, R.drawable.mark_green_more),
+ UPDATE_COORDINATES(47, "47", "update coordinates", "", R.string.log_update),
+ DISCOVERED_IT(48, "48", "discovered it", "", R.string.log_discovered, R.drawable.mark_green),
+ POST_REVIEWER_NOTE(18, "68", "post reviewer note", "", R.string.log_reviewer),
+ VISIT(1001, "75", "visit", "", R.string.log_tb_visit, R.drawable.mark_green),
+ WEBCAM_PHOTO_TAKEN(11, "11", "webcam photo taken", "", R.string.log_webcam, R.drawable.mark_green),
+ ANNOUNCEMENT(74, "74", "announcement", "", R.string.log_announcement),
+ MOVE_COLLECTION(69, "69", "unused_collection", "", R.string.log_movecollection),
+ MOVE_INVENTORY(70, "70", "unused_inventory", "", R.string.log_moveinventory),
+ RETRACT(25, "25", "retract listing", "", R.string.log_retractlisting),
+ MARKED_MISSING(16, "16", "marked missing", "", R.string.log_marked_missing, R.drawable.mark_red),
+ UNKNOWN(0, "unknown", "", "", R.string.err_unknown, R.drawable.mark_red); // LogType not init. yet
public final int id;
public final String iconName;
public final String type;
+ public final String oc_type;
private final int stringId;
public final int markerId;
- LogType(int id, String iconName, String type, int stringId, int markerId) {
+ LogType(int id, String iconName, String type, String oc_type, int stringId, int markerId) {
this.id = id;
this.iconName = iconName;
this.type = type;
+ this.oc_type = oc_type;
this.stringId = stringId;
this.markerId = markerId;
}
- LogType(int id, String iconName, String type, int stringId) {
- this(id, iconName, type, stringId, R.drawable.mark_gray);
+ LogType(int id, String iconName, String type, String oc_type, int stringId) {
+ this(id, iconName, type, oc_type, stringId, R.drawable.mark_gray);
}
private final static Map<String, LogType> FIND_BY_ICONNAME;
diff --git a/main/src/cgeo/geocaching/loaders/CoordsGeocacheListLoader.java b/main/src/cgeo/geocaching/loaders/CoordsGeocacheListLoader.java
index ca2461c..09ea459 100644
--- a/main/src/cgeo/geocaching/loaders/CoordsGeocacheListLoader.java
+++ b/main/src/cgeo/geocaching/loaders/CoordsGeocacheListLoader.java
@@ -19,7 +19,11 @@ public class CoordsGeocacheListLoader extends AbstractSearchLoader {
@Override
public SearchResult runSearch() {
- SearchResult search = GCParser.searchByCoords(coords, Settings.getCacheType(), Settings.isShowCaptcha(), this);
+
+ SearchResult search = new SearchResult();
+ if (Settings.isGCConnectorActive()) {
+ search = GCParser.searchByCoords(coords, Settings.getCacheType(), Settings.isShowCaptcha(), this);
+ }
for (ISearchByCenter centerConn : ConnectorFactory.getSearchByCenterConnectors()) {
if (centerConn.isActivated()) {
diff --git a/main/src/cgeo/geocaching/maps/CGeoMap.java b/main/src/cgeo/geocaching/maps/CGeoMap.java
index 0af0e6c..ea51375 100644
--- a/main/src/cgeo/geocaching/maps/CGeoMap.java
+++ b/main/src/cgeo/geocaching/maps/CGeoMap.java
@@ -1146,7 +1146,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto
searchResult = ConnectorFactory.searchByViewport(viewport.resize(0.8), tokens);
if (searchResult != null) {
downloaded = true;
- if (searchResult.getError() == StatusCode.NOT_LOGGED_IN) {
+ if (searchResult.getError() == StatusCode.NOT_LOGGED_IN && Settings.isGCConnectorActive()) {
Login.login();
tokens = null;
} else {
diff --git a/main/src/cgeo/geocaching/network/OAuth.java b/main/src/cgeo/geocaching/network/OAuth.java
index 0b7a261..6740096 100644
--- a/main/src/cgeo/geocaching/network/OAuth.java
+++ b/main/src/cgeo/geocaching/network/OAuth.java
@@ -1,9 +1,9 @@
package cgeo.geocaching.network;
-import cgeo.geocaching.Settings;
import cgeo.geocaching.utils.CryptUtils;
import ch.boye.httpclientandroidlib.NameValuePair;
+
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
@@ -11,9 +11,17 @@ import java.util.Date;
import java.util.List;
public class OAuth {
- public static void signOAuth(final String host, final String path, final String method, final boolean https, final Parameters params, final String token, final String tokenSecret) {
+ public static void signOAuth(final String host,
+ final String path,
+ final String method,
+ final boolean https,
+ final Parameters params,
+ final String token,
+ final String tokenSecret,
+ final String consumerKey,
+ final String consumerSecret) {
params.put(
- "oauth_consumer_key", Settings.getKeyConsumerPublic(),
+ "oauth_consumer_key", consumerKey,
"oauth_nonce", CryptUtils.md5(Long.toString(System.currentTimeMillis())),
"oauth_signature_method", "HMAC-SHA1",
"oauth_timestamp", Long.toString(new Date().getTime() / 1000),
@@ -26,7 +34,7 @@ public class OAuth {
paramsEncoded.add(nameValue.getName() + "=" + Network.rfc3986URLEncode(nameValue.getValue()));
}
- final String keysPacked = Settings.getKeyConsumerSecret() + "&" + StringUtils.defaultString(tokenSecret); // both even if empty some of them!
+ final String keysPacked = consumerSecret + "&" + StringUtils.defaultString(tokenSecret); // both even if empty some of them!
final String requestPacked = method + "&" + Network.rfc3986URLEncode((https ? "https" : "http") + "://" + host + path) + "&" + Network.rfc3986URLEncode(StringUtils.join(paramsEncoded.toArray(), '&'));
params.put("oauth_signature", CryptUtils.base64Encode(CryptUtils.hashHmac(requestPacked, keysPacked)));
}
diff --git a/main/src/cgeo/geocaching/network/OAuthAuthorizationActivity.java b/main/src/cgeo/geocaching/network/OAuthAuthorizationActivity.java
new file mode 100644
index 0000000..751443e
--- /dev/null
+++ b/main/src/cgeo/geocaching/network/OAuthAuthorizationActivity.java
@@ -0,0 +1,326 @@
+package cgeo.geocaching.network;
+
+import cgeo.geocaching.R;
+import cgeo.geocaching.activity.AbstractActivity;
+import cgeo.geocaching.utils.Log;
+import cgeo.geocaching.utils.MatcherWrapper;
+
+import ch.boye.httpclientandroidlib.client.entity.UrlEncodedFormEntity;
+import ch.boye.httpclientandroidlib.util.EntityUtils;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import java.util.regex.Pattern;
+
+public abstract class OAuthAuthorizationActivity extends AbstractActivity {
+
+ private String host;
+ private String pathRequest;
+ private String pathAuthorize;
+ private String pathAccess;
+ private boolean https;
+ private String consumerKey;
+ private String consumerSecret;
+ private String OAtoken = null;
+ private String OAtokenSecret = null;
+ private final Pattern paramsPattern1 = Pattern.compile("oauth_token=([a-zA-Z0-9\\-\\_.]+)");
+ private final Pattern paramsPattern2 = Pattern.compile("oauth_token_secret=([a-zA-Z0-9\\-\\_.]+)");
+ private Button startButton = null;
+ private EditText pinEntry = null;
+ private Button pinEntryButton = null;
+ private ProgressDialog requestTokenDialog = null;
+ private ProgressDialog changeTokensDialog = null;
+ private Handler requestTokenHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (requestTokenDialog != null && requestTokenDialog.isShowing()) {
+ requestTokenDialog.dismiss();
+ }
+
+ startButton.setOnClickListener(new StartListener());
+ startButton.setEnabled(true);
+
+ if (msg.what == 1) {
+ startButton.setText(getAuthAgain());
+
+ pinEntry.setVisibility(View.VISIBLE);
+ pinEntryButton.setVisibility(View.VISIBLE);
+ pinEntryButton.setOnClickListener(new ConfirmPINListener());
+ } else {
+ showToast(getErrAuthInitialize());
+ startButton.setText(getAuthStart());
+ }
+ }
+
+ };
+ private Handler changeTokensHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (changeTokensDialog != null && changeTokensDialog.isShowing()) {
+ changeTokensDialog.dismiss();
+ }
+
+ pinEntryButton.setOnClickListener(new ConfirmPINListener());
+ pinEntryButton.setEnabled(true);
+
+ if (msg.what == 1) {
+ showToast(getAuthDialogCompleted());
+
+ pinEntryButton.setVisibility(View.GONE);
+
+ finish();
+ } else {
+ showToast(getErrAuthProcess());
+
+ pinEntry.setVisibility(View.GONE);
+ pinEntryButton.setVisibility(View.GONE);
+ startButton.setText(getAuthStart());
+ }
+ }
+ };
+
+ public OAuthAuthorizationActivity(String host,
+ String pathRequest,
+ String pathAuthorize,
+ String pathAccess,
+ boolean https,
+ String consumerKey,
+ String consumerSecret) {
+ this.host = host;
+ this.pathRequest = pathRequest;
+ this.pathAuthorize = pathAuthorize;
+ this.pathAccess = pathAccess;
+ this.https = https;
+ this.consumerKey = consumerKey;
+ this.consumerSecret = consumerSecret;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState, R.layout.authorization_activity);
+
+ setTitle(getAuthTitle());
+
+ init();
+ }
+
+ private void init() {
+ startButton = (Button) findViewById(R.id.start);
+ pinEntry = (EditText) findViewById(R.id.pin);
+ pinEntryButton = (Button) findViewById(R.id.pin_button);
+
+ TextView auth = (TextView) findViewById(R.id.auth_1);
+ auth.setText(getAboutAuth1());
+ auth = (TextView) findViewById(R.id.auth_2);
+ auth.setText(getAboutAuth2());
+
+ ImmutablePair<String, String> tempToken = getTempToken();
+ OAtoken = tempToken.left;
+ OAtokenSecret = tempToken.right;
+
+ startButton.setText(getAuthAuthorize());
+ pinEntryButton.setText(getAuthFinish());
+
+ startButton.setEnabled(true);
+ startButton.setOnClickListener(new StartListener());
+
+ if (StringUtils.isBlank(OAtoken) && StringUtils.isBlank(OAtokenSecret)) {
+ // start authorization process
+ startButton.setText(getAuthStart());
+ } else {
+ // already have temporary tokens, continue from pin
+ startButton.setText(getAuthAgain());
+
+ pinEntry.setHint(getAuthPinHint());
+ pinEntry.setVisibility(View.VISIBLE);
+ pinEntryButton.setVisibility(View.VISIBLE);
+ pinEntryButton.setOnClickListener(new ConfirmPINListener());
+ }
+ }
+
+ private void requestToken() {
+
+ int status = 0;
+ try {
+ final Parameters params = new Parameters();
+ params.put("oauth_callback", "oob");
+ final String method = "GET";
+ OAuth.signOAuth(host, pathRequest, method, https, params, null, null, consumerKey, consumerSecret);
+ final String line = Network.getResponseData(Network.getRequest(getUrlPrefix() + host + pathRequest, params));
+
+ if (StringUtils.isNotBlank(line)) {
+ final MatcherWrapper paramsMatcher1 = new MatcherWrapper(paramsPattern1, line);
+ if (paramsMatcher1.find()) {
+ OAtoken = paramsMatcher1.group(1);
+ }
+ final MatcherWrapper paramsMatcher2 = new MatcherWrapper(paramsPattern2, line);
+ if (paramsMatcher2.find()) {
+ OAtokenSecret = paramsMatcher2.group(1);
+ }
+
+ if (StringUtils.isNotBlank(OAtoken) && StringUtils.isNotBlank(OAtokenSecret)) {
+ setTempTokens(OAtoken, OAtokenSecret);
+ try {
+ final Parameters paramsBrowser = new Parameters();
+ paramsBrowser.put("oauth_token", OAtoken);
+ final String encodedParams = EntityUtils.toString(new UrlEncodedFormEntity(paramsBrowser));
+ startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getUrlPrefix() + host + pathAuthorize + "?" + encodedParams)));
+ status = 1;
+ } catch (Exception e) {
+ Log.e("OAuthAuthorizationActivity.requestToken(2)", e);
+ }
+ }
+ }
+ } catch (Exception e) {
+ Log.e("OAuthAuthorizationActivity.requestToken(1)", e);
+ }
+
+ requestTokenHandler.sendEmptyMessage(status);
+ }
+
+ private void changeToken() {
+
+ int status = 0;
+
+ try {
+ final Parameters params = new Parameters("oauth_verifier", pinEntry.getText().toString());
+
+ final String method = "POST";
+ OAuth.signOAuth(host, pathAccess, method, https, params, OAtoken, OAtokenSecret, consumerKey, consumerSecret);
+ final String line = StringUtils.defaultString(Network.getResponseData(Network.postRequest(getUrlPrefix() + host + pathAccess, params)));
+
+ OAtoken = "";
+ OAtokenSecret = "";
+
+ final MatcherWrapper paramsMatcher1 = new MatcherWrapper(paramsPattern1, line);
+ if (paramsMatcher1.find()) {
+ OAtoken = paramsMatcher1.group(1);
+ }
+ final MatcherWrapper paramsMatcher2 = new MatcherWrapper(paramsPattern2, line);
+ if (paramsMatcher2.find() && paramsMatcher2.groupCount() > 0) {
+ OAtokenSecret = paramsMatcher2.group(1);
+ }
+
+ if (StringUtils.isBlank(OAtoken) && StringUtils.isBlank(OAtokenSecret)) {
+ OAtoken = "";
+ OAtokenSecret = "";
+ setTokens(null, null, false);
+ } else {
+ setTokens(OAtoken, OAtokenSecret, true);
+ status = 1;
+ }
+ } catch (Exception e) {
+ Log.e("OAuthAuthorizationActivity.changeToken", e);
+ }
+
+ changeTokensHandler.sendEmptyMessage(status);
+ }
+
+ private String getUrlPrefix() {
+ return https ? "https://" : "http://";
+ }
+
+ private class StartListener implements View.OnClickListener {
+
+ @Override
+ public void onClick(View arg0) {
+ if (requestTokenDialog == null) {
+ requestTokenDialog = new ProgressDialog(OAuthAuthorizationActivity.this);
+ requestTokenDialog.setCancelable(false);
+ requestTokenDialog.setMessage(getAuthDialogWait());
+ }
+ requestTokenDialog.show();
+ startButton.setEnabled(false);
+ startButton.setOnTouchListener(null);
+ startButton.setOnClickListener(null);
+
+ setTempTokens(null, null);
+ (new Thread() {
+
+ @Override
+ public void run() {
+ requestToken();
+ }
+ }).start();
+ }
+ }
+
+ private class ConfirmPINListener implements View.OnClickListener {
+
+ @Override
+ public void onClick(View arg0) {
+ if (StringUtils.isEmpty(((EditText) findViewById(R.id.pin)).getText().toString())) {
+ helpDialog(getAuthDialogPinTitle(), getAuthDialogPinMessage());
+ return;
+ }
+
+ if (changeTokensDialog == null) {
+ changeTokensDialog = new ProgressDialog(OAuthAuthorizationActivity.this);
+ changeTokensDialog.setCancelable(false);
+ changeTokensDialog.setMessage(getAuthDialogWait());
+ }
+ changeTokensDialog.show();
+ pinEntryButton.setEnabled(false);
+ pinEntryButton.setOnTouchListener(null);
+ pinEntryButton.setOnClickListener(null);
+
+ (new Thread() {
+
+ @Override
+ public void run() {
+ changeToken();
+ }
+ }).start();
+ }
+ }
+
+ protected abstract ImmutablePair<String, String> getTempToken();
+
+ protected abstract void setTempTokens(String tokenPublic, String tokenSecret);
+
+ protected abstract void setTokens(String tokenPublic, String tokenSecret, boolean enable);
+
+ // get resources from derived class
+
+ protected abstract String getAuthTitle();
+
+ protected abstract String getAuthAgain();
+
+ protected abstract String getErrAuthInitialize();
+
+ protected abstract String getAuthStart();
+
+ protected abstract String getAuthDialogCompleted();
+
+ protected abstract String getErrAuthProcess();
+
+ protected abstract String getAuthDialogWait();
+
+ protected abstract String getAuthDialogPinTitle();
+
+ protected abstract String getAuthDialogPinMessage();
+
+ protected abstract String getAboutAuth1();
+
+ protected abstract String getAboutAuth2();
+
+ protected abstract String getAuthAuthorize();
+
+ protected abstract String getAuthPinHint();
+
+ protected abstract String getAuthFinish();
+}
diff --git a/main/src/cgeo/geocaching/twitter/Twitter.java b/main/src/cgeo/geocaching/twitter/Twitter.java
index e3d3f77..525b7f0 100644
--- a/main/src/cgeo/geocaching/twitter/Twitter.java
+++ b/main/src/cgeo/geocaching/twitter/Twitter.java
@@ -38,7 +38,7 @@ public final class Twitter {
"display_coordinates", "true");
}
- OAuth.signOAuth("api.twitter.com", "/1/statuses/update.json", "POST", false, parameters, Settings.getTokenPublic(), Settings.getTokenSecret());
+ OAuth.signOAuth("api.twitter.com", "/1/statuses/update.json", "POST", false, parameters, Settings.getTokenPublic(), Settings.getTokenSecret(), Settings.getKeyConsumerPublic(), Settings.getKeyConsumerSecret());
final HttpResponse httpResponse = Network.postRequest("http://api.twitter.com/1/statuses/update.json", parameters);
if (httpResponse != null && httpResponse.getStatusLine().getStatusCode() == 200) {
Log.i("Tweet posted");
diff --git a/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java b/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java
index 3bc1dec..7146a62 100644
--- a/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java
+++ b/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java
@@ -2,258 +2,105 @@ package cgeo.geocaching.twitter;
import cgeo.geocaching.R;
import cgeo.geocaching.Settings;
-import cgeo.geocaching.activity.AbstractActivity;
-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 cgeo.geocaching.network.OAuthAuthorizationActivity;
-import ch.boye.httpclientandroidlib.client.entity.UrlEncodedFormEntity;
-import ch.boye.httpclientandroidlib.util.EntityUtils;
-
-import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
-import android.app.ProgressDialog;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.view.View;
-import android.widget.Button;
-import android.widget.EditText;
-
-import java.util.regex.Pattern;
-
-public class TwitterAuthorizationActivity extends AbstractActivity {
- private String OAtoken = null;
- private String OAtokenSecret = null;
- private final Pattern paramsPattern1 = Pattern.compile("oauth_token=([a-zA-Z0-9\\-\\_.]+)");
- private final Pattern paramsPattern2 = Pattern.compile("oauth_token_secret=([a-zA-Z0-9\\-\\_.]+)");
- private Button startButton = null;
- private EditText pinEntry = null;
- private Button pinEntryButton = null;
- private ProgressDialog requestTokenDialog = null;
- private ProgressDialog changeTokensDialog = null;
- private Handler requestTokenHandler = new Handler() {
-
- @Override
- public void handleMessage(Message msg) {
- if (requestTokenDialog != null && requestTokenDialog.isShowing()) {
- requestTokenDialog.dismiss();
- }
-
- startButton.setOnClickListener(new StartListener());
- startButton.setEnabled(true);
-
- if (msg.what == 1) {
- startButton.setText(res.getString(R.string.auth_again));
-
- pinEntry.setVisibility(View.VISIBLE);
- pinEntryButton.setVisibility(View.VISIBLE);
- pinEntryButton.setOnClickListener(new ConfirmPINListener());
- } else {
- showToast(res.getString(R.string.err_auth_initialize));
- startButton.setText(res.getString(R.string.auth_start));
- }
- }
- };
- private Handler changeTokensHandler = new Handler() {
+public class TwitterAuthorizationActivity extends OAuthAuthorizationActivity {
- @Override
- public void handleMessage(Message msg) {
- if (changeTokensDialog != null && changeTokensDialog.isShowing()) {
- changeTokensDialog.dismiss();
- }
-
- pinEntryButton.setOnClickListener(new ConfirmPINListener());
- pinEntryButton.setEnabled(true);
-
- if (msg.what == 1) {
- showToast(res.getString(R.string.auth_dialog_completed));
-
- pinEntryButton.setVisibility(View.GONE);
-
- finish();
- } else {
- showToast(res.getString(R.string.err_auth_process));
-
- pinEntry.setVisibility(View.GONE);
- pinEntryButton.setVisibility(View.GONE);
- startButton.setText(res.getString(R.string.auth_start));
- }
- }
- };
+ public TwitterAuthorizationActivity() {
+ super("api.twitter.com",
+ "/oauth/request_token",
+ "/oauth/authorize",
+ "/oauth/access_token",
+ true,
+ Settings.getKeyConsumerPublic(),
+ Settings.getKeyConsumerSecret());
+ }
@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState, R.layout.twitter_authorization_activity);
-
- init();
+ protected ImmutablePair<String, String> getTempToken() {
+ return Settings.getTempToken();
}
- private void init() {
- startButton = (Button) findViewById(R.id.start);
- pinEntry = (EditText) findViewById(R.id.pin);
- pinEntryButton = (Button) findViewById(R.id.pin_button);
-
- ImmutablePair<String, String> tempToken = Settings.getTempToken();
- OAtoken = tempToken.left;
- OAtokenSecret = tempToken.right;
-
- startButton.setEnabled(true);
- startButton.setOnClickListener(new StartListener());
-
- if (StringUtils.isBlank(OAtoken) && StringUtils.isBlank(OAtokenSecret)) {
- // start authorization process
- startButton.setText(res.getString(R.string.auth_start));
- } else {
- // already have temporary tokens, continue from pin
- startButton.setText(res.getString(R.string.auth_again));
-
- pinEntry.setVisibility(View.VISIBLE);
- pinEntryButton.setVisibility(View.VISIBLE);
- pinEntryButton.setOnClickListener(new ConfirmPINListener());
- }
+ @Override
+ protected void setTempTokens(String tokenPublic, String tokenSecret) {
+ Settings.setTwitterTempTokens(tokenPublic, tokenSecret);
}
- private void requestToken() {
-
- int status = 0;
- try {
- final Parameters params = new Parameters();
- final String method = "GET";
- final String pathRequest = "/oauth/request_token";
- final String host = "api.twitter.com";
- OAuth.signOAuth(host, pathRequest, method, true, params, null, null);
- final String line = Network.getResponseData(Network.getRequest("https://" + host + pathRequest, params));
-
-
- if (StringUtils.isNotBlank(line)) {
- final MatcherWrapper paramsMatcher1 = new MatcherWrapper(paramsPattern1, line);
- if (paramsMatcher1.find()) {
- OAtoken = paramsMatcher1.group(1);
- }
- final MatcherWrapper paramsMatcher2 = new MatcherWrapper(paramsPattern2, line);
- if (paramsMatcher2.find()) {
- OAtokenSecret = paramsMatcher2.group(1);
- }
-
- if (StringUtils.isNotBlank(OAtoken) && StringUtils.isNotBlank(OAtokenSecret)) {
- Settings.setTwitterTempTokens(OAtoken, OAtokenSecret);
- try {
- final Parameters paramsBrowser = new Parameters();
- paramsBrowser.put("oauth_callback", "oob");
- final String pathAuthorize = "/oauth/authorize";
- OAuth.signOAuth(host, pathAuthorize, "GET", true, paramsBrowser, OAtoken, OAtokenSecret);
- final String encodedParams = EntityUtils.toString(new UrlEncodedFormEntity(paramsBrowser));
- startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://" + host + pathAuthorize + "?" + encodedParams)));
- status = 1;
- } catch (Exception e) {
- Log.e("TwitterAuthorizationActivity.requestToken(2)", e);
- }
- }
- }
- } catch (Exception e) {
- Log.e("TwitterAuthorizationActivity.requestToken(1)", e);
- }
-
- requestTokenHandler.sendEmptyMessage(status);
+ @Override
+ protected void setTokens(String tokenPublic, String tokenSecret, boolean enable) {
+ Settings.setTwitterTokens(tokenPublic, tokenSecret, enable);
}
- private void changeToken() {
-
- int status = 0;
-
- try {
- final Parameters params = new Parameters("oauth_verifier", pinEntry.getText().toString());
-
- final String method = "POST";
- final String path = "/oauth/access_token";
- final String host = "api.twitter.com";
- OAuth.signOAuth(host, path, method, true, params, OAtoken, OAtokenSecret);
- final String line = StringUtils.defaultString(Network.getResponseData(Network.postRequest("https://" + host + path, params)));
+ @Override
+ protected String getAuthTitle() {
+ return res.getString(R.string.auth_twitter);
+ }
- OAtoken = "";
- OAtokenSecret = "";
+ @Override
+ protected String getAuthAgain() {
+ return res.getString(R.string.auth_again);
+ }
- final MatcherWrapper paramsMatcher1 = new MatcherWrapper(paramsPattern1, line);
- if (paramsMatcher1.find()) {
- OAtoken = paramsMatcher1.group(1);
- }
- final MatcherWrapper paramsMatcher2 = new MatcherWrapper(paramsPattern2, line);
- if (paramsMatcher2.find() && paramsMatcher2.groupCount() > 0) {
- OAtokenSecret = paramsMatcher2.group(1);
- }
+ @Override
+ protected String getErrAuthInitialize() {
+ return res.getString(R.string.err_auth_initialize);
+ }
- if (StringUtils.isBlank(OAtoken) && StringUtils.isBlank(OAtokenSecret)) {
- OAtoken = "";
- OAtokenSecret = "";
- Settings.setTwitterTokens(null, null, false);
- } else {
- Settings.setTwitterTokens(OAtoken, OAtokenSecret, true);
- status = 1;
- }
- } catch (Exception e) {
- Log.e("TwitterAuthorizationActivity.changeToken", e);
- }
+ @Override
+ protected String getAuthStart() {
+ return res.getString(R.string.auth_start);
+ }
- changeTokensHandler.sendEmptyMessage(status);
+ @Override
+ protected String getAuthDialogCompleted() {
+ return res.getString(R.string.auth_dialog_completed);
}
- private class StartListener implements View.OnClickListener {
+ @Override
+ protected String getErrAuthProcess() {
+ return res.getString(R.string.err_auth_process);
+ }
- @Override
- public void onClick(View arg0) {
- if (requestTokenDialog == null) {
- requestTokenDialog = new ProgressDialog(TwitterAuthorizationActivity.this);
- requestTokenDialog.setCancelable(false);
- requestTokenDialog.setMessage(res.getString(R.string.auth_dialog_wait));
- }
- requestTokenDialog.show();
- startButton.setEnabled(false);
- startButton.setOnTouchListener(null);
- startButton.setOnClickListener(null);
+ @Override
+ protected String getAuthDialogWait() {
+ return res.getString(R.string.auth_dialog_wait);
+ }
- Settings.setTwitterTempTokens(null, null);
- (new Thread() {
+ @Override
+ protected String getAuthDialogPinTitle() {
+ return res.getString(R.string.auth_dialog_pin_title);
+ }
- @Override
- public void run() {
- requestToken();
- }
- }).start();
- }
+ @Override
+ protected String getAuthDialogPinMessage() {
+ return res.getString(R.string.auth_dialog_pin_message);
}
- private class ConfirmPINListener implements View.OnClickListener {
+ @Override
+ protected String getAboutAuth1() {
+ return res.getString(R.string.about_auth_1);
+ }
- @Override
- public void onClick(View arg0) {
- if (StringUtils.isEmpty(((EditText) findViewById(R.id.pin)).getText().toString())) {
- helpDialog(res.getString(R.string.auth_dialog_pin_title), res.getString(R.string.auth_dialog_pin_message));
- return;
- }
+ @Override
+ protected String getAboutAuth2() {
+ return res.getString(R.string.about_auth_2);
+ }
- if (changeTokensDialog == null) {
- changeTokensDialog = new ProgressDialog(TwitterAuthorizationActivity.this);
- changeTokensDialog.setCancelable(false);
- changeTokensDialog.setMessage(res.getString(R.string.auth_dialog_wait));
- }
- changeTokensDialog.show();
- pinEntryButton.setEnabled(false);
- pinEntryButton.setOnTouchListener(null);
- pinEntryButton.setOnClickListener(null);
+ @Override
+ protected String getAuthAuthorize() {
+ return res.getString(R.string.auth_authorize);
+ }
- (new Thread() {
+ @Override
+ protected String getAuthPinHint() {
+ return res.getString(R.string.auth_pin_hint);
+ }
- @Override
- public void run() {
- changeToken();
- }
- }).start();
- }
+ @Override
+ protected String getAuthFinish() {
+ return res.getString(R.string.auth_finish);
}
+
}
diff --git a/main/templates/ocde_okapi.xml b/main/templates/ocde_okapi.xml
new file mode 100644
index 0000000..9ca39b3
--- /dev/null
+++ b/main/templates/ocde_okapi.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources>
+ <string name="oc_de_okapi_consumer_key" translatable="false">@ocde.okapi.consumer.key@</string>
+ <string name="oc_de_okapi_consumer_secret" translatable="false">@ocde.okapi.consumer.secret@</string>
+</resources>
diff --git a/main/templates/private.properties b/main/templates/private.properties
index f29cb7c..13c424b 100644
--- a/main/templates/private.properties
+++ b/main/templates/private.properties
@@ -16,3 +16,10 @@ maps.api.key.market=
#key.alias=
#key.store.password=
#key.alias.password=
+
+# These keys allow c:geo to be registered at openaching.de and
+# to search and log caches in your name
+# You can request your own at http://www.opencaching.de/okapi/signup.html
+
+ocde.okapi.consumer.key=
+ocde.okapi.consumer.secret=