diff options
200 files changed, 2933 insertions, 1719 deletions
diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml index 1a974ed..e355fde 100644 --- a/.idea/codeStyleSettings.xml +++ b/.idea/codeStyleSettings.xml @@ -39,7 +39,11 @@ <emptyLine /> </value> </option> + <option name="JD_KEEP_INVALID_TAGS" value="false" /> <option name="JD_DO_NOT_WRAP_ONE_LINE_COMMENTS" value="true" /> + <option name="JD_KEEP_EMPTY_PARAMETER" value="false" /> + <option name="JD_KEEP_EMPTY_EXCEPTION" value="false" /> + <option name="JD_KEEP_EMPTY_RETURN" value="false" /> <option name="JD_PARAM_DESCRIPTION_ON_NEW_LINE" value="true" /> <option name="WRAP_COMMENTS" value="true" /> <XML> diff --git a/cgeo-calendar/.settings/org.eclipse.jdt.core.prefs b/cgeo-calendar/.settings/org.eclipse.jdt.core.prefs index 25aa954..10b38b3 100644 --- a/cgeo-calendar/.settings/org.eclipse.jdt.core.prefs +++ b/cgeo-calendar/.settings/org.eclipse.jdt.core.prefs @@ -40,8 +40,8 @@ org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore diff --git a/cgeo-calendar/build.gradle b/cgeo-calendar/build.gradle index 7ca4741..4663e5f 100644 --- a/cgeo-calendar/build.gradle +++ b/cgeo-calendar/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 19 - buildToolsVersion "21.1" + buildToolsVersion "21.1.2" diff --git a/cgeo-calendar/src/cgeo/calendar/AbstractAddEntry.java b/cgeo-calendar/src/cgeo/calendar/AbstractAddEntry.java index 31e2bd9..893161c 100644 --- a/cgeo-calendar/src/cgeo/calendar/AbstractAddEntry.java +++ b/cgeo-calendar/src/cgeo/calendar/AbstractAddEntry.java @@ -4,10 +4,10 @@ import android.util.Log; abstract class AbstractAddEntry { - protected CalendarEntry entry; - protected CalendarActivity activity; + protected final CalendarEntry entry; + protected final CalendarActivity activity; - public AbstractAddEntry(final CalendarEntry entry, CalendarActivity activity) { + public AbstractAddEntry(final CalendarEntry entry, final CalendarActivity activity) { this.entry = entry; this.activity = activity; } @@ -16,7 +16,7 @@ abstract class AbstractAddEntry { try { addEntryToCalendarInternal(); activity.showToast(R.string.event_success); - } catch (Exception e) { + } catch (final Exception e) { activity.showToast(R.string.event_fail); Log.e(CalendarActivity.LOG_TAG, "addToCalendar", e); diff --git a/cgeo-calendar/src/cgeo/calendar/AddEntry.java b/cgeo-calendar/src/cgeo/calendar/AddEntry.java index cdb59d9..30af346 100644 --- a/cgeo-calendar/src/cgeo/calendar/AddEntry.java +++ b/cgeo-calendar/src/cgeo/calendar/AddEntry.java @@ -8,14 +8,15 @@ import java.util.Date; class AddEntry extends AbstractAddEntry { - private int calendarId; + private final int calendarId; /** * @param entry + * new entry to be stored * @param calendarId - * The selected calendar + * The selected calendar */ - public AddEntry(CalendarEntry entry, CalendarActivity activity, int calendarId) { + public AddEntry(final CalendarEntry entry, final CalendarActivity activity, final int calendarId) { super(entry, activity); this.calendarId = calendarId; } diff --git a/cgeo-calendar/src/cgeo/calendar/AddEntryLevel14.java b/cgeo-calendar/src/cgeo/calendar/AddEntryLevel14.java index 0ceed67..11820b4 100644 --- a/cgeo-calendar/src/cgeo/calendar/AddEntryLevel14.java +++ b/cgeo-calendar/src/cgeo/calendar/AddEntryLevel14.java @@ -12,7 +12,7 @@ import java.util.Date; */ class AddEntryLevel14 extends AbstractAddEntry { - public AddEntryLevel14(CalendarEntry entry, CalendarActivity activity) { + public AddEntryLevel14(final CalendarEntry entry, final CalendarActivity activity) { super(entry, activity); } diff --git a/cgeo-calendar/src/cgeo/calendar/CalendarActivity.java b/cgeo-calendar/src/cgeo/calendar/CalendarActivity.java index 14d71b4..2f0c08d 100644 --- a/cgeo-calendar/src/cgeo/calendar/CalendarActivity.java +++ b/cgeo-calendar/src/cgeo/calendar/CalendarActivity.java @@ -16,7 +16,7 @@ public final class CalendarActivity extends Activity { /** Called when the activity is first created. */ @Override - public void onCreate(Bundle savedInstanceState) { + public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); try { @@ -35,7 +35,7 @@ public final class CalendarActivity extends Activity { } else { selectCalendarForAdding(entry); } - } catch (Exception e) { + } catch (final Exception e) { Log.e(LOG_TAG, e.getMessage(), e); finish(); } @@ -45,6 +45,7 @@ public final class CalendarActivity extends Activity { * Adds the cache to the Android-calendar if it is an event. * * @param entry + * new entry to be stored */ private void selectCalendarForAdding(final CalendarEntry entry) { final SparseArray<String> calendars = queryCalendars(); @@ -64,7 +65,7 @@ public final class CalendarActivity extends Activity { builder.setTitle(R.string.calendars); builder.setItems(items, new DialogInterface.OnClickListener() { @Override - public void onClick(DialogInterface dialog, int item) { + public void onClick(final DialogInterface dialog, final int item) { final int calendarId = calendars.keyAt(item); new AddEntry(entry, CalendarActivity.this, calendarId).addEntryToCalendar(); finish(); @@ -72,7 +73,7 @@ public final class CalendarActivity extends Activity { }); builder.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override - public void onCancel(DialogInterface dialog) { + public void onCancel(final DialogInterface dialog) { finish(); } }); @@ -82,11 +83,11 @@ public final class CalendarActivity extends Activity { private SparseArray<String> queryCalendars() { final SparseArray<String> calendars = new SparseArray<>(); - final String[] projection = new String[] { "_id", "displayName" }; final Uri calendarProvider = Compatibility.getCalendarProviderURI(); Cursor cursor = null; try { + final String[] projection = new String[]{"_id", "displayName"}; cursor = getContentResolver().query(calendarProvider, projection, "selected=1", null, null); if (cursor == null) { @@ -102,13 +103,13 @@ public final class CalendarActivity extends Activity { final String idString = cursor.getString(indexId); if (idString != null) { try { - int id = Integer.parseInt(idString); + final int id = Integer.parseInt(idString); final String calName = cursor.getString(indexName); if (id > 0 && calName != null) { calendars.put(id, calName); } - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e(LOG_TAG, "CalendarActivity.selectCalendarForAdding", e); } } diff --git a/cgeo-calendar/src/cgeo/calendar/CalendarEntry.java b/cgeo-calendar/src/cgeo/calendar/CalendarEntry.java index a1761c7..1e37d6b 100644 --- a/cgeo-calendar/src/cgeo/calendar/CalendarEntry.java +++ b/cgeo-calendar/src/cgeo/calendar/CalendarEntry.java @@ -16,14 +16,14 @@ import java.util.Date; class CalendarEntry { - private String shortDesc; - private String hiddenDate; - private String url; - private String personalNote; - private String name; - private String coords; + private final String shortDesc; + private final String hiddenDate; + private final String url; + private final String personalNote; + private final String name; + private final String coords; private int startTimeMinutes = -1; - private Uri uri; + private final Uri uri; public CalendarEntry(final Uri uri) { this.uri = uri; @@ -37,7 +37,7 @@ class CalendarEntry { if (startTime.length() > 0) { try { this.startTimeMinutes = Integer.parseInt(startTime); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("CalendarEntry creation", e); } } @@ -50,7 +50,7 @@ class CalendarEntry { return ""; } return URLDecoder.decode(param, CharEncoding.UTF_8).trim(); - } catch (UnsupportedEncodingException e) { + } catch (final UnsupportedEncodingException e) { Log.e("CalendarEntry.getParameter", e); } return ""; @@ -81,14 +81,14 @@ class CalendarEntry { */ protected Date parseDate() { try { - Calendar cal = Calendar.getInstance(); + final Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(Long.parseLong(getHiddenDate())); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); return cal.getTime(); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { // cannot happen normally, but static code analysis does not know } return null; diff --git a/cgeo-calendar/src/cgeo/calendar/Compatibility.java b/cgeo-calendar/src/cgeo/calendar/Compatibility.java index 8813f41..ff14a0f 100644 --- a/cgeo-calendar/src/cgeo/calendar/Compatibility.java +++ b/cgeo-calendar/src/cgeo/calendar/Compatibility.java @@ -5,19 +5,19 @@ import android.os.Build; public final class Compatibility { - private final static int sdkVersion = Build.VERSION.SDK_INT; - private final static boolean isLevel8 = sdkVersion >= 8; - private final static boolean isLevel14 = sdkVersion >= 14; + private final static int SDK_VERSION = Build.VERSION.SDK_INT; + private final static boolean IS_LEVEL_8 = SDK_VERSION >= 8; + private final static boolean IS_LEVEL_14 = SDK_VERSION >= 14; public static Uri getCalendarProviderURI() { - return Uri.parse(isLevel8 ? "content://com.android.calendar/calendars" : "content://calendar/calendars"); + return Uri.parse(IS_LEVEL_8 ? "content://com.android.calendar/calendars" : "content://calendar/calendars"); } public static Uri getCalendarEventsProviderURI() { - return Uri.parse(isLevel8 ? "content://com.android.calendar/events" : "content://calendar/events"); + return Uri.parse(IS_LEVEL_8 ? "content://com.android.calendar/events" : "content://calendar/events"); } public static boolean isLevel14() { - return isLevel14; + return IS_LEVEL_14; } } diff --git a/cgeo-contacts/.settings/org.eclipse.jdt.core.prefs b/cgeo-contacts/.settings/org.eclipse.jdt.core.prefs index 0dabcc7..859272c 100644 --- a/cgeo-contacts/.settings/org.eclipse.jdt.core.prefs +++ b/cgeo-contacts/.settings/org.eclipse.jdt.core.prefs @@ -40,8 +40,8 @@ org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore diff --git a/cgeo-contacts/build.gradle b/cgeo-contacts/build.gradle index 3d99c29..74d135b 100644 --- a/cgeo-contacts/build.gradle +++ b/cgeo-contacts/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 19 - buildToolsVersion "21.1" + buildToolsVersion "21.1.2" diff --git a/cgeo-contacts/src/cgeo/contacts/ContactsActivity.java b/cgeo-contacts/src/cgeo/contacts/ContactsActivity.java index bbda493..7d2f1e1 100644 --- a/cgeo-contacts/src/cgeo/contacts/ContactsActivity.java +++ b/cgeo-contacts/src/cgeo/contacts/ContactsActivity.java @@ -28,7 +28,7 @@ public final class ContactsActivity extends Activity { static final String LOG_TAG = "cgeo.contacts"; @Override - public void onCreate(Bundle savedInstanceState) { + public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Uri uri = getIntent().getData(); @@ -67,14 +67,14 @@ public final class ContactsActivity extends Activity { selectContact(contacts); } else { - int contactId = contacts.get(0).first; + final int contactId = contacts.get(0).first; openContactAndFinish(contactId); } } private void selectContact(final List<Pair<Integer, String>> contacts) { - List<String> list = new ArrayList<>(); - for (Pair<Integer, String> p : contacts) { + final List<String> list = new ArrayList<>(); + for (final Pair<Integer, String> p : contacts) { list.add(p.second); } final CharSequence[] items = list.toArray(new CharSequence[list.size()]); @@ -82,8 +82,9 @@ public final class ContactsActivity extends Activity { .setTitle(R.string.multiple_matches) .setItems(items, new OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - int contactId = contacts.get(which).first; + @Override + public void onClick(final DialogInterface dialog, final int which) { + final int contactId = contacts.get(which).first; dialog.dismiss(); openContactAndFinish(contactId); } @@ -91,29 +92,29 @@ public final class ContactsActivity extends Activity { .create().show(); } - private void openContactAndFinish(int id) { + private void openContactAndFinish(final int id) { final Intent intent = new Intent(Intent.ACTION_VIEW); - Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(id)); + final Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(id)); intent.setData(uri); startActivity(intent); finish(); } - private List<Pair<Integer, String>> getContacts(final @NonNull String searchName, Uri uri, final @NonNull String idColumnName, final @NonNull String selectionColumnName, boolean like) { + private List<Pair<Integer, String>> getContacts(final @NonNull String searchName, final Uri uri, final @NonNull String idColumnName, final @NonNull String selectionColumnName, final boolean like) { final String[] projection = new String[] { idColumnName, selectionColumnName }; final String selection = selectionColumnName + (like ? " LIKE" : " =") + " ? COLLATE NOCASE"; final String[] selectionArgs = new String[] { like ? "%" + searchName + "%" : searchName }; Cursor cursor = null; - List<Pair<Integer, String>> result = new ArrayList<>(); + final List<Pair<Integer, String>> result = new ArrayList<>(); try { cursor = getContentResolver().query(uri, projection, selection, selectionArgs, null); while (cursor != null && cursor.moveToNext()) { - int foundId = cursor.getInt(0); - String foundName = cursor.getString(1); + final int foundId = cursor.getInt(0); + final String foundName = cursor.getString(1); result.add(new Pair<>(foundId, foundName)); } - } catch (Exception e) { + } catch (final Exception e) { Log.e(LOG_TAG, "ContactsActivity.getContactId", e); } finally { if (cursor != null) { @@ -137,7 +138,7 @@ public final class ContactsActivity extends Activity { return StringUtils.EMPTY; } return URLDecoder.decode(param, CharEncoding.UTF_8).trim(); - } catch (UnsupportedEncodingException e) { + } catch (final UnsupportedEncodingException e) { Log.e(LOG_TAG, "ContactsActivity.getParameter", e); } return StringUtils.EMPTY; diff --git a/main/.settings/org.eclipse.jdt.core.prefs b/main/.settings/org.eclipse.jdt.core.prefs index 301a4d1..92b431a 100644 --- a/main/.settings/org.eclipse.jdt.core.prefs +++ b/main/.settings/org.eclipse.jdt.core.prefs @@ -50,8 +50,8 @@ org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore diff --git a/main/build.gradle b/main/build.gradle index f4fe016..4d90c4c 100644 --- a/main/build.gradle +++ b/main/build.gradle @@ -27,7 +27,7 @@ gradle connectedCheck //https://github.com/stephanenicolas/Quality-Tools-for-Android def AAVersion = '3.2' -def RXJavaVersion = '1.0.3' +def RXJavaVersion = '1.0.4' def RXAndroidVersion = '0.23.0' def RXJavaAsyncUtilVersion = '0.21.0' def JacksonCoreVersion = '2.4.4' @@ -42,7 +42,7 @@ version = '0.0.1' android { compileSdkVersion "Google Inc.:Google APIs:19" //compileSdkVersion 19 - buildToolsVersion "21.1" + buildToolsVersion "21.1.2" compileOptions { sourceCompatibility JavaVersion.VERSION_1_7 diff --git a/main/cgeo.iml b/main/cgeo.iml index 85c7887..45f86a9 100644 --- a/main/cgeo.iml +++ b/main/cgeo.iml @@ -46,11 +46,8 @@ <root url="file://$MODULE_DIR$/compile-libs" /> </CLASSES> <JAVADOC /> - <SOURCES> - <root url="file://$MODULE_DIR$/compile-libs" /> - </SOURCES> + <SOURCES /> <jarDirectory url="file://$MODULE_DIR$/compile-libs" recursive="false" /> - <jarDirectory url="file://$MODULE_DIR$/compile-libs" recursive="false" type="SOURCES" /> </library> </orderEntry> <orderEntry type="module-library" exported=""> diff --git a/main/libs/androidannotations-api-3.2.jar.properties b/main/libs/androidannotations-api-3.2.jar.properties index ff6ac8c..6e05cd2 100644 --- a/main/libs/androidannotations-api-3.2.jar.properties +++ b/main/libs/androidannotations-api-3.2.jar.properties @@ -1 +1,2 @@ -src=src/androidannotations-api-3.2-sources.jar
\ No newline at end of file +src=src/androidannotations-api-3.2-sources.jar +doc=src/androidannotations-api-3.2-javadoc.jar
\ No newline at end of file diff --git a/main/libs/findbugs-annotations.jar b/main/libs/annotations-3.0.0.jar Binary files differindex 3641ad6..a2f68fe 100644 --- a/main/libs/findbugs-annotations.jar +++ b/main/libs/annotations-3.0.0.jar diff --git a/main/libs/annotations-3.0.0.jar.properties b/main/libs/annotations-3.0.0.jar.properties new file mode 100644 index 0000000..a32a2ea --- /dev/null +++ b/main/libs/annotations-3.0.0.jar.properties @@ -0,0 +1,2 @@ +src=src/annotations-3.0.0-sources.jar +doc=src/annotations-3.0.0-javadoc.jar diff --git a/main/libs/butterknife-6.0.0.jar.properties b/main/libs/butterknife-6.0.0.jar.properties new file mode 100644 index 0000000..d7282cc --- /dev/null +++ b/main/libs/butterknife-6.0.0.jar.properties @@ -0,0 +1,2 @@ +src=src/butterknife-6.0.0-sources.jar +doc=src/butterknife-6.0.0-javadoc.jar diff --git a/main/libs/rxjava-1.0.3.jar.properties b/main/libs/rxjava-1.0.3.jar.properties deleted file mode 100644 index 9814db6..0000000 --- a/main/libs/rxjava-1.0.3.jar.properties +++ /dev/null @@ -1,2 +0,0 @@ -src=src/rxjava-1.0.3-sources.jar -doc=src/rxjava-1.0.3-javadoc.jar diff --git a/main/libs/rxjava-1.0.3.jar b/main/libs/rxjava-1.0.4.jar Binary files differindex c16ca79..feed10f 100644 --- a/main/libs/rxjava-1.0.3.jar +++ b/main/libs/rxjava-1.0.4.jar diff --git a/main/libs/rxjava-1.0.4.jar.properties b/main/libs/rxjava-1.0.4.jar.properties new file mode 100644 index 0000000..944cfad --- /dev/null +++ b/main/libs/rxjava-1.0.4.jar.properties @@ -0,0 +1,2 @@ +src=src/rxjava-1.0.4-sources.jar +doc=src/rxjava-1.0.4-javadoc.jar diff --git a/main/libs/src/androidannotations-api-3.2-javadoc.jar b/main/libs/src/androidannotations-api-3.2-javadoc.jar Binary files differnew file mode 100644 index 0000000..efdaf1f --- /dev/null +++ b/main/libs/src/androidannotations-api-3.2-javadoc.jar diff --git a/main/libs/src/annotations-3.0.0-javadoc.jar b/main/libs/src/annotations-3.0.0-javadoc.jar Binary files differnew file mode 100644 index 0000000..0098501 --- /dev/null +++ b/main/libs/src/annotations-3.0.0-javadoc.jar diff --git a/main/libs/src/annotations-3.0.0-sources.jar b/main/libs/src/annotations-3.0.0-sources.jar Binary files differnew file mode 100644 index 0000000..0098501 --- /dev/null +++ b/main/libs/src/annotations-3.0.0-sources.jar diff --git a/main/libs/src/butterknife-6.0.0-javadoc.jar b/main/libs/src/butterknife-6.0.0-javadoc.jar Binary files differnew file mode 100644 index 0000000..1aef6df --- /dev/null +++ b/main/libs/src/butterknife-6.0.0-javadoc.jar diff --git a/main/libs/src/butterknife-6.0.0-sources.jar b/main/libs/src/butterknife-6.0.0-sources.jar Binary files differnew file mode 100644 index 0000000..3b29f50 --- /dev/null +++ b/main/libs/src/butterknife-6.0.0-sources.jar diff --git a/main/libs/src/rxjava-1.0.3-javadoc.jar b/main/libs/src/rxjava-1.0.3-javadoc.jar Binary files differdeleted file mode 100644 index d21929d..0000000 --- a/main/libs/src/rxjava-1.0.3-javadoc.jar +++ /dev/null diff --git a/main/libs/src/rxjava-1.0.4-javadoc.jar b/main/libs/src/rxjava-1.0.4-javadoc.jar Binary files differnew file mode 100644 index 0000000..92dc0fc --- /dev/null +++ b/main/libs/src/rxjava-1.0.4-javadoc.jar diff --git a/main/libs/src/rxjava-1.0.3-sources.jar b/main/libs/src/rxjava-1.0.4-sources.jar Binary files differindex e3d74ce..c77e946 100644 --- a/main/libs/src/rxjava-1.0.3-sources.jar +++ b/main/libs/src/rxjava-1.0.4-sources.jar diff --git a/main/project/libraries/.gitignore b/main/project/libraries/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/main/project/libraries/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/main/project/libraries/pom.xml b/main/project/libraries/pom.xml new file mode 100644 index 0000000..a86c3c7 --- /dev/null +++ b/main/project/libraries/pom.xml @@ -0,0 +1,209 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.cgeo</groupId> + <artifactId>library.check</artifactId> + <version>0.0.1-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>c:geo dependency update check</name> + + <description>This project checks or updates c:geo dependencies. + Run 'mvn validate' to see available dependency updates. + Run 'mvn install' to actually update to the latest releases. You may need to refresh your IDE afterwards. + </description> + + <properties> + <AndroidAnnotationsVersion>3.2</AndroidAnnotationsVersion> + + <ButterKnifeVersion>6.0.0</ButterKnifeVersion> + + <CommonsCollections4Version>4.0</CommonsCollections4Version> + <CommonsIoVersion>2.4</CommonsIoVersion> + <CommonsLang3Version>3.3.2</CommonsLang3Version> + + <FindbugsAnnotationsVersion>3.0.0</FindbugsAnnotationsVersion> + + <JacksonAnnotationsVersion>2.4.4</JacksonAnnotationsVersion> + <JacksonCoreVersion>2.4.4</JacksonCoreVersion> + <JacksonDatabindVersion>2.4.4</JacksonDatabindVersion> + + <RXAndroidVersion>0.23.0</RXAndroidVersion> + <RXJavaVersion>1.0.3</RXJavaVersion> + <RXJavaAsyncUtilVersion>0.21.0</RXJavaAsyncUtilVersion> + </properties> + + <dependencies> + <dependency> + <groupId>org.androidannotations</groupId> + <artifactId>androidannotations</artifactId> + <version>${AndroidAnnotationsVersion}</version> + </dependency> + <dependency> + <groupId>org.androidannotations</groupId> + <artifactId>androidannotations-api</artifactId> + <version>${AndroidAnnotationsVersion}</version> + </dependency> + + <dependency> + <groupId>com.jakewharton</groupId> + <artifactId>butterknife</artifactId> + <version>${ButterKnifeVersion}</version> + </dependency> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-collections4</artifactId> + <version>${CommonsCollections4Version}</version> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <version>${CommonsLang3Version}</version> + </dependency> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>${CommonsIoVersion}</version> + </dependency> + + <dependency> + <groupId>com.google.code.findbugs</groupId> + <artifactId>annotations</artifactId> + <version>${FindbugsAnnotationsVersion}</version> + </dependency> + + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-annotations</artifactId> + <version>${JacksonAnnotationsVersion}</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-core</artifactId> + <version>${JacksonCoreVersion}</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + <version>${JacksonDatabindVersion}</version> + </dependency> + + <dependency> + <groupId>io.reactivex</groupId> + <artifactId>rxjava</artifactId> + <version>${RXJavaVersion}</version> + </dependency> + <dependency> + <groupId>io.reactivex</groupId> + <artifactId>rxjava-async-util</artifactId> + <version>${RXJavaAsyncUtilVersion}</version> + </dependency> + <dependency> + <groupId>io.reactivex</groupId> + <artifactId>rxandroid</artifactId> + <version>${RXAndroidVersion}</version> + </dependency> + + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>versions-maven-plugin</artifactId> + <version>2.1</version> + <executions> + <execution> + <id>display-property-updates</id> + <phase>validate</phase> + <goals> + <goal>display-property-updates</goal> + </goals> + </execution> + <execution> + <id>update-properties</id> + <phase>install</phase> + <goals> + <goal>update-properties</goal> + </goals> + <configuration> + <allowSnapshots>false</allowSnapshots> + <allowMajorUpdates>true</allowMajorUpdates> + <allowMinorUpdates>true</allowMinorUpdates> + <generateBackupPoms>true</generateBackupPoms> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <version>2.9</version> + <executions> + <execution> + <id>copy-dependencies</id> + <phase>package</phase> + <goals> + <goal>copy-dependencies</goal> + </goals> + <configuration> + <outputDirectory>${basedir}/../../libs</outputDirectory> + <overWriteReleases>true</overWriteReleases> + <excludeTransitive>true</excludeTransitive> + </configuration> + </execution> + <execution> + <id>src-dependencies</id> + <phase>package</phase> + <goals> + <goal>copy-dependencies</goal> + </goals> + <configuration> + <classifier>sources</classifier> + <outputDirectory>${basedir}/../../libs/src</outputDirectory> + <failOnMissingClassifierArtifact>false</failOnMissingClassifierArtifact> + <overWriteReleases>true</overWriteReleases> + <excludeTransitive>true</excludeTransitive> + </configuration> + </execution> + <execution> + <id>javadoc-dependencies</id> + <phase>package</phase> + <goals> + <goal>copy-dependencies</goal> + </goals> + <configuration> + <classifier>javadoc</classifier> + <outputDirectory>${basedir}/../../libs/src</outputDirectory> + <failOnMissingClassifierArtifact>false</failOnMissingClassifierArtifact> + <overWriteReleases>true</overWriteReleases> + <excludeTransitive>true</excludeTransitive> + </configuration> + </execution> + </executions> + </plugin> + <!-- unfinished: update properties files of source and javadoc + <plugin> + <artifactId>maven-antrun-plugin</artifactId> + <version>1.4</version> + <executions> + <execution> + <id>write-properties-files</id> + <phase>package</phase> + <configuration> + <tasks> + </tasks> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + </executions> + </plugin> + --> + </plugins> + </build> +</project> diff --git a/main/project/libraries/update-libs.sh b/main/project/libraries/update-libs.sh index 460532a..be3483c 100755 --- a/main/project/libraries/update-libs.sh +++ b/main/project/libraries/update-libs.sh @@ -1,7 +1,7 @@ #! /bin/sh # -RXJAVA=1.0.3 +RXJAVA=1.0.4 RXANDROID=0.23.0 RXJAVAASYNCUTIL=0.21.0 JACKSONCORE=2.4.4 diff --git a/main/res/drawable-hdpi/star_half.png b/main/res/drawable-hdpi/star_half.png Binary files differdeleted file mode 100644 index 000208c..0000000 --- a/main/res/drawable-hdpi/star_half.png +++ /dev/null diff --git a/main/res/drawable-ldpi/star_half.png b/main/res/drawable-ldpi/star_half.png Binary files differdeleted file mode 100644 index 97fa464..0000000 --- a/main/res/drawable-ldpi/star_half.png +++ /dev/null diff --git a/main/res/drawable-mdpi/star_half.png b/main/res/drawable-mdpi/star_half.png Binary files differdeleted file mode 100644 index 1ea9152..0000000 --- a/main/res/drawable-mdpi/star_half.png +++ /dev/null diff --git a/main/res/drawable/star_rating.xml b/main/res/drawable/star_rating.xml new file mode 100644 index 0000000..0d8a8e2 --- /dev/null +++ b/main/res/drawable/star_rating.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+android:id/background" + android:drawable="@drawable/star_off" /> + <item android:id="@+android:id/secondaryProgress" + android:drawable="@drawable/star_off" /> + <item android:id="@+android:id/progress" + android:drawable="@drawable/star_on" /> +</layer-list>
\ No newline at end of file diff --git a/main/res/layout/attribute_descriptions.xml b/main/res/layout/attribute_descriptions.xml deleted file mode 100644 index 1e67f72..0000000 --- a/main/res/layout/attribute_descriptions.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/attribute_descriptions" - android:layout_width="fill_parent" - android:layout_height="wrap_content" > - - <TextView - android:id="@+id/attribute_descriptions_textview" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_gravity="left" - android:linksClickable="true" - android:textColor="?text_color" - android:textColorLink="?text_color_link" - android:textIsSelectable="false" - android:textSize="14sp" /> - -</LinearLayout>
\ No newline at end of file diff --git a/main/res/layout/cache_information_item.xml b/main/res/layout/cache_information_item.xml index 1b766e4..d213e56 100644 --- a/main/res/layout/cache_information_item.xml +++ b/main/res/layout/cache_information_item.xml @@ -34,17 +34,17 @@ android:textIsSelectable="false" android:textSize="14sp" /> - <LinearLayout + <RatingBar android:id="@+id/stars" + style="@style/cacheRatingBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" + android:layout_gravity="center_horizontal" android:layout_toRightOf="@+id/value" android:baselineAligned="false" android:gravity="center_vertical" - android:orientation="horizontal" - android:visibility="gone" > - </LinearLayout> + android:visibility="gone" /> <TextView android:id="@+id/addition" diff --git a/main/res/layout/cachedetail_details_page.xml b/main/res/layout/cachedetail_details_page.xml index e8902fd..7b48554 100644 --- a/main/res/layout/cachedetail_details_page.xml +++ b/main/res/layout/cachedetail_details_page.xml @@ -27,23 +27,38 @@ android:id="@+id/attributes_box"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="gone" >
+ android:orientation="vertical" >
<View
style="@style/separator_horizontal"
android:layout_marginBottom="9dp"
android:layout_marginTop="9dp" />
- <!-- innerbox is only needed to define the paddings easily -->
+ <cgeo.geocaching.ui.WrappingGridView
+ android:id="@+id/attributes_grid"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:columnWidth="32dip"
+ android:horizontalSpacing="2dip"
+ android:numColumns="auto_fit"
+ android:paddingLeft="6dp"
+ android:paddingRight="6dp"
+ android:scrollbars="none"
+ android:stretchMode="none"
+ android:verticalSpacing="2dip" />
- <LinearLayout
- android:id="@+id/attributes_innerbox"
+ <TextView
+ android:id="@+id/attributes_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
+ android:layout_gravity="left"
+ android:linksClickable="true"
android:paddingLeft="6dp"
- android:paddingRight="6dp" />
+ android:paddingRight="6dp"
+ android:textColor="?text_color"
+ android:textColorLink="?text_color_link"
+ android:textIsSelectable="false"
+ android:textSize="14sp" />
</LinearLayout>
<!-- Offline box -->
diff --git a/main/res/layout/gcvote_dialog.xml b/main/res/layout/gcvote_dialog.xml new file mode 100644 index 0000000..221a8f5 --- /dev/null +++ b/main/res/layout/gcvote_dialog.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dip" + android:orientation="vertical" > + + <include layout="@layout/gcvote_rating_bar" /> + +</LinearLayout>
\ No newline at end of file diff --git a/main/res/layout/gcvote_rating_bar.xml b/main/res/layout/gcvote_rating_bar.xml new file mode 100644 index 0000000..87d4ef5 --- /dev/null +++ b/main/res/layout/gcvote_rating_bar.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<merge xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" > + + <RatingBar + android:id="@+id/gcvoteRating" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:max="5" + android:numStars="5" + android:stepSize="0.5" + android:visibility="gone" /> + + <TextView + android:id="@+id/gcvoteLabel" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:gravity="center_horizontal" + android:padding="10dip" + android:text="@string/log_no_rating" + android:textColor="?text_color" + android:textSize="12sp" + android:visibility="gone" /> + +</merge> diff --git a/main/res/layout/logcache_activity.xml b/main/res/layout/logcache_activity.xml index de6b37a..2e966d1 100644 --- a/main/res/layout/logcache_activity.xml +++ b/main/res/layout/logcache_activity.xml @@ -74,26 +74,7 @@ tools:ignore="TextFields" /> </LinearLayout> - <RatingBar - android:id="@+id/gcvoteRating" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:max="5" - android:numStars="5" - android:stepSize="0.5" - android:visibility="gone" /> - - <TextView - android:id="@+id/gcvoteLabel" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:gravity="center_horizontal" - android:padding="10dip" - android:text="@string/log_no_rating" - android:textColor="?text_color" - android:textSize="12sp" - android:visibility="gone" /> + <include layout="@layout/gcvote_rating_bar" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <LinearLayout android:id="@+id/tweet_box" diff --git a/main/res/layout/star_image.xml b/main/res/layout/star_image.xml deleted file mode 100644 index 809a17d..0000000 --- a/main/res/layout/star_image.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<ImageView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="12dip" - android:layout_height="12dip" - android:layout_gravity="center" - android:layout_margin="1dip" - android:gravity="center" - android:scaleType="fitXY" - android:src="@drawable/star_off" - tools:context=".ui.CacheDetailsCreator" /> diff --git a/main/res/menu/cache_options.xml b/main/res/menu/cache_options.xml index 8fdb2aa..c6d7322 100644 --- a/main/res/menu/cache_options.xml +++ b/main/res/menu/cache_options.xml @@ -38,6 +38,12 @@ app:showAsAction="ifRoom"> </item> <item + android:id="@+id/menu_gcvote" + android:title="@string/cache_menu_vote" + android:visible="false" + app:showAsAction="ifRoom"> + </item> + <item android:id="@+id/menu_caches_around" android:icon="@drawable/ic_menu_rotate" android:title="@string/cache_menu_around" diff --git a/main/res/values/changelog_master.xml b/main/res/values/changelog_master.xml index 1e3c3d4..bfac8de 100644 --- a/main/res/values/changelog_master.xml +++ b/main/res/values/changelog_master.xml @@ -2,5 +2,10 @@ <resources> <!-- changelog for the master branch --> <string name="changelog_master" translatable="false"> + <b>Next feature release:</b>\n + · New: Waypoints created from personal notes can now be deleted\n + · New: More easy to use vote dialog in cache details\n + · Fix: Up navigation did not work when c:geo was invoked from another app\n + \n </string> </resources> diff --git a/main/res/values/strings.xml b/main/res/values/strings.xml index 4be3ab6..df7c7bd 100644 --- a/main/res/values/strings.xml +++ b/main/res/values/strings.xml @@ -142,6 +142,7 @@ <string name="err_login">No Login information stored</string> <string name="err_login_failed_toast">c:geo can\'t log in. c:geo works offline with Stored caches. Check Login settings or enable your internet connection.</string> <string name="err_unknown">Unknown error</string> + <string name="err_unknown_address">c:geo was unable to map this address to an existing location</string> <string name="err_comm">Unknown communication error</string> <string name="err_missing_auth">No username and/or password set.</string> <string name="err_wrong">Login information incorrect</string> @@ -176,7 +177,6 @@ <string name="err_acquire_image_failed">Acquiring an image failed.</string> <string name="err_tb_display">c:geo can\'t display the trackable you want. Is it really a trackable?</string> <string name="err_tb_details_open">c:geo can\'t open trackable details.</string> - <string name="err_tb_forgot_saw">c:geo forgot which trackable you saw.</string> <string name="err_tb_find">c:geo can\'t find trackable</string> <string name="err_tb_find_that">c:geo can\'t find that trackable.</string> <string name="err_waypoint_cache_unknown">c:geo doesn\'t know to which cache you want to add a waypoint for.</string> @@ -324,6 +324,9 @@ <string name="caches_filter_personal_note">With personal note</string> <string name="caches_filter_popularity">Favorites</string> <string name="caches_filter_popularity_ratio">Favorites [%]</string> + <string name="caches_filter_personal_data">With personal data</string> + <string name="caches_filter_rating">With rating</string> + <string name="caches_filter_own_rating">With own rating</string> <string name="caches_removing_from_history">Removing from History…</string> <string name="caches_clear_offlinelogs">Clear offline logs</string> <string name="caches_clear_offlinelogs_message">Do you want to clear the offline logs?</string> @@ -362,6 +365,7 @@ <!-- settings --> <string name="settings_title_services">Services</string> + <string name="settings_summary_services">Configure user account information and access to optional services.</string> <string name="settings_title_appearance">Appearance</string> <string name="settings_title_cachedetails">Cache Details</string> <string name="settings_title_offlinedata">Offline Data</string> @@ -414,6 +418,7 @@ <string name="init_gcvote">GCvote.com</string> <string name="init_gcvote_password_description">To be able to rate a cache, you need to follow the instructions at GCVote.com and enter your GCVote password here.</string> <string name="err_gcvote_send_rating">Error while sending rating, check GCVote password in settings or empty it.</string> + <string name="gcvote_sent">Voting successfully sent</string> <string name="init_twitter">Twitter</string> <string name="settings_activate_twitter">Activate</string> <string name="init_username">Username</string> @@ -711,6 +716,7 @@ <string name="cache_menu_navigon">Navigon</string> <string name="cache_menu_pebble">Pebble</string> <string name="cache_menu_android_wear">Android Wear</string> + <string name="cache_menu_vote">Vote</string> <string name="cache_status">Status</string> <string name="cache_status_offline_log">Saved Log</string> <string name="cache_status_found">Found</string> diff --git a/main/res/values/strings_not_translatable.xml b/main/res/values/strings_not_translatable.xml index 585500f..35fef08 100644 --- a/main/res/values/strings_not_translatable.xml +++ b/main/res/values/strings_not_translatable.xml @@ -81,6 +81,8 @@ · <a href="http://rrze-icon-set.berlios.de/index.html">RRZE Icon set</a> (<a href="http://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA 3.0</a>)\n · <a href="http://iconfindr.com/1mNr3rl">Layers icon by Cole Bemis</a> (<a href="http://creativecommons.org/licenses/by/3.0/">CC-BY 3.0</a>)\n · <a href="https://github.com/amlcurran/Showcaseview">ShowcaseView by Alex Curran</a> (<a href="http://www.apache.org/licenses/LICENSE-2.0.html">Apache License 2.0</a>)\n + · <a href="http://www.mapquest.com/">Geocoding courtesy of MapQuest</a>\n + · <a href="http://www.openstreetmap.org/">Geocoding data from OpenStreetMap</a>\n </string> <!-- cache menu --> diff --git a/main/res/values/styles.xml b/main/res/values/styles.xml index be1e1a4..5f280f2 100644 --- a/main/res/values/styles.xml +++ b/main/res/values/styles.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<resources> +<resources xmlns:android="http://schemas.android.com/apk/res/android"> <!-- system definitions --> @@ -338,4 +338,13 @@ <item name="android:src">@drawable/mark_green</item> </style> + <style name="cacheRatingBar" parent="@android:style/Widget.RatingBar"> + <item name="android:progressDrawable">@drawable/star_rating</item> + <item name="android:minHeight">12dip</item> + <item name="android:maxHeight">22dip</item> + <item name="android:max">5</item> + <item name="android:stepSize">0.5</item> + <item name="android:isIndicator">true</item> + </style> + </resources>
\ No newline at end of file diff --git a/main/res/xml/preferences.xml b/main/res/xml/preferences.xml index 9648462..48d8a21 100644 --- a/main/res/xml/preferences.xml +++ b/main/res/xml/preferences.xml @@ -6,7 +6,8 @@ <PreferenceScreen android:icon="?attr/settings_cloud" android:key="@string/preference_screen_services" - android:title="@string/settings_title_services" > + android:title="@string/settings_title_services" + android:summary="@string/settings_summary_services"> <PreferenceCategory android:title="@string/settings_category_geocaching" > <PreferenceScreen android:key="@string/preference_screen_gc" diff --git a/main/src/cgeo/geocaching/AddressListActivity.java b/main/src/cgeo/geocaching/AddressListActivity.java index 4f71ab6..4f81f8b 100644 --- a/main/src/cgeo/geocaching/AddressListActivity.java +++ b/main/src/cgeo/geocaching/AddressListActivity.java @@ -1,14 +1,17 @@ package cgeo.geocaching; import cgeo.geocaching.activity.AbstractListActivity; -import cgeo.geocaching.location.Geocoder; +import cgeo.geocaching.location.AndroidGeocoder; +import cgeo.geocaching.location.GCGeocoder; +import cgeo.geocaching.location.MapQuestGeocoder; import cgeo.geocaching.ui.AddressListAdapter; -import org.apache.commons.collections4.CollectionUtils; +import rx.Observable; +import rx.android.observables.AndroidObservable; +import rx.functions.Action1; import android.app.ProgressDialog; import android.location.Address; -import android.os.AsyncTask; import android.os.Bundle; import java.util.List; @@ -19,47 +22,35 @@ public class AddressListActivity extends AbstractListActivity { public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState, R.layout.addresslist_activity); - // get parameters - final String keyword = getIntent().getStringExtra(Intents.EXTRA_KEYWORD); - - if (keyword == null) { - showToast(res.getString(R.string.err_search_address_forgot)); - finish(); - return; - } final AddressListAdapter adapter = new AddressListAdapter(this); setListAdapter(adapter); + final String keyword = getIntent().getStringExtra(Intents.EXTRA_KEYWORD); final ProgressDialog waitDialog = ProgressDialog.show(this, res.getString(R.string.search_address_started), keyword, true); waitDialog.setCancelable(true); - lookupAddressInBackground(keyword, adapter, waitDialog); } private void lookupAddressInBackground(final String keyword, final AddressListAdapter adapter, final ProgressDialog waitDialog) { - new AsyncTask<Void, Void, List<Address>>() { - + final Observable<Address> geocoderObservable = new AndroidGeocoder(this).getFromLocationName(keyword) + .onErrorResumeNext(MapQuestGeocoder.getFromLocationName(keyword)) + .onErrorResumeNext(GCGeocoder.getFromLocationName(keyword)); + AndroidObservable.bindActivity(this, geocoderObservable.toList()).subscribe(new Action1<List<Address>>() { @Override - protected List<Address> doInBackground(final Void... params) { - final Geocoder geocoder = new Geocoder(AddressListActivity.this); - return geocoder.getFromLocationName(keyword); - } - - @Override - protected void onPostExecute(final List<Address> addresses) { + public void call(final List<Address> addresses) { waitDialog.dismiss(); - if (CollectionUtils.isNotEmpty(addresses)) { - for (final Address address : addresses) { - adapter.add(address); // don't use addAll, it's only available with API >= 11 - } - } else { - finish(); - CacheListActivity.startActivityAddress(AddressListActivity.this, null, keyword); + for (final Address address : addresses) { + adapter.add(address); // don't use addAll, it's only available with API >= 11 } } - - }.execute(); + }, new Action1<Throwable>() { + @Override + public void call(final Throwable throwable) { + finish(); + showToast(res.getString(R.string.err_unknown_address)); + } + }); } }
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/AttributesGridAdapter.java b/main/src/cgeo/geocaching/AttributesGridAdapter.java new file mode 100644 index 0000000..fd81339 --- /dev/null +++ b/main/src/cgeo/geocaching/AttributesGridAdapter.java @@ -0,0 +1,79 @@ +package cgeo.geocaching; + +import cgeo.geocaching.enumerations.CacheAttribute; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import java.util.List; + +public class AttributesGridAdapter extends BaseAdapter { + private final Context context; + private final Resources resources; + private final List<String> attributes; + private final LayoutInflater inflater; + + public AttributesGridAdapter(final Context context, final Geocache cache) { + this.context = context; + resources = context.getResources(); + attributes = cache.getAttributes(); + inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + @Override + public int getCount() { + return attributes.size(); + } + + @Override + public Object getItem(final int position) { + return attributes.get(position); + } + + @Override + public long getItemId(final int position) { + return 0; + } + + @Override + public View getView(final int position, final View convertView, final ViewGroup parent) { + final FrameLayout attributeLayout; + if (convertView == null) { + attributeLayout = (FrameLayout) inflater.inflate(R.layout.attribute_image, parent, false); + } else { + attributeLayout = (FrameLayout) convertView; + } + + drawAttribute(attributeLayout, attributes.get(position)); + return attributeLayout; + } + + private void drawAttribute(final FrameLayout attributeLayout, final String attributeName) { + final ImageView imageView = (ImageView) attributeLayout.getChildAt(0); + + final boolean strikeThrough = !CacheAttribute.isEnabled(attributeName); + final CacheAttribute attrib = CacheAttribute.getByRawName(CacheAttribute.trimAttributeName(attributeName)); + if (attrib != null) { + Drawable drawable = resources.getDrawable(attrib.drawableId); + imageView.setImageDrawable(drawable); + if (strikeThrough) { + // generate strike through image with same properties as attribute image + final ImageView strikeThroughImage = new ImageView(context); + strikeThroughImage.setLayoutParams(imageView.getLayoutParams()); + drawable = resources.getDrawable(R.drawable.attribute__strikethru); + strikeThroughImage.setImageDrawable(drawable); + attributeLayout.addView(strikeThroughImage); + } + } else { + imageView.setImageDrawable(resources.getDrawable(R.drawable.attribute_unknown)); + } + } + +}
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/CacheDetailActivity.java b/main/src/cgeo/geocaching/CacheDetailActivity.java index e74b2d2..ef3823e 100644 --- a/main/src/cgeo/geocaching/CacheDetailActivity.java +++ b/main/src/cgeo/geocaching/CacheDetailActivity.java @@ -12,7 +12,6 @@ import cgeo.geocaching.activity.Progress; import cgeo.geocaching.apps.cache.navi.NavigationAppFactory; import cgeo.geocaching.apps.cache.navi.NavigationSelectionActionProvider; import cgeo.geocaching.apps.cachelist.MapsWithMeCacheListApp; -import cgeo.geocaching.compatibility.Compatibility; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.IConnector; import cgeo.geocaching.connector.gc.GCConnector; @@ -21,6 +20,8 @@ import cgeo.geocaching.enumerations.CacheAttribute; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.enumerations.LoadFlags.SaveFlag; import cgeo.geocaching.enumerations.WaypointType; +import cgeo.geocaching.gcvote.GCVote; +import cgeo.geocaching.gcvote.GCVoteDialog; import cgeo.geocaching.list.StoredList; import cgeo.geocaching.location.Units; import cgeo.geocaching.network.HtmlImage; @@ -105,13 +106,11 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; -import android.view.ViewParent; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.Button; -import android.widget.FrameLayout; +import android.widget.GridView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; @@ -505,6 +504,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc menu.findItem(R.id.menu_store).setVisible(cache != null && !cache.isOffline()); menu.findItem(R.id.menu_delete).setVisible(cache != null && cache.isOffline()); menu.findItem(R.id.menu_refresh).setVisible(cache != null && cache.isOffline()); + menu.findItem(R.id.menu_gcvote).setVisible(cache != null && GCVote.isVotingPossible(cache)); return super.onPrepareOptionsMenu(menu); } @@ -526,6 +526,9 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc case R.id.menu_refresh: refreshCache(); return true; + case R.id.menu_gcvote: + showVoteDialog(); + return true; default: if (NavigationAppFactory.onMenuItemSelected(item, this, cache)) { return true; @@ -539,6 +542,15 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc return super.onOptionsItemSelected(item); } + private void showVoteDialog() { + GCVoteDialog.show(this, cache, new Runnable() { + @Override + public void run() { + notifyDataSetChanged(); + } + }); + } + private static final class CacheDetailsGeoDirHandler extends GeoDirHandler { private final WeakReference<CacheDetailActivity> activityRef; @@ -617,7 +629,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } private void notifyDataSetChanged() { - // This might get called asynchronically when the activity is shut down + // This might get called asynchronous when the activity is shut down if (isFinishing()) { return; } @@ -722,185 +734,6 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } } - private class AttributeViewBuilder { - private ViewGroup attributeIconsLayout; // layout for attribute icons - private ViewGroup attributeDescriptionsLayout; // layout for attribute descriptions - private boolean attributesShowAsIcons = true; // default: show icons - /** - * If the cache is from a non GC source, it might be without icons. Disable switching in those cases. - */ - private boolean noAttributeIconsFound = false; - private int attributeBoxMaxWidth; - - public void fillView(final LinearLayout attributeBox) { - // first ensure that the view is empty - attributeBox.removeAllViews(); - - // maximum width for attribute icons is screen width - paddings of parents - attributeBoxMaxWidth = Compatibility.getDisplayWidth(); - ViewParent child = attributeBox; - do { - if (child instanceof View) { - attributeBoxMaxWidth -= ((View) child).getPaddingLeft() + ((View) child).getPaddingRight(); - } - child = child.getParent(); - } while (child != null); - - // delete views holding description / icons - attributeDescriptionsLayout = null; - attributeIconsLayout = null; - - attributeBox.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(final View v) { - // toggle between attribute icons and descriptions - toggleAttributeDisplay(attributeBox, attributeBoxMaxWidth); - } - }); - - // icons or text? - // - // also show icons when noAttributeImagesFound == true. Explanation: - // 1. no icons could be found in the first invocation of this method - // 2. user refreshes cache from web - // 3. now this method is called again - // 4. attributeShowAsIcons is false but noAttributeImagesFound is true - // => try to show them now - if (attributesShowAsIcons || noAttributeIconsFound) { - showAttributeIcons(attributeBox, attributeBoxMaxWidth); - } else { - showAttributeDescriptions(attributeBox); - } - } - - /** - * lazy-creates the layout holding the icons of the caches attributes - * and makes it visible - */ - private void showAttributeIcons(final LinearLayout attribBox, final int parentWidth) { - if (attributeIconsLayout == null) { - attributeIconsLayout = createAttributeIconsLayout(parentWidth); - // no matching icons found? show text - if (noAttributeIconsFound) { - showAttributeDescriptions(attribBox); - return; - } - } - attribBox.removeAllViews(); - attribBox.addView(attributeIconsLayout); - attributesShowAsIcons = true; - } - - /** - * lazy-creates the layout holding the descriptions of the caches attributes - * and makes it visible - */ - private void showAttributeDescriptions(final LinearLayout attribBox) { - if (attributeDescriptionsLayout == null) { - attributeDescriptionsLayout = createAttributeDescriptionsLayout(attribBox); - } - attribBox.removeAllViews(); - attribBox.addView(attributeDescriptionsLayout); - attributesShowAsIcons = false; - } - - /** - * toggle attribute descriptions and icons - */ - private void toggleAttributeDisplay(final LinearLayout attribBox, final int parentWidth) { - // Don't toggle when there are no icons to show. - if (noAttributeIconsFound) { - return; - } - - // toggle - if (attributesShowAsIcons) { - showAttributeDescriptions(attribBox); - } else { - showAttributeIcons(attribBox, parentWidth); - } - } - - private ViewGroup createAttributeIconsLayout(final int parentWidth) { - final LinearLayout rows = new LinearLayout(CacheDetailActivity.this); - rows.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); - rows.setOrientation(LinearLayout.VERTICAL); - - LinearLayout attributeRow = newAttributeIconsRow(); - rows.addView(attributeRow); - - noAttributeIconsFound = true; - - for (final String attributeName : cache.getAttributes()) { - // check if another attribute icon fits in this row - attributeRow.measure(0, 0); - final int rowWidth = attributeRow.getMeasuredWidth(); - final FrameLayout fl = (FrameLayout) getLayoutInflater().inflate(R.layout.attribute_image, attributeRow, false); - final ImageView iv = (ImageView) fl.getChildAt(0); - if ((parentWidth - rowWidth) < iv.getLayoutParams().width) { - // make a new row - attributeRow = newAttributeIconsRow(); - rows.addView(attributeRow); - } - - final boolean strikeThrough = !CacheAttribute.isEnabled(attributeName); - final CacheAttribute attrib = CacheAttribute.getByRawName(CacheAttribute.trimAttributeName(attributeName)); - if (attrib != null) { - noAttributeIconsFound = false; - Drawable drawable = res.getDrawable(attrib.drawableId); - iv.setImageDrawable(drawable); - // strike through? - if (strikeThrough) { - // generate strike through image with same properties as attribute image - final ImageView strikeThroughImage = new ImageView(CacheDetailActivity.this); - strikeThroughImage.setLayoutParams(iv.getLayoutParams()); - drawable = res.getDrawable(R.drawable.attribute__strikethru); - strikeThroughImage.setImageDrawable(drawable); - fl.addView(strikeThroughImage); - } - } else { - iv.setImageDrawable(res.getDrawable(R.drawable.attribute_unknown)); - } - - attributeRow.addView(fl); - } - - return rows; - } - - private LinearLayout newAttributeIconsRow() { - final LinearLayout rowLayout = new LinearLayout(CacheDetailActivity.this); - rowLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, - LayoutParams.WRAP_CONTENT)); - rowLayout.setOrientation(LinearLayout.HORIZONTAL); - return rowLayout; - } - - private ViewGroup createAttributeDescriptionsLayout(final LinearLayout parentView) { - final LinearLayout descriptions = (LinearLayout) getLayoutInflater().inflate( - R.layout.attribute_descriptions, parentView, false); - final TextView attribView = (TextView) descriptions.getChildAt(0); - - final StringBuilder buffer = new StringBuilder(); - for (String attributeName : cache.getAttributes()) { - final boolean enabled = CacheAttribute.isEnabled(attributeName); - // search for a translation of the attribute - final CacheAttribute attrib = CacheAttribute.getByRawName(CacheAttribute.trimAttributeName(attributeName)); - if (attrib != null) { - attributeName = attrib.getL10n(enabled); - } - if (buffer.length() > 0) { - buffer.append('\n'); - } - buffer.append(attributeName); - } - - attribView.setText(buffer); - - return descriptions; - } - } - private void refreshCache() { if (progress.isShowing()) { showToast(res.getString(R.string.err_detail_still_working)); @@ -916,7 +749,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc progress.show(this, res.getString(R.string.cache_dialog_refresh_title), res.getString(R.string.cache_dialog_refresh_message), true, refreshCacheHandler.cancelMessage()); - cache.refresh(refreshCacheHandler, RxUtils.networkScheduler); + cache.refresh(refreshCacheHandler, RxUtils.refreshScheduler); } private void dropCache() { @@ -1043,11 +876,9 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } // cache attributes - if (!cache.getAttributes().isEmpty()) { - final LinearLayout innerLayout = ButterKnife.findById(view, R.id.attributes_innerbox); - new AttributeViewBuilder().fillView(innerLayout); - view.findViewById(R.id.attributes_box).setVisibility(View.VISIBLE); - } + updateAttributesText(); + updateAttributesIcons(); + ButterKnife.findById(view, R.id.attributes_box).setVisibility(cache.getAttributes().isEmpty() ? View.GONE : View.VISIBLE); updateOfflineBox(view, cache, res, new RefreshCacheClickListener(), new DropCacheClickListener(), new StoreCacheClickListener()); @@ -1086,6 +917,61 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc return view; } + private void updateAttributesIcons() { + final GridView gridView = ButterKnife.findById(view, R.id.attributes_grid); + final List<String> attributes = cache.getAttributes(); + if (attributes.isEmpty()) { + gridView.setVisibility(View.GONE); + return; + } + gridView.setAdapter(new AttributesGridAdapter(CacheDetailActivity.this, cache)); + gridView.setVisibility(View.VISIBLE); + gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(final android.widget.AdapterView<?> parent, final View view, final int position, final long id) { + toggleAttributesView(); + } + }); + } + + protected void toggleAttributesView() { + final View textView = ButterKnife.findById(view, R.id.attributes_text); + textView.setVisibility(textView.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE); + final View gridView = ButterKnife.findById(view, R.id.attributes_grid); + gridView.setVisibility(gridView.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE); + } + + private void updateAttributesText() { + final TextView attribView = ButterKnife.findById(view, R.id.attributes_text); + final List<String> attributes = cache.getAttributes(); + if (attributes.isEmpty()) { + attribView.setVisibility(View.GONE); + return; + } + final StringBuilder text = new StringBuilder(); + for (String attributeName : attributes) { + final boolean enabled = CacheAttribute.isEnabled(attributeName); + // search for a translation of the attribute + final CacheAttribute attrib = CacheAttribute.getByRawName(CacheAttribute.trimAttributeName(attributeName)); + if (attrib != null) { + attributeName = attrib.getL10n(enabled); + } + if (text.length() > 0) { + text.append('\n'); + } + text.append(attributeName); + } + attribView.setText(text); + attribView.setVisibility(View.GONE); + attribView.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(final View v) { + toggleAttributesView(); + } + }); + } + private class StoreCacheClickListener implements View.OnClickListener { @Override public void onClick(final View arg0) { @@ -1304,11 +1190,10 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc buttonRemove.setEnabled(false); buttonRemove.setVisibility(View.GONE); } - } /** - * Show/hide buttons, set text in favourite line and box + * Show/hide buttons, set text in favorite line and box */ private void updateFavPointBox() { // Favorite counts @@ -1361,12 +1246,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc // update text final TextView text = ButterKnife.findById(view, R.id.list_text); final StoredList list = DataStore.getList(cache.getListId()); - if (list != null) { - text.setText(res.getString(R.string.cache_list_text) + " " + list.title); - } else { - // this should not happen - text.setText(R.string.cache_list_unknown); - } + text.setText(res.getString(R.string.cache_list_text) + " " + list.title); } else { // hide box box.setVisibility(View.GONE); diff --git a/main/src/cgeo/geocaching/CacheListActivity.java b/main/src/cgeo/geocaching/CacheListActivity.java index c8f1101..4b84d08 100644 --- a/main/src/cgeo/geocaching/CacheListActivity.java +++ b/main/src/cgeo/geocaching/CacheListActivity.java @@ -27,7 +27,6 @@ import cgeo.geocaching.list.PseudoList; import cgeo.geocaching.list.StoredList; import cgeo.geocaching.loaders.AbstractSearchLoader; import cgeo.geocaching.loaders.AbstractSearchLoader.CacheListLoaderType; -import cgeo.geocaching.loaders.AddressGeocacheListLoader; import cgeo.geocaching.loaders.CoordsGeocacheListLoader; import cgeo.geocaching.loaders.FinderGeocacheListLoader; import cgeo.geocaching.loaders.HistoryGeocacheListLoader; @@ -44,6 +43,7 @@ import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Send2CgeoDownloader; import cgeo.geocaching.sensors.GeoData; import cgeo.geocaching.sensors.GeoDirHandler; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.settings.SettingsActivity; import cgeo.geocaching.sorting.CacheComparator; @@ -296,21 +296,18 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA progress.setMessage(res.getString(R.string.caches_downloading) + " " + res.getQuantityString(R.plurals.caches_eta_mins, minutesRemaining, minutesRemaining)); } } else { - new AsyncTask<Void, Void, Void>() { + new AsyncTask<Void, Void, Set<Geocache>>() { @Override - protected Void doInBackground(final Void... params) { - if (search != null) { - final Set<Geocache> cacheListTmp = search.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB); - if (CollectionUtils.isNotEmpty(cacheListTmp)) { - cacheList.clear(); - cacheList.addAll(cacheListTmp); - } - } - return null; + protected Set<Geocache> doInBackground(final Void... params) { + return search != null ? search.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB) : null; } @Override - protected void onPostExecute(final Void result) { + protected void onPostExecute(final Set<Geocache> result) { + if (CollectionUtils.isNotEmpty(result)) { + cacheList.clear(); + cacheList.addAll(result); + } setAdapterCurrentCoordinates(false); showProgress(false); @@ -412,7 +409,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA } } if (type == CacheListType.NEAREST) { - coords = CgeoApplication.getInstance().currentGeo().getCoords(); + coords = Sensors.getInstance().currentGeo().getCoords(); } setTitle(title); @@ -542,7 +539,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA } private void setAdapterCurrentCoordinates(final boolean forceSort) { - adapter.setActualCoordinates(app.currentGeo().getCoords()); + adapter.setActualCoordinates(Sensors.getInstance().currentGeo().getCoords()); if (forceSort) { adapter.forceSort(); } @@ -1295,9 +1292,6 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA title = res.getString(R.string.list_all_lists); } else { final StoredList list = DataStore.getList(id); - if (list == null) { - return; - } listId = list.id; title = list.title; } @@ -1549,12 +1543,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA } else { title = coords.toString(); } - if (coords != null) { - loader = new CoordsGeocacheListLoader(app, coords); - } - else { - loader = new AddressGeocacheListLoader(app, address); - } + loader = new CoordsGeocacheListLoader(app, coords); break; case FINDER: final String username = extras.getString(Intents.EXTRA_USERNAME); diff --git a/main/src/cgeo/geocaching/CacheMenuHandler.java b/main/src/cgeo/geocaching/CacheMenuHandler.java index 82eb061..fa2f994 100644 --- a/main/src/cgeo/geocaching/CacheMenuHandler.java +++ b/main/src/cgeo/geocaching/CacheMenuHandler.java @@ -90,7 +90,6 @@ public final class CacheMenuHandler extends AbstractUIFactory { menu.findItem(R.id.menu_navigate).setVisible(hasCoords); menu.findItem(R.id.menu_caches_around).setVisible(hasCoords && cache.supportsCachesAround()); menu.findItem(R.id.menu_calendar).setVisible(cache.canBeAddedToCalendar()); - menu.findItem(R.id.menu_show_in_browser).setVisible(cache.canOpenInBrowser()); menu.findItem(R.id.menu_log_visit).setVisible(cache.supportsLogging() && !Settings.getLogOffline()); menu.findItem(R.id.menu_log_visit_offline).setVisible(cache.supportsLogging() && Settings.getLogOffline()); diff --git a/main/src/cgeo/geocaching/CgeoApplication.java b/main/src/cgeo/geocaching/CgeoApplication.java index eea8154..2217e11 100644 --- a/main/src/cgeo/geocaching/CgeoApplication.java +++ b/main/src/cgeo/geocaching/CgeoApplication.java @@ -1,12 +1,6 @@ package cgeo.geocaching; -import cgeo.geocaching.playservices.LocationProvider; -import cgeo.geocaching.sensors.GeoData; -import cgeo.geocaching.sensors.GeoDataProvider; -import cgeo.geocaching.sensors.GpsStatusProvider; -import cgeo.geocaching.sensors.GpsStatusProvider.Status; -import cgeo.geocaching.sensors.OrientationProvider; -import cgeo.geocaching.sensors.RotationProvider; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.OOMDumpingUncaughtExceptionHandler; @@ -17,10 +11,6 @@ import com.google.android.gms.common.GooglePlayServicesUtil; import org.eclipse.jdt.annotation.NonNull; -import rx.Observable; -import rx.functions.Action1; -import rx.functions.Func1; - import android.app.Application; import android.view.ViewConfiguration; @@ -32,21 +22,7 @@ public class CgeoApplication extends Application { public boolean showLoginToast = true; //login toast shown just once. private boolean liveMapHintShownInThisSession = false; // livemap hint has been shown private static CgeoApplication instance; - private Observable<GeoData> geoDataObservable; - private Observable<GeoData> geoDataObservableLowPower; - private Observable<Float> directionObservable; - private Observable<Status> gpsStatusObservable; - @NonNull private volatile GeoData currentGeo = GeoData.DUMMY_LOCATION; - private volatile boolean hasValidLocation = false; - private volatile float currentDirection = 0.0f; private boolean isGooglePlayServicesAvailable = false; - private final Action1<GeoData> rememberGeodataAction = new Action1<GeoData>() { - @Override - public void call(final GeoData geoData) { - currentGeo = geoData; - hasValidLocation = true; - } - }; public static void dumpOnOutOfMemory(final boolean enable) { @@ -66,7 +42,7 @@ public class CgeoApplication extends Application { setInstance(this); } - private static void setInstance(final CgeoApplication application) { + private static void setInstance(@NonNull final CgeoApplication application) { instance = application; } @@ -95,47 +71,14 @@ public class CgeoApplication extends Application { isGooglePlayServicesAvailable = true; } Log.i("Google Play services are " + (isGooglePlayServicesAvailable ? "" : "not ") + "available"); - setupGeoDataObservables(Settings.useGooglePlayServices(), Settings.useLowPowerMode()); - setupDirectionObservable(Settings.useLowPowerMode()); - gpsStatusObservable = GpsStatusProvider.create(this).replay(1).refCount(); + final Sensors sensors = Sensors.getInstance(); + sensors.setupGeoDataObservables(Settings.useGooglePlayServices(), Settings.useLowPowerMode()); + sensors.setupDirectionObservable(Settings.useLowPowerMode()); // Attempt to acquire an initial location before any real activity happens. - geoDataObservableLowPower.subscribeOn(RxUtils.looperCallbacksScheduler).first().subscribe(); - } - - public void setupGeoDataObservables(final boolean useGooglePlayServices, final boolean useLowPowerLocation) { - if (useGooglePlayServices) { - geoDataObservable = LocationProvider.getMostPrecise(this).doOnNext(rememberGeodataAction); - if (useLowPowerLocation) { - geoDataObservableLowPower = LocationProvider.getLowPower(this).doOnNext(rememberGeodataAction); - } else { - geoDataObservableLowPower = geoDataObservable; - } - } else { - geoDataObservable = RxUtils.rememberLast(GeoDataProvider.create(this).doOnNext(rememberGeodataAction)); - geoDataObservableLowPower = geoDataObservable; - } + sensors.geoDataObservable(true).subscribeOn(RxUtils.looperCallbacksScheduler).first().subscribe(); } - public void setupDirectionObservable(final boolean useLowPower) { - directionObservable = RxUtils.rememberLast(RotationProvider.create(this, useLowPower).onErrorResumeNext(new Func1<Throwable, Observable<? extends Float>>() { - @Override - public Observable<? extends Float> call(final Throwable throwable) { - return OrientationProvider.create(CgeoApplication.this); - } - }).onErrorResumeNext(new Func1<Throwable, Observable<? extends Float>>() { - @Override - public Observable<? extends Float> call(final Throwable throwable) { - Log.e("Device orientation will not be available as no suitable sensors were found"); - return Observable.<Float>never().startWith(0.0f); - } - }).doOnNext(new Action1<Float>() { - @Override - public void call(final Float direction) { - currentDirection = direction; - } - })); - } @Override public void onLowMemory() { @@ -143,34 +86,6 @@ public class CgeoApplication extends Application { DataStore.removeAllFromCache(); } - public Observable<GeoData> geoDataObservable(final boolean lowPower) { - return lowPower ? geoDataObservableLowPower : geoDataObservable; - } - - public Observable<Float> directionObservable() { - return directionObservable; - } - - public Observable<Status> gpsStatusObservable() { - if (gpsStatusObservable == null) { - gpsStatusObservable = GpsStatusProvider.create(this).share(); - } - return gpsStatusObservable; - } - - @NonNull - public GeoData currentGeo() { - return currentGeo; - } - - public boolean hasValidLocation() { - return hasValidLocation; - } - - public float currentDirection() { - return currentDirection; - } - public boolean isLiveMapHintShownInThisSession() { return liveMapHintShownInThisSession; } diff --git a/main/src/cgeo/geocaching/CompassActivity.java b/main/src/cgeo/geocaching/CompassActivity.java index 1a1bad2..8b0fc73 100644 --- a/main/src/cgeo/geocaching/CompassActivity.java +++ b/main/src/cgeo/geocaching/CompassActivity.java @@ -11,6 +11,7 @@ import cgeo.geocaching.maps.CGeoMap; import cgeo.geocaching.sensors.GeoData; import cgeo.geocaching.sensors.GeoDirHandler; import cgeo.geocaching.sensors.GpsStatusProvider.Status; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.speech.SpeechService; import cgeo.geocaching.ui.CompassView; @@ -29,8 +30,6 @@ import rx.functions.Action1; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; -import android.hardware.Sensor; -import android.hardware.SensorManager; import android.media.AudioManager; import android.os.Bundle; import android.view.Menu; @@ -59,7 +58,6 @@ public class CompassActivity extends AbstractActionBarActivity { private Geocache cache = null; private Geopoint dstCoords = null; private float cacheHeading = 0; - private boolean hasMagneticFieldSensor; private String description; @Override @@ -67,12 +65,6 @@ public class CompassActivity extends AbstractActionBarActivity { super.onCreate(savedInstanceState, R.layout.compass_activity); ButterKnife.inject(this); - final SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); - hasMagneticFieldSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null; - if (!hasMagneticFieldSensor) { - Settings.setUseCompass(false); - } - // get parameters final Bundle extras = getIntent().getExtras(); if (extras == null) { @@ -89,12 +81,13 @@ public class CompassActivity extends AbstractActionBarActivity { // find the wanted navigation target if (extras.containsKey(Intents.EXTRA_WAYPOINT_ID)) { final int waypointId = extras.getInt(Intents.EXTRA_WAYPOINT_ID); - setTarget(DataStore.loadWaypoint(waypointId)); + final Waypoint waypoint = DataStore.loadWaypoint(waypointId); + if (waypoint != null) { + setTarget(waypoint); + } } else if (extras.containsKey(Intents.EXTRA_COORDS)) { - final Geopoint coords = extras.getParcelable(Intents.EXTRA_COORDS); - final String description = extras.getString(Intents.EXTRA_DESCRIPTION); - setTarget(coords, description); + setTarget(extras.<Geopoint>getParcelable(Intents.EXTRA_COORDS), extras.getString(Intents.EXTRA_DESCRIPTION)); } else { setTarget(cache); @@ -115,7 +108,7 @@ public class CompassActivity extends AbstractActionBarActivity { @Override public void onResume() { super.onResume(geoDirHandler.start(GeoDirHandler.UPDATE_GEODIR), - app.gpsStatusObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(gpsStatusHandler)); + Sensors.getInstance().gpsStatusObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(gpsStatusHandler)); forceRefresh(); } @@ -139,15 +132,14 @@ public class CompassActivity extends AbstractActionBarActivity { private void forceRefresh() { // Force a refresh of location and direction when data is available. - final CgeoApplication app = CgeoApplication.getInstance(); - final GeoData geo = app.currentGeo(); - geoDirHandler.updateGeoDir(geo, app.currentDirection()); + final Sensors sensors = Sensors.getInstance(); + geoDirHandler.updateGeoDir(sensors.currentGeo(), sensors.currentDirection()); } @Override public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.compass_activity_options, menu); - menu.findItem(R.id.menu_compass_sensor).setVisible(hasMagneticFieldSensor); + menu.findItem(R.id.menu_compass_sensor).setVisible(Sensors.getInstance().hasMagneticFieldSensor()); if (cache != null) { LoggingUI.addMenuItems(this, menu, cache); } @@ -226,12 +218,12 @@ public class CompassActivity extends AbstractActionBarActivity { return super.onOptionsItemSelected(item); } - private void setTarget(final Geopoint coords, final String description) { + private void setTarget(@NonNull final Geopoint coords, final String newDescription) { setDestCoords(coords); - setTargetDescription(description); - updateDistanceInfo(app.currentGeo()); + setTargetDescription(newDescription); + updateDistanceInfo(Sensors.getInstance().currentGeo()); - Log.d("destination set: " + description + " (" + dstCoords + ")"); + Log.d("destination set: " + newDescription + " (" + dstCoords + ")"); } private void setTarget(final @NonNull Waypoint waypoint) { @@ -253,12 +245,12 @@ public class CompassActivity extends AbstractActionBarActivity { private void setTargetDescription(final @Nullable String newDescription) { description = newDescription; - if (description == null) { + if (this.description == null) { cacheInfoView.setVisibility(View.GONE); return; } cacheInfoView.setVisibility(View.VISIBLE); - cacheInfoView.setText(description); + cacheInfoView.setText(this.description); } private void updateDistanceInfo(final GeoData geo) { diff --git a/main/src/cgeo/geocaching/CreateShortcutActivity.java b/main/src/cgeo/geocaching/CreateShortcutActivity.java index 01754e2..70ab900 100644 --- a/main/src/cgeo/geocaching/CreateShortcutActivity.java +++ b/main/src/cgeo/geocaching/CreateShortcutActivity.java @@ -99,9 +99,6 @@ public class CreateShortcutActivity extends AbstractActionBarActivity { protected void createOfflineListShortcut(final int listId) { final StoredList list = DataStore.getList(listId); - if (list == null) { - return; - } // target to be executed by the shortcut final Intent targetIntent = new Intent(this, CacheListActivity.class); targetIntent.putExtra(Intents.EXTRA_LIST_ID, list.id); diff --git a/main/src/cgeo/geocaching/DataStore.java b/main/src/cgeo/geocaching/DataStore.java index 3403f3a..b5a4a7a 100644 --- a/main/src/cgeo/geocaching/DataStore.java +++ b/main/src/cgeo/geocaching/DataStore.java @@ -374,6 +374,7 @@ public class DataStore { database = null; } + @NonNull public static File getBackupFileInternal() { return new File(LocalStorage.getStorage(), "cgeo.sqlite"); } @@ -441,14 +442,17 @@ public class DataStore { }); } + @NonNull private static File databasePath(final boolean internal) { return new File(internal ? LocalStorage.getInternalDbDirectory() : LocalStorage.getExternalDbDirectory(), dbName); } + @NonNull private static File databasePath() { return databasePath(!Settings.isDbOnSDCard()); } + @NonNull private static File databaseAlternatePath() { return databasePath(Settings.isDbOnSDCard()); } @@ -1029,6 +1033,7 @@ public class DataStore { return false; } + @Nullable public static String getGeocodeForGuid(final String guid) { if (StringUtils.isBlank(guid)) { return null; @@ -1104,7 +1109,9 @@ public class DataStore { for (final Geocache cache : caches) { final String geocode = cache.getGeocode(); final Geocache existingCache = existingCaches.get(geocode); - final boolean dbUpdateRequired = !cache.gatherMissingFrom(existingCache) || cacheCache.getCacheFromCache(geocode) != null; + boolean dbUpdateRequired = !cache.gatherMissingFrom(existingCache) || cacheCache.getCacheFromCache(geocode) != null; + // parse the note AFTER merging the local information in + dbUpdateRequired |= cache.parseWaypointsFromNote(); cache.addStorageLocation(StorageLocation.CACHE); cacheCache.putCacheInCache(cache); @@ -1150,7 +1157,7 @@ public class DataStore { values.put("hidden", hiddenDate.getTime()); } values.put("hint", cache.getHint()); - values.put("size", cache.getSize() == null ? "" : cache.getSize().id); + values.put("size", cache.getSize().id); values.put("difficulty", cache.getDifficulty()); values.put("terrain", cache.getTerrain()); values.put("location", cache.getLocation()); @@ -1340,6 +1347,7 @@ public class DataStore { * index of the longitude column * @return the coordinates, or null if latitude or longitude is null or the coordinates are invalid */ + @Nullable private static Geopoint getCoords(final Cursor cursor, final int indexLat, final int indexLon) { if (cursor.isNull(indexLat) || cursor.isNull(indexLon)) { return null; @@ -1517,6 +1525,7 @@ public class DataStore { } } + @Nullable public static Viewport getBounds(final Set<String> geocodes) { if (CollectionUtils.isEmpty(geocodes)) { return null; @@ -1533,6 +1542,7 @@ public class DataStore { * The Geocode GCXXXX * @return the loaded cache (if found). Can be null */ + @Nullable public static Geocache loadCache(final String geocode, final EnumSet<LoadFlag> loadFlags) { if (StringUtils.isBlank(geocode)) { throw new IllegalArgumentException("geocode must not be empty"); @@ -1548,6 +1558,7 @@ public class DataStore { * @param geocodes * @return Set of loaded caches. Never null. */ + @NonNull public static Set<Geocache> loadCaches(final Collection<String> geocodes, final EnumSet<LoadFlag> loadFlags) { if (CollectionUtils.isEmpty(geocodes)) { return new HashSet<>(); @@ -1604,6 +1615,7 @@ public class DataStore { * @param loadFlags * @return Set of loaded caches. Never null. */ + @NonNull private static Set<Geocache> loadCachesFromGeocodes(final Set<String> geocodes, final EnumSet<LoadFlag> loadFlags) { if (CollectionUtils.isEmpty(geocodes)) { return Collections.emptySet(); @@ -1695,6 +1707,7 @@ public class DataStore { * @return */ + @NonNull private static StringBuilder buildCoordinateWhere(final String dbTable, final Viewport viewport) { return viewport.resize(1.5).sqlWhere(dbTable); } @@ -1705,6 +1718,7 @@ public class DataStore { * @param cursor * @return Cache from DB */ + @NonNull private static Geocache createCacheFromDatabaseContent(final Cursor cursor) { final Geocache cache = new Geocache(); @@ -1766,6 +1780,7 @@ public class DataStore { return cache; } + @Nullable public static List<String> loadAttributes(final String geocode) { if (StringUtils.isBlank(geocode)) { return null; @@ -1783,6 +1798,7 @@ public class DataStore { GET_STRING_0); } + @Nullable public static Waypoint loadWaypoint(final int id) { if (id == 0) { return null; @@ -1809,6 +1825,7 @@ public class DataStore { return waypoint; } + @Nullable public static List<Waypoint> loadWaypoints(final String geocode) { if (StringUtils.isBlank(geocode)) { return null; @@ -1831,6 +1848,7 @@ public class DataStore { }); } + @NonNull private static Waypoint createWaypointFromDatabaseContent(final Cursor cursor) { final String name = cursor.getString(cursor.getColumnIndex("name")); final WaypointType type = WaypointType.findById(cursor.getString(cursor.getColumnIndex("type"))); @@ -1847,6 +1865,7 @@ public class DataStore { return waypoint; } + @Nullable private static List<Image> loadSpoilers(final String geocode) { if (StringUtils.isBlank(geocode)) { return null; @@ -1875,6 +1894,7 @@ public class DataStore { * * @return A list of previously entered destinations or an empty list. */ + @NonNull public static List<Destination> loadHistoryOfSearchedLocations() { return queryToColl(dbTableSearchDestinationHistory, new String[]{"_id", "date", "latitude", "longitude"}, @@ -1953,6 +1973,7 @@ public class DataStore { return Collections.unmodifiableList(logs); } + @Nullable public static Map<LogType, Integer> loadLogCounts(final String geocode) { if (StringUtils.isBlank(geocode)) { return null; @@ -1981,6 +2002,7 @@ public class DataStore { return logCounts; } + @Nullable private static List<Trackable> loadInventory(final String geocode) { if (StringUtils.isBlank(geocode)) { return null; @@ -2034,6 +2056,7 @@ public class DataStore { return trackable; } + @NonNull private static Trackable createTrackableFromDatabaseContent(final Cursor cursor) { final Trackable trackable = new Trackable(); trackable.setGeocode(cursor.getString(cursor.getColumnIndex("tbcode"))); @@ -2113,6 +2136,7 @@ public class DataStore { return 0; } + @NonNull private static<T, U extends Collection<? super T>> U queryToColl(@NonNull final String table, final String[] columns, final String selection, @@ -2148,6 +2172,7 @@ public class DataStore { * @param listId * @return a non-null set of geocodes */ + @NonNull private static Set<String> loadBatchOfStoredGeocodes(final Geopoint coords, final CacheType cacheType, final int listId) { if (cacheType == null) { throw new IllegalArgumentException("cacheType must not be null"); @@ -2194,6 +2219,7 @@ public class DataStore { } } + @NonNull private static Set<String> loadBatchOfHistoricGeocodes(final boolean detailedOnly, final CacheType cacheType) { final StringBuilder selection = new StringBuilder("visiteddate > 0"); @@ -2225,11 +2251,13 @@ public class DataStore { } /** Retrieve all stored caches from DB */ + @NonNull public static SearchResult loadCachedInViewport(final Viewport viewport, final CacheType cacheType) { return loadInViewport(false, viewport, cacheType); } /** Retrieve stored caches from DB with listId >= 1 */ + @NonNull public static SearchResult loadStoredInViewport(final Viewport viewport, final CacheType cacheType) { return loadInViewport(true, viewport, cacheType); } @@ -2242,6 +2270,7 @@ public class DataStore { * @param cacheType the cache type * @return the matching caches */ + @NonNull private static SearchResult loadInViewport(final boolean stored, final Viewport viewport, final CacheType cacheType) { final Set<String> geocodes = new HashSet<>(); @@ -2335,6 +2364,9 @@ public class DataStore { Log.d("Database clean: removing obsolete log images records"); database.delete(dbTableLogImages, "log_id NOT IN (SELECT _id FROM " + dbTableLogs + ")", null); + // Remove the obsolete "_others" directory where the user avatar used to be stored. + LocalStorage.deleteDirectory(LocalStorage.getStorageDir("_others")); + if (version > -1) { Settings.setVersion(version); } @@ -2353,7 +2385,8 @@ public class DataStore { * @param geocodes * @return */ - private static Set<String> exceptCachesWithOfflineLog(final Set<String> geocodes) { + @NonNull + private static Set<String> exceptCachesWithOfflineLog(@NonNull final Set<String> geocodes) { if (geocodes.isEmpty()) { return geocodes; } @@ -2462,6 +2495,7 @@ public class DataStore { return id != -1; } + @Nullable public static LogEntry loadLogOffline(final String geocode) { if (StringUtils.isBlank(geocode)) { return null; @@ -2580,6 +2614,7 @@ public class DataStore { return lists; } + @NonNull private static ArrayList<StoredList> getListsFromCursor(final Cursor cursor) { final int indexId = cursor.getColumnIndex("_id"); final int indexTitle = cursor.getColumnIndex("title"); @@ -2593,6 +2628,7 @@ public class DataStore { }); } + @NonNull public static StoredList getList(final int id) { init(); if (id >= customListIdOffset) { @@ -2616,11 +2652,7 @@ public class DataStore { } // fall back to standard list in case of invalid list id - if (id == StoredList.STANDARD_LIST_ID || id >= customListIdOffset) { - return new StoredList(StoredList.STANDARD_LIST_ID, res.getString(R.string.list_inbox), (int) PreparedStatement.COUNT_CACHES_ON_STANDARD_LIST.simpleQueryForLong()); - } - - return null; + return new StoredList(StoredList.STANDARD_LIST_ID, res.getString(R.string.list_inbox), (int) PreparedStatement.COUNT_CACHES_ON_STANDARD_LIST.simpleQueryForLong()); } public static int getAllCachesCount() { @@ -2794,6 +2826,7 @@ public class DataStore { * @param geocode * @return */ + @NonNull public static Geocache loadCacheTexts(final String geocode) { final Geocache partial = new Geocache(); @@ -2851,6 +2884,7 @@ public class DataStore { * Creates the WHERE clause for matching multiple geocodes. This automatically converts all given codes to * UPPERCASE. */ + @NonNull private static StringBuilder whereGeocodeIn(final Collection<String> geocodes) { final StringBuilder whereExpr = new StringBuilder("geocode in ("); final Iterator<String> iterator = geocodes.iterator(); @@ -2873,6 +2907,7 @@ public class DataStore { * @return */ + @NonNull public static Set<Waypoint> loadWaypoints(final Viewport viewport, final boolean excludeMine, final boolean excludeDisabled, final CacheType type) { final StringBuilder where = buildCoordinateWhere(dbTableWaypoints, viewport); if (excludeMine) { @@ -2969,6 +3004,7 @@ public class DataStore { moveToList(caches, StoredList.TEMPORARY_LIST.id); } + @Nullable public static Viewport getBounds(final String geocode) { if (geocode == null) { return null; @@ -2981,11 +3017,13 @@ public class DataStore { setVisitDate(Arrays.asList(selected), 0); } + @NonNull public static SearchResult getBatchOfStoredCaches(final Geopoint coords, final CacheType cacheType, final int listId) { final Set<String> geocodes = DataStore.loadBatchOfStoredGeocodes(coords, cacheType, listId); return new SearchResult(geocodes, DataStore.getAllStoredCachesCount(cacheType, listId)); } + @NonNull public static SearchResult getHistoryOfCaches(final boolean detailedOnly, final CacheType cacheType) { final Set<String> geocodes = DataStore.loadBatchOfHistoricGeocodes(detailedOnly, cacheType); return new SearchResult(geocodes, DataStore.getAllHistoryCachesCount()); @@ -2999,6 +3037,7 @@ public class DataStore { return false; } + @NonNull public static Set<String> getCachedMissingFromSearch(final SearchResult searchResult, final Set<Tile> tiles, final IConnector connector, final int maxZoom) { // get cached CacheListActivity @@ -3029,6 +3068,7 @@ public class DataStore { return missingFromSearch; } + @Nullable public static Cursor findSuggestions(final String searchTerm) { // require 3 characters, otherwise there are to many results if (StringUtils.length(searchTerm) < 3) { @@ -3064,6 +3104,7 @@ public class DataStore { cursor.close(); } + @NonNull private static String getSuggestionArgument(final String input) { return "%" + StringUtils.trim(input) + "%"; } @@ -3091,6 +3132,7 @@ public class DataStore { cursor.close(); } + @NonNull public static String[] getSuggestions(final String table, final String column, final String input) { try { final Cursor cursor = database.rawQuery("SELECT DISTINCT " + column @@ -3104,22 +3146,27 @@ public class DataStore { } } + @NonNull public static String[] getSuggestionsOwnerName(final String input) { return getSuggestions(dbTableCaches, "owner_real", input); } + @NonNull public static String[] getSuggestionsTrackableCode(final String input) { return getSuggestions(dbTableTrackables, "tbcode", input); } + @NonNull public static String[] getSuggestionsFinderName(final String input) { return getSuggestions(dbTableLogs, "author", input); } + @NonNull public static String[] getSuggestionsGeocode(final String input) { return getSuggestions(dbTableCaches, "geocode", input); } + @NonNull public static String[] getSuggestionsKeyword(final String input) { return getSuggestions(dbTableCaches, "name", input); } @@ -3128,6 +3175,7 @@ public class DataStore { * * @return list of last caches opened in the details view, ordered by most recent first */ + @NonNull public static ArrayList<Geocache> getLastOpenedCaches() { final List<String> geocodes = Settings.getLastOpenedCaches(); final Set<Geocache> cachesSet = DataStore.loadCaches(geocodes, LoadFlags.LOAD_CACHE_OR_DB); diff --git a/main/src/cgeo/geocaching/EditWaypointActivity.java b/main/src/cgeo/geocaching/EditWaypointActivity.java index 7cfb9f9..8cb947a 100644 --- a/main/src/cgeo/geocaching/EditWaypointActivity.java +++ b/main/src/cgeo/geocaching/EditWaypointActivity.java @@ -12,6 +12,7 @@ import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.location.GeopointFormatter; import cgeo.geocaching.sensors.GeoData; import cgeo.geocaching.sensors.GeoDirHandler; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.dialog.CoordinatesInputDialog; import cgeo.geocaching.ui.dialog.Dialogs; @@ -308,7 +309,7 @@ public class EditWaypointActivity extends AbstractActionBarActivity implements C @Override protected void onPostExecute(final Geocache cache) { - final CoordinatesInputDialog coordsDialog = CoordinatesInputDialog.getInstance(cache, geopoint, app.currentGeo()); + final CoordinatesInputDialog coordsDialog = CoordinatesInputDialog.getInstance(cache, geopoint, Sensors.getInstance().currentGeo()); coordsDialog.setCancelable(true); coordsDialog.show(getSupportFragmentManager(), "wpeditdialog"); } @@ -391,7 +392,7 @@ public class EditWaypointActivity extends AbstractActionBarActivity implements C return; } } else { - coords = app.currentGeo().getCoords(); + coords = Sensors.getInstance().currentGeo().getCoords(); } if (StringUtils.isNotBlank(bearingText) && StringUtils.isNotBlank(distanceText)) { diff --git a/main/src/cgeo/geocaching/Geocache.java b/main/src/cgeo/geocaching/Geocache.java index 69babc4..c842a7f 100644 --- a/main/src/cgeo/geocaching/Geocache.java +++ b/main/src/cgeo/geocaching/Geocache.java @@ -98,7 +98,7 @@ public class Geocache implements IWaypoint { * lazy initialized */ private String hint = null; - private CacheSize size = CacheSize.UNKNOWN; + @NonNull private CacheSize size = CacheSize.UNKNOWN; private float difficulty = 0; private float terrain = 0; private Float direction = null; @@ -271,7 +271,7 @@ public class Geocache implements IWaypoint { if (!detailed && StringUtils.isBlank(getHint())) { hint = other.getHint(); } - if (size == null || CacheSize.UNKNOWN == size) { + if (size == CacheSize.UNKNOWN) { size = other.size; } if (difficulty == 0) { @@ -479,6 +479,7 @@ public class Geocache implements IWaypoint { notifyChange(); } + @NonNull public List<LogType> getPossibleLogTypes() { return getConnector().getPossibleLogTypes(this); } @@ -502,19 +503,11 @@ public class Geocache implements IWaypoint { } } - - private String getCacheUrl() { - return getConnector().getCacheUrl(this); - } - + @NonNull private IConnector getConnector() { return ConnectorFactory.getConnector(this); } - public boolean canOpenInBrowser() { - return getCacheUrl() != null; - } - public boolean supportsRefresh() { return getConnector() instanceof ISearchByGeocode; } @@ -539,6 +532,7 @@ public class Geocache implements IWaypoint { return getConnector().supportsOwnCoordinates(); } + @NonNull public ILoggingManager getLoggingManager(final LogCacheActivity activity) { return getConnector().getLoggingManager(activity, this); } @@ -559,10 +553,8 @@ public class Geocache implements IWaypoint { return ownerDisplayName; } + @NonNull public CacheSize getSize() { - if (size == null) { - return CacheSize.UNKNOWN; - } return size; } @@ -597,7 +589,8 @@ public class Geocache implements IWaypoint { /** * @return GC username of the (actual) owner, might differ from the owner. Never empty. */ - @NonNull public String getOwnerUserId() { + @NonNull + public String getOwnerUserId() { return ownerUserId; } @@ -706,6 +699,8 @@ public class Geocache implements IWaypoint { fromActivity.startActivity(Intent.createChooser(intent, res.getText(R.string.cache_menu_share))); } + + @NonNull public Intent getShareIntent() { final StringBuilder subject = new StringBuilder("Geocache "); subject.append(geocode); @@ -721,15 +716,20 @@ public class Geocache implements IWaypoint { return intent; } + @NonNull public String getUrl() { return getConnector().getCacheUrl(this); } + @NonNull public String getLongUrl() { return getConnector().getLongCacheUrl(this); } - public String getCgeoUrl() { return getConnector().getCacheUrl(this); } + @NonNull + public String getCgeoUrl() { + return getConnector().getCacheUrl(this); + } public boolean supportsGCVote() { return StringUtils.startsWithIgnoreCase(geocode, "GC"); @@ -760,6 +760,7 @@ public class Geocache implements IWaypoint { return hidden; } + @NonNull public List<String> getAttributes() { return attributes.getUnderlyingList(); } @@ -775,6 +776,7 @@ public class Geocache implements IWaypoint { spoilers.add(spoiler); } + @NonNull public List<Image> getSpoilers() { return ListUtils.unmodifiableList(ListUtils.emptyIfNull(spoilers)); } @@ -972,6 +974,7 @@ public class Geocache implements IWaypoint { * * @return always non <code>null</code> */ + @NonNull public List<Waypoint> getWaypoints() { return waypoints.getUnderlyingList(); } @@ -1079,13 +1082,8 @@ public class Geocache implements IWaypoint { this.hint = hint; } - public void setSize(final CacheSize size) { - if (size == null) { - this.size = CacheSize.UNKNOWN; - } - else { - this.size = size; - } + public void setSize(@NonNull final CacheSize size) { + this.size = size; } public void setDifficulty(final float difficulty) { @@ -1368,12 +1366,15 @@ public class Geocache implements IWaypoint { /** * Detect coordinates in the personal note and convert them to user defined waypoints. Works by rule of thumb. */ - public void parseWaypointsFromNote() { + public boolean parseWaypointsFromNote() { + boolean changed = false; for (final Waypoint waypoint : Waypoint.parseWaypointsFromNote(StringUtils.defaultString(getPersonalNote()))) { if (!hasIdenticalWaypoint(waypoint.getCoords())) { addOrChangeWaypoint(waypoint, false); + changed = true; } } + return changed; } private boolean hasIdenticalWaypoint(final Geopoint point) { @@ -1475,7 +1476,7 @@ public class Geocache implements IWaypoint { warnIncorrectParsingIfBlank(getOwnerUserId(), "owner"); warnIncorrectParsingIf(getHiddenDate() == null, "hidden"); warnIncorrectParsingIf(getFavoritePoints() < 0, "favoriteCount"); - warnIncorrectParsingIf(getSize() == null, "size"); + warnIncorrectParsingIf(getSize() == CacheSize.UNKNOWN, "size"); warnIncorrectParsingIf(getType() == null || getType() == CacheType.UNKNOWN, "type"); warnIncorrectParsingIf(getCoords() == null, "coordinates"); warnIncorrectParsingIfBlank(getLocation(), "location"); @@ -1617,6 +1618,7 @@ public class Geocache implements IWaypoint { * * @return start time in minutes after midnight */ + @Nullable public String guessEventTimeMinutes() { if (!isEventCache()) { return null; @@ -1666,6 +1668,7 @@ public class Geocache implements IWaypoint { } }; + @NonNull public Collection<Image> getImages() { final LinkedList<Image> result = new LinkedList<>(); result.addAll(getSpoilers()); @@ -1735,6 +1738,7 @@ public class Geocache implements IWaypoint { return getConnector().getWaypointGpxId(prefix, geocode); } + @NonNull public String getWaypointPrefix(final String name) { return getConnector().getWaypointPrefix(name); } @@ -1757,6 +1761,7 @@ public class Geocache implements IWaypoint { return (getType().applyDistanceRule() || hasUserModifiedCoords()) && getConnector() == GCConnector.getInstance(); } + @NonNull public LogType getDefaultLogType() { if (isEventCache()) { final Date eventDate = getHiddenDate(); @@ -1782,7 +1787,8 @@ public class Geocache implements IWaypoint { * @param caches a collection of caches * @return the non-blank geocodes of the caches */ - public static Set<String> getGeocodes(final Collection<Geocache> caches) { + @NonNull + public static Set<String> getGeocodes(@NonNull final Collection<Geocache> caches) { final Set<String> geocodes = new HashSet<>(caches.size()); for (final Geocache cache : caches) { final String geocode = cache.getGeocode(); diff --git a/main/src/cgeo/geocaching/GpxFileListActivity.java b/main/src/cgeo/geocaching/GpxFileListActivity.java index 3da4927..352dbab 100644 --- a/main/src/cgeo/geocaching/GpxFileListActivity.java +++ b/main/src/cgeo/geocaching/GpxFileListActivity.java @@ -9,6 +9,7 @@ import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.GPXListAdapter; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import android.app.Activity; import android.content.Intent; @@ -24,7 +25,7 @@ public class GpxFileListActivity extends AbstractFileListActivity<GPXListAdapter } @Override - protected GPXListAdapter getAdapter(List<File> files) { + protected GPXListAdapter getAdapter(final List<File> files) { return new GPXListAdapter(this, files); } @@ -33,17 +34,17 @@ public class GpxFileListActivity extends AbstractFileListActivity<GPXListAdapter return Collections.singletonList(new File(Settings.getGpxImportDir())); } - public static void startSubActivity(Activity fromActivity, int listId) { + public static void startSubActivity(final Activity fromActivity, final int listId) { final Intent intent = new Intent(fromActivity, GpxFileListActivity.class); intent.putExtra(Intents.EXTRA_LIST_ID, StoredList.getConcreteList(listId)); fromActivity.startActivityForResult(intent, 0); } @Override - protected boolean filenameBelongsToList(final String filename) { + protected boolean filenameBelongsToList(@NonNull final String filename) { if (super.filenameBelongsToList(filename)) { if (StringUtils.endsWithIgnoreCase(filename, GPXImporter.ZIP_FILE_EXTENSION)) { - for (IConnector connector : ConnectorFactory.getConnectors()) { + for (final IConnector connector : ConnectorFactory.getConnectors()) { if (connector.isZippedGPXFile(filename)) { return true; } diff --git a/main/src/cgeo/geocaching/LogCacheActivity.java b/main/src/cgeo/geocaching/LogCacheActivity.java index 996b2d8..118c47b 100644 --- a/main/src/cgeo/geocaching/LogCacheActivity.java +++ b/main/src/cgeo/geocaching/LogCacheActivity.java @@ -12,6 +12,8 @@ import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.LogTypeTrackable; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.gcvote.GCVote; +import cgeo.geocaching.gcvote.GCVoteRatingBarUtil; +import cgeo.geocaching.gcvote.GCVoteRatingBarUtil.OnRatingChangeListener; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.twitter.Twitter; import cgeo.geocaching.ui.dialog.DateDialog; @@ -45,8 +47,6 @@ import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.LinearLayout; -import android.widget.RatingBar; -import android.widget.RatingBar.OnRatingBarChangeListener; import android.widget.TextView; import java.util.ArrayList; @@ -86,7 +86,6 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia private String imageDescription; private Uri imageUri; private boolean sendButtonEnabled; - private boolean isRatingBarShown = false; public void onLoadFinished() { if (loggingManager.hasLoaderError()) { @@ -286,25 +285,14 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia } private void initializeRatingBar() { - if (GCVote.isVotingPossible(cache) && !isRatingBarShown) { - final RatingBar ratingBar = ButterKnife.findById(this, R.id.gcvoteRating); - final TextView label = ButterKnife.findById(this, R.id.gcvoteLabel); - isRatingBarShown = true; - ratingBar.setVisibility(View.VISIBLE); - label.setVisibility(View.VISIBLE); - ratingBar.setOnRatingBarChangeListener(new OnRatingBarChangeListener() { + if (GCVote.isVotingPossible(cache)) { + GCVoteRatingBarUtil.initializeRatingBar(cache, getWindow().getDecorView().getRootView(), new OnRatingChangeListener() { @Override - public void onRatingChanged(final RatingBar ratingBar, final float stars, final boolean fromUser) { - // 0.5 is not a valid rating, therefore we must limit - rating = GCVote.isValidRating(stars) ? stars : 0; - if (rating < stars) { - ratingBar.setRating(rating); - } - label.setText(GCVote.getDescription(rating)); + public void onRatingChanged(final float stars) { + rating = stars; } }); - ratingBar.setRating(cache.getMyVote()); } } diff --git a/main/src/cgeo/geocaching/LogTrackableActivity.java b/main/src/cgeo/geocaching/LogTrackableActivity.java index fde328a..fc4a066 100644 --- a/main/src/cgeo/geocaching/LogTrackableActivity.java +++ b/main/src/cgeo/geocaching/LogTrackableActivity.java @@ -136,19 +136,18 @@ public class LogTrackableActivity extends AbstractLoggingActivity implements Dat trackable = DataStore.loadTrackable(geocode); + if (trackable == null) { + Log.e("LogTrackableActivity.onCreate: cannot load trackable " + geocode); + finish(); + return; + } + if (StringUtils.isNotBlank(trackable.getName())) { setTitle(res.getString(R.string.trackable_touch) + ": " + trackable.getName()); } else { setTitle(res.getString(R.string.trackable_touch) + ": " + trackable.getGeocode()); } - if (guid == null) { - showToast(res.getString(R.string.err_tb_forgot_saw)); - - finish(); - return; - } - init(); requestKeyboardForLogging(); } @@ -256,11 +255,6 @@ public class LogTrackableActivity extends AbstractLoggingActivity implements Dat public LoadDataThread() { super("Load data for logging trackable"); - if (guid == null) { - showToast(res.getString(R.string.err_tb_forgot_saw)); - - finish(); - } } @Override diff --git a/main/src/cgeo/geocaching/MainActivity.java b/main/src/cgeo/geocaching/MainActivity.java index 321eaca..558c70a 100644 --- a/main/src/cgeo/geocaching/MainActivity.java +++ b/main/src/cgeo/geocaching/MainActivity.java @@ -19,6 +19,7 @@ import cgeo.geocaching.sensors.GeoData; import cgeo.geocaching.sensors.GeoDirHandler; import cgeo.geocaching.sensors.GpsStatusProvider; import cgeo.geocaching.sensors.GpsStatusProvider.Status; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.settings.SettingsActivity; import cgeo.geocaching.ui.dialog.Dialogs; @@ -113,7 +114,7 @@ public class MainActivity extends AbstractActionBarActivity { for (final ILogin conn : loginConns) { - final TextView connectorInfo = (TextView) inflater.inflate(R.layout.main_activity_connectorstatus, null); + final TextView connectorInfo = (TextView) inflater.inflate(R.layout.main_activity_connectorstatus, infoArea, false); infoArea.addView(connectorInfo); final StringBuilder userInfo = new StringBuilder(conn.getName()).append(Formatter.SEPARATOR); @@ -226,11 +227,8 @@ public class MainActivity extends AbstractActionBarActivity { @Override public void onResume() { super.onResume(locationUpdater.start(GeoDirHandler.UPDATE_GEODATA | GeoDirHandler.LOW_POWER), - app.gpsStatusObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(satellitesHandler)); + Sensors.getInstance().gpsStatusObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(satellitesHandler)); updateUserInfoHandler.sendEmptyMessage(-1); - if (app.hasValidLocation()) { - locationUpdater.updateGeoData(app.currentGeo()); - } startBackgroundLogin(); init(); @@ -239,7 +237,7 @@ public class MainActivity extends AbstractActionBarActivity { } private void startBackgroundLogin() { - assert(app != null); + assert app != null; final boolean mustLogin = app.mustRelog(); @@ -585,8 +583,7 @@ public class MainActivity extends AbstractActionBarActivity { try { addCoords = geo.getCoords(); final Geocoder geocoder = new Geocoder(MainActivity.this, Locale.getDefault()); - final Geopoint coords = app.currentGeo().getCoords(); - final List<Address> addresses = geocoder.getFromLocation(coords.getLatitude(), coords.getLongitude(), 1); + final List<Address> addresses = geocoder.getFromLocation(addCoords.getLatitude(), addCoords.getLongitude(), 1); if (!addresses.isEmpty()) { subscriber.onNext(formatAddress(addresses.get(0))); } diff --git a/main/src/cgeo/geocaching/NavigateAnyPointActivity.java b/main/src/cgeo/geocaching/NavigateAnyPointActivity.java index e75d6b1..d92e441 100644 --- a/main/src/cgeo/geocaching/NavigateAnyPointActivity.java +++ b/main/src/cgeo/geocaching/NavigateAnyPointActivity.java @@ -12,6 +12,7 @@ import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.location.GeopointFormatter; import cgeo.geocaching.sensors.GeoData; import cgeo.geocaching.sensors.GeoDirHandler; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.AbstractViewHolder; import cgeo.geocaching.ui.NavigationActionProvider; @@ -286,7 +287,7 @@ public class NavigateAnyPointActivity extends AbstractActionBarActivity implemen if (latButton.getText().length() > 0 && lonButton.getText().length() > 0) { gp = new Geopoint(latButton.getText().toString() + " " + lonButton.getText().toString()); } - final CoordinatesInputDialog coordsDialog = CoordinatesInputDialog.getInstance(null, gp, app.currentGeo()); + final CoordinatesInputDialog coordsDialog = CoordinatesInputDialog.getInstance(null, gp, Sensors.getInstance().currentGeo()); coordsDialog.setCancelable(true); coordsDialog.show(getSupportFragmentManager(),"wpedit_dialog"); } @@ -477,7 +478,7 @@ public class NavigateAnyPointActivity extends AbstractActionBarActivity implemen @Override public void onClick(final View arg0) { - final Geopoint coords = app.currentGeo().getCoords(); + final Geopoint coords = Sensors.getInstance().currentGeo().getCoords(); latButton.setText(coords.format(GeopointFormatter.Format.LAT_DECMINUTE)); lonButton.setText(coords.format(GeopointFormatter.Format.LON_DECMINUTE)); changed = false; @@ -507,7 +508,7 @@ public class NavigateAnyPointActivity extends AbstractActionBarActivity implemen return null; } } else { - coords = app.currentGeo().getCoords(); + coords = Sensors.getInstance().currentGeo().getCoords(); } // apply projection diff --git a/main/src/cgeo/geocaching/SearchActivity.java b/main/src/cgeo/geocaching/SearchActivity.java index 9de279d..8842603 100644 --- a/main/src/cgeo/geocaching/SearchActivity.java +++ b/main/src/cgeo/geocaching/SearchActivity.java @@ -13,6 +13,7 @@ import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.location.GeopointFormatter; import cgeo.geocaching.search.AutoCompleteAdapter; import cgeo.geocaching.sensors.GeoData; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.dialog.CoordinatesInputDialog; import cgeo.geocaching.ui.dialog.Dialogs; @@ -144,7 +145,7 @@ public class SearchActivity extends AbstractActionBarActivity implements Coordin } final IConnector connector = ConnectorFactory.getConnector(geocode); - if (connector instanceof ISearchByGeocode) { + if (connector instanceof ISearchByGeocode && geocode != null) { CacheDetailActivity.startActivity(this, geocode.toUpperCase(Locale.US)); return true; } @@ -161,7 +162,7 @@ public class SearchActivity extends AbstractActionBarActivity implements Coordin } } - if (trackableConnector != ConnectorFactory.UNKNOWN_TRACKABLE_CONNECTOR) { + if (trackableConnector != ConnectorFactory.UNKNOWN_TRACKABLE_CONNECTOR && geocode != null) { final Intent trackablesIntent = new Intent(this, TrackableActivity.class); trackablesIntent.putExtra(Intents.EXTRA_GEOCODE, geocode.toUpperCase(Locale.US)); startActivity(trackablesIntent); @@ -293,7 +294,7 @@ public class SearchActivity extends AbstractActionBarActivity implements Coordin } private void updateCoordinates() { - final CoordinatesInputDialog coordsDialog = CoordinatesInputDialog.getInstance(null, null, app.currentGeo()); + final CoordinatesInputDialog coordsDialog = CoordinatesInputDialog.getInstance(null, null, Sensors.getInstance().currentGeo()); coordsDialog.setCancelable(true); coordsDialog.show(getSupportFragmentManager(), "wpedit_dialog"); } @@ -309,7 +310,7 @@ public class SearchActivity extends AbstractActionBarActivity implements Coordin final String lonText = StringUtils.trim(buttonLongitude.getText().toString()); if (StringUtils.isEmpty(latText) || StringUtils.isEmpty(lonText)) { - final GeoData geo = app.currentGeo(); + final GeoData geo = Sensors.getInstance().currentGeo(); buttonLatitude.setText(geo.getCoords().format(GeopointFormatter.Format.LAT_DECMINUTE)); buttonLongitude.setText(geo.getCoords().format(GeopointFormatter.Format.LON_DECMINUTE)); } else { @@ -373,7 +374,7 @@ public class SearchActivity extends AbstractActionBarActivity implements Coordin } private void findByGeocodeFn() { - final String geocodeText = StringUtils.trim(geocodeEditText.getText().toString()); + final String geocodeText = StringUtils.trimToEmpty(geocodeEditText.getText().toString()); if (StringUtils.isBlank(geocodeText) || geocodeText.equalsIgnoreCase("GC")) { Dialogs.message(this, R.string.warn_search_help_title, R.string.warn_search_help_gccode); @@ -384,7 +385,7 @@ public class SearchActivity extends AbstractActionBarActivity implements Coordin } private void findTrackableFn() { - final String trackableText = StringUtils.trim(trackableEditText.getText().toString()); + final String trackableText = StringUtils.trimToEmpty(trackableEditText.getText().toString()); if (StringUtils.isBlank(trackableText) || trackableText.equalsIgnoreCase("TB")) { Dialogs.message(this, R.string.warn_search_help_title, R.string.warn_search_help_tb); diff --git a/main/src/cgeo/geocaching/SelectMapfileActivity.java b/main/src/cgeo/geocaching/SelectMapfileActivity.java index 697f609..a506f16 100644 --- a/main/src/cgeo/geocaching/SelectMapfileActivity.java +++ b/main/src/cgeo/geocaching/SelectMapfileActivity.java @@ -87,7 +87,7 @@ public class SelectMapfileActivity extends AbstractFileListActivity<FileSelectio for (File dir : LocalStorage.getStorages()) { folders.add(new File(dir, "mfmaps")); folders.add(new File(new File(dir, "Locus"), "mapsVector")); - folders.add(new File(dir, LocalStorage.cache)); + folders.add(new File(dir, LocalStorage.CACHE_DIRNAME)); } return folders; } diff --git a/main/src/cgeo/geocaching/StaticMapsActivity.java b/main/src/cgeo/geocaching/StaticMapsActivity.java index 7d822ec..be363f0 100644 --- a/main/src/cgeo/geocaching/StaticMapsActivity.java +++ b/main/src/cgeo/geocaching/StaticMapsActivity.java @@ -91,12 +91,14 @@ public class StaticMapsActivity extends AbstractActionBarActivity { public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState, R.layout.staticmaps_activity); - if (geocode == null) { - showToast("Sorry, c:geo forgot for what cache you want to load static maps."); + cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + + if (cache == null) { + Log.e("StaticMapsActivity.onCreate: cannot find the cache " + geocode); finish(); return; } - cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + setCacheTitleBar(cache); waitDialog = ProgressDialog.show(this, null, res.getString(R.string.map_static_loading), true); diff --git a/main/src/cgeo/geocaching/StaticMapsProvider.java b/main/src/cgeo/geocaching/StaticMapsProvider.java index fd7b751..5184f71 100644 --- a/main/src/cgeo/geocaching/StaticMapsProvider.java +++ b/main/src/cgeo/geocaching/StaticMapsProvider.java @@ -17,6 +17,7 @@ import org.eclipse.jdt.annotation.NonNull; import rx.Observable; import rx.functions.Action0; +import rx.functions.Func0; import rx.util.async.Async; import android.graphics.Bitmap; @@ -38,6 +39,8 @@ public final class StaticMapsProvider { private static final String MAP_FILENAME_PREFIX = "map_"; private static final String MARKERS_URL = "http://status.cgeo.org/assets/markers/"; + private static volatile long last403 = 0; + /** We assume there is no real usable image with less than 1k. */ private static final int MIN_MAP_IMAGE_BYTES = 1000; @@ -54,32 +57,43 @@ public final class StaticMapsProvider { return LocalStorage.getStorageFile(geocode, MAP_FILENAME_PREFIX + prefix, false, createDirs); } + private static <T> Observable<T> checkDownloadPermission(final Observable<T> ifPermitted) { + return Observable.defer(new Func0<Observable<T>>() { + @Override + public Observable<T> call() { + if (System.currentTimeMillis() - last403 >= 30000) { + return ifPermitted; + } + Log.d("StaticMaps.downloadMap: request ignored because of recent \"permission denied\" answer"); + return Observable.empty(); + } + }); + } + private static Observable<String> downloadDifferentZooms(final String geocode, final String markerUrl, final String prefix, final String latlonMap, final int width, final int height, final Parameters waypoints) { - return Observable.merge(downloadMap(geocode, 20, SATELLITE, markerUrl, prefix + '1', "", latlonMap, width, height, waypoints), + return checkDownloadPermission(Observable.merge(downloadMap(geocode, 20, SATELLITE, markerUrl, prefix + '1', "", latlonMap, width, height, waypoints), downloadMap(geocode, 18, SATELLITE, markerUrl, prefix + '2', "", latlonMap, width, height, waypoints), downloadMap(geocode, 16, ROADMAP, markerUrl, prefix + '3', "", latlonMap, width, height, waypoints), downloadMap(geocode, 14, ROADMAP, markerUrl, prefix + '4', "", latlonMap, width, height, waypoints), - downloadMap(geocode, 11, ROADMAP, markerUrl, prefix + '5', "", latlonMap, width, height, waypoints)); + downloadMap(geocode, 11, ROADMAP, markerUrl, prefix + '5', "", latlonMap, width, height, waypoints))); } private static Observable<String> downloadMap(final String geocode, final int zoom, final String mapType, final String markerUrl, final String prefix, final String shadow, final String latlonMap, final int width, final int height, final Parameters waypoints) { - int scale = 1; - if (width > GOOGLE_MAPS_MAX_SIZE) { - scale = 2; - } + // If it has been less than 30 seconds since we got a 403 (permission denied) from Google servers, + // do not try again. + final int scale = width <= GOOGLE_MAPS_MAX_SIZE ? 1 : 2; final float aspectRatio = width / (float) height; final int requestWidth = Math.min(width / scale, GOOGLE_MAPS_MAX_SIZE); final int requestHeight = (aspectRatio > 1) ? Math.round(requestWidth / aspectRatio) : requestWidth; - final int requestScale = scale; final int requestZoom = Math.min((scale == 2) ? zoom + 1 : zoom, GOOGLE_MAX_ZOOM); - return Async.fromAction(new Action0() { + return checkDownloadPermission(Async.fromAction(new Action0() { @Override public void call() { final Parameters params = new Parameters( "center", latlonMap, "zoom", String.valueOf(requestZoom), "size", String.valueOf(requestWidth) + 'x' + String.valueOf(requestHeight), - "scale", String.valueOf(requestScale), + "scale", String.valueOf(scale), "maptype", mapType, "markers", "icon:" + markerUrl + '|' + shadow + latlonMap, "sensor", "false"); @@ -92,8 +106,12 @@ public final class StaticMapsProvider { Log.e("StaticMapsProvider.downloadMap: httpResponse is null"); return; } - if (httpResponse.getStatusLine().getStatusCode() != 200) { - Log.d("StaticMapsProvider.downloadMap: httpResponseCode = " + httpResponse.getStatusLine().getStatusCode()); + final int statusCode = httpResponse.getStatusLine().getStatusCode(); + if (statusCode != 200) { + Log.d("StaticMapsProvider.downloadMap: httpResponseCode = " + statusCode); + if (statusCode == 403) { + last403 = System.currentTimeMillis(); + } return; } final File file = getMapFile(geocode, prefix, true); @@ -105,7 +123,7 @@ public final class StaticMapsProvider { } } } - }, prefix, RxUtils.networkScheduler); + }, prefix, RxUtils.networkScheduler)); } public static Observable<String> downloadMaps(final Geocache cache) { @@ -132,7 +150,7 @@ public final class StaticMapsProvider { } - return Observable.merge(downloaders); + return checkDownloadPermission(Observable.merge(downloaders)); } /** @@ -144,7 +162,7 @@ public final class StaticMapsProvider { for (final Waypoint waypoint : cache.getWaypoints()) { downloaders.add(storeWaypointStaticMap(cache.getGeocode(), width, height, waypoint)); } - return Observable.merge(downloaders); + return checkDownloadPermission(Observable.merge(downloaders)); } public static Observable<String> storeWaypointStaticMap(final Geocache cache, final Waypoint waypoint) { diff --git a/main/src/cgeo/geocaching/Trackable.java b/main/src/cgeo/geocaching/Trackable.java index 15416bc..f93ec7b 100644 --- a/main/src/cgeo/geocaching/Trackable.java +++ b/main/src/cgeo/geocaching/Trackable.java @@ -42,10 +42,12 @@ public class Trackable implements ILogable { private List<LogEntry> logs = new ArrayList<>(); private String trackingcode = null; + @NonNull public String getUrl() { return getConnector().getUrl(this); } + @NonNull private TrackableConnector getConnector() { return ConnectorFactory.getConnector(this); } @@ -219,6 +221,7 @@ public class Trackable implements ILogable { this.trackingcode = trackingcode; } + @NonNull public Collection<Image> getImages() { final List<Image> images = new LinkedList<>(); if (StringUtils.isNotBlank(image)) { @@ -231,6 +234,7 @@ public class Trackable implements ILogable { return images; } + @NonNull static List<LogType> getPossibleLogTypes() { final List<LogType> logTypes = new ArrayList<>(); logTypes.add(LogType.RETRIEVED_IT); diff --git a/main/src/cgeo/geocaching/Waypoint.java b/main/src/cgeo/geocaching/Waypoint.java index 5cfeb29..5cac4db 100644 --- a/main/src/cgeo/geocaching/Waypoint.java +++ b/main/src/cgeo/geocaching/Waypoint.java @@ -97,18 +97,11 @@ public class Waypoint implements IWaypoint { if (newPrefixes.containsKey(prefix)) { newPrefixes.get(prefix).merge(oldWaypoint); } else if (oldWaypoint.isUserDefined() || forceMerge) { - // personal note waypoints should always be taken from the new list only - if (!isPersonalNoteWaypoint(oldWaypoint)) { - newPoints.add(oldWaypoint); - } + newPoints.add(oldWaypoint); } } } - private static boolean isPersonalNoteWaypoint(final @NonNull Waypoint waypoint) { - return StringUtils.startsWith(waypoint.getName(), CgeoApplication.getInstance().getString(R.string.cache_personal_note) + " "); - } - public boolean isUserDefined() { return own || WaypointType.OWN == waypointType; } @@ -157,6 +150,7 @@ public class Waypoint implements IWaypoint { cachedOrder = ORDER_UNDEFINED; } + @NonNull public String getUrl() { return "http://www.geocaching.com/seek/cache_details.aspx?wp=" + geocode; } @@ -304,7 +298,7 @@ public class Waypoint implements IWaypoint { ((point.getLatitudeE6() % 1000) != 0 || (point.getLongitudeE6() % 1000) != 0)) { final String name = CgeoApplication.getInstance().getString(R.string.cache_personal_note) + " " + count; final String potentialWaypointType = note.substring(Math.max(0, matcher.start() - 15)); - final Waypoint waypoint = new Waypoint(name, parseWaypointType(potentialWaypointType), false); + final Waypoint waypoint = new Waypoint(name, parseWaypointType(potentialWaypointType), true); waypoint.setCoords(point); waypoints.add(waypoint); count++; diff --git a/main/src/cgeo/geocaching/WaypointPopupFragment.java b/main/src/cgeo/geocaching/WaypointPopupFragment.java index 83dad09..227e30d 100644 --- a/main/src/cgeo/geocaching/WaypointPopupFragment.java +++ b/main/src/cgeo/geocaching/WaypointPopupFragment.java @@ -60,6 +60,13 @@ public class WaypointPopupFragment extends AbstractDialogFragment { super.init(); waypoint = DataStore.loadWaypoint(waypointId); + + if (waypoint == null) { + Log.e("WaypointPopupFragment.init: unable to get waypoint " + waypointId); + getActivity().finish(); + return; + } + try { if (StringUtils.isNotBlank(waypoint.getName())) { setTitle(waypoint.getName()); diff --git a/main/src/cgeo/geocaching/activity/ActivityMixin.java b/main/src/cgeo/geocaching/activity/ActivityMixin.java index b135358..14a2fbf 100644 --- a/main/src/cgeo/geocaching/activity/ActivityMixin.java +++ b/main/src/cgeo/geocaching/activity/ActivityMixin.java @@ -157,6 +157,15 @@ public final class ActivityMixin { editText.setSelection(newCursor); } + /** + * This is the exact code from Google to implement Up navigation, with one exception: activity.isTaskRoot() was + * added as {@link NavUtils#shouldUpRecreateTask(Activity, Intent)} seems not to handle the case, that this activity + * was created from an intent by another app, and our own app is not yet running. The bug seems to be fixed in + * Android 4.4.something, however. + * + * @param activity + * @return + */ public static boolean navigateUp(@NonNull final Activity activity) { // see http://developer.android.com/training/implementing-navigation/ancestral.html final Intent upIntent = NavUtils.getParentActivityIntent(activity); @@ -164,7 +173,7 @@ public final class ActivityMixin { activity.finish(); return true; } - if (NavUtils.shouldUpRecreateTask(activity, upIntent)) { + if (NavUtils.shouldUpRecreateTask(activity, upIntent) || activity.isTaskRoot()) { // This activity is NOT part of this app's task, so create a new task // when navigating up, with a synthesized back stack. TaskStackBuilder.create(activity) diff --git a/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsDirectionApp.java b/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsDirectionApp.java index ccfd7a1..d14fca4 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsDirectionApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsDirectionApp.java @@ -1,10 +1,10 @@ package cgeo.geocaching.apps.cache.navi; -import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.maps.MapProviderFactory; import cgeo.geocaching.sensors.GeoData; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.utils.Log; import android.app.Activity; @@ -25,7 +25,7 @@ class GoogleMapsDirectionApp extends AbstractPointNavigationApp { @Override public void navigate(final Activity activity, final Geopoint coords) { try { - final GeoData geo = CgeoApplication.getInstance().currentGeo(); + final GeoData geo = Sensors.getInstance().currentGeo(); activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri .parse("http://maps.google.com/maps?f=d&saddr=" + geo.getCoords().getLatitude() + "," + geo.getCoords().getLongitude() + "&daddr=" diff --git a/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java b/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java index f346fc0..d542541 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java @@ -80,6 +80,9 @@ public final class NavigationAppFactory extends AbstractAppFactory { this.app = app; this.id = id; this.preferenceKey = preferenceKey; + if (preferenceKey == 0 || preferenceKey == -1) { + throw new IllegalStateException("Every navigation app must have a boolean preference in the settings to be enabled/disabled."); + } } /** diff --git a/main/src/cgeo/geocaching/connector/AbstractConnector.java b/main/src/cgeo/geocaching/connector/AbstractConnector.java index 834167a..4984273 100644 --- a/main/src/cgeo/geocaching/connector/AbstractConnector.java +++ b/main/src/cgeo/geocaching/connector/AbstractConnector.java @@ -39,12 +39,12 @@ public abstract class AbstractConnector implements IConnector { } @Override - public boolean addToWatchlist(Geocache cache) { + public boolean addToWatchlist(@NonNull final Geocache cache) { return false; } @Override - public boolean removeFromWatchlist(Geocache cache) { + public boolean removeFromWatchlist(@NonNull final Geocache cache) { return false; } @@ -54,7 +54,7 @@ public abstract class AbstractConnector implements IConnector { } @Override - public boolean uploadPersonalNote(Geocache cache) { + public boolean uploadPersonalNote(@NonNull final Geocache cache) { throw new UnsupportedOperationException(); } @@ -64,7 +64,7 @@ public abstract class AbstractConnector implements IConnector { } @Override - public boolean uploadModifiedCoordinates(Geocache cache, Geopoint wpt) { + public boolean uploadModifiedCoordinates(@NonNull final Geocache cache, @NonNull final Geopoint wpt) { throw new UnsupportedOperationException(); } @@ -72,12 +72,12 @@ public abstract class AbstractConnector implements IConnector { * {@link IConnector} */ @Override - public boolean deleteModifiedCoordinates(Geocache cache) { + public boolean deleteModifiedCoordinates(@NonNull final Geocache cache) { throw new UnsupportedOperationException(); } @Override - public boolean supportsFavoritePoints(final Geocache cache) { + public boolean supportsFavoritePoints(@NonNull final Geocache cache) { return false; } @@ -92,36 +92,38 @@ public abstract class AbstractConnector implements IConnector { } @Override - public boolean canLog(Geocache cache) { + public boolean canLog(@NonNull final Geocache cache) { return false; } @Override - public ILoggingManager getLoggingManager(final LogCacheActivity activity, final Geocache cache) { + @NonNull + public ILoggingManager getLoggingManager(@NonNull final LogCacheActivity activity, @NonNull final Geocache cache) { return new NoLoggingManager(); } @Override + @NonNull public String getLicenseText(final @NonNull Geocache cache) { - return null; + return StringUtils.EMPTY; } protected static boolean isNumericId(final String str) { try { return Integer.parseInt(str) > 0; - } catch (NumberFormatException ignored) { + } catch (final NumberFormatException ignored) { } return false; } @Override - public boolean isZippedGPXFile(String fileName) { + public boolean isZippedGPXFile(@NonNull final String fileName) { // don't accept any file by default return false; } @Override - public boolean isReliableLatLon(boolean cacheHasReliableLatLon) { + public boolean isReliableLatLon(final boolean cacheHasReliableLatLon) { // let every cache have reliable coordinates by default return true; } @@ -129,9 +131,8 @@ public abstract class AbstractConnector implements IConnector { @Override public String getGeocodeFromUrl(final String url) { final String urlPrefix = getCacheUrlPrefix(); - if (StringUtils.startsWith(url, urlPrefix)) { - @NonNull - String geocode = url.substring(urlPrefix.length()); + if (StringUtils.isEmpty(urlPrefix) || StringUtils.startsWith(url, urlPrefix)) { + @NonNull final String geocode = url.substring(urlPrefix.length()); if (canHandle(geocode)) { return geocode; } @@ -139,9 +140,11 @@ public abstract class AbstractConnector implements IConnector { return null; } + @NonNull abstract protected String getCacheUrlPrefix(); @Override + @NonNull public String getLongCacheUrl(final @NonNull Geocache cache) { return getCacheUrl(cache); } @@ -152,7 +155,7 @@ public abstract class AbstractConnector implements IConnector { } @Override - public int getCacheMapMarkerId(boolean disabled) { + public int getCacheMapMarkerId(final boolean disabled) { if (disabled) { return R.drawable.marker_disabled_other; } @@ -160,7 +163,8 @@ public abstract class AbstractConnector implements IConnector { } @Override - public List<LogType> getPossibleLogTypes(Geocache geocache) { + @NonNull + public List<LogType> getPossibleLogTypes(@NonNull final Geocache geocache) { final List<LogType> logTypes = new ArrayList<>(); if (geocache.isEventCache()) { logTypes.add(LogType.WILL_ATTEND); @@ -197,13 +201,14 @@ public abstract class AbstractConnector implements IConnector { } @Override - public String getWaypointGpxId(String prefix, String geocode) { + public String getWaypointGpxId(final String prefix, final String geocode) { // Default: just return the prefix return prefix; } @Override - public String getWaypointPrefix(String name) { + @NonNull + public String getWaypointPrefix(final String name) { // Default: just return the name return name; } @@ -214,8 +219,9 @@ public abstract class AbstractConnector implements IConnector { } @Override + @NonNull public final Collection<String> getCapabilities() { - ArrayList<String> list = new ArrayList<>(); + final ArrayList<String> list = new ArrayList<>(); addCapability(list, ISearchByViewPort.class, R.string.feature_search_live_map); addCapability(list, ISearchByKeyword.class, R.string.feature_search_keyword); addCapability(list, ISearchByCenter.class, R.string.feature_search_center); @@ -246,20 +252,20 @@ public abstract class AbstractConnector implements IConnector { } } - private static String feature(int featureResourceId) { + private static String feature(final int featureResourceId) { return CgeoApplication.getInstance().getString(featureResourceId); } @Override public @NonNull List<UserAction> getUserActions() { - List<UserAction> actions = getDefaultUserActions(); + final List<UserAction> actions = getDefaultUserActions(); if (this instanceof ISearchByOwner) { actions.add(new UserAction(R.string.user_menu_view_hidden, new Action1<Context>() { @Override - public void call(Context context) { + public void call(final Context context) { CacheListActivity.startActivityOwner(context.activity, context.userName); } })); @@ -269,7 +275,7 @@ public abstract class AbstractConnector implements IConnector { actions.add(new UserAction(R.string.user_menu_view_found, new Action1<UserAction.Context>() { @Override - public void call(Context context) { + public void call(final Context context) { CacheListActivity.startActivityFinder(context.activity, context.userName); } })); @@ -287,7 +293,7 @@ public abstract class AbstractConnector implements IConnector { actions.add(new UserAction(R.string.user_menu_open_contact, new Action1<UserAction.Context>() { @Override - public void call(Context context) { + public void call(final Context context) { ContactsAddon.openContactCard(context.activity, context.userName); } })); diff --git a/main/src/cgeo/geocaching/connector/AbstractLoggingManager.java b/main/src/cgeo/geocaching/connector/AbstractLoggingManager.java index 9e702c4..e53fcf1 100644 --- a/main/src/cgeo/geocaching/connector/AbstractLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/AbstractLoggingManager.java @@ -2,6 +2,8 @@ package cgeo.geocaching.connector; import cgeo.geocaching.TrackableLog; +import org.eclipse.jdt.annotation.NonNull; + import java.util.Collections; import java.util.List; @@ -13,6 +15,7 @@ public abstract class AbstractLoggingManager implements ILoggingManager { } @Override + @NonNull public List<TrackableLog> getTrackables() { return Collections.emptyList(); } diff --git a/main/src/cgeo/geocaching/connector/AbstractLogin.java b/main/src/cgeo/geocaching/connector/AbstractLogin.java index b40ed4f..252daeb 100644 --- a/main/src/cgeo/geocaching/connector/AbstractLogin.java +++ b/main/src/cgeo/geocaching/connector/AbstractLogin.java @@ -8,6 +8,7 @@ import cgeo.geocaching.network.Network; import cgeo.geocaching.settings.Settings; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; public abstract class AbstractLogin { @@ -39,7 +40,7 @@ public abstract class AbstractLogin { return actualLoginStatus; } - protected void setActualLoginStatus(boolean loginStatus) { + protected void setActualLoginStatus(final boolean loginStatus) { actualLoginStatus = loginStatus; } @@ -47,7 +48,7 @@ public abstract class AbstractLogin { return actualUserName; } - protected void setActualUserName(String userName) { + protected void setActualUserName(final String userName) { actualUserName = userName; } @@ -69,6 +70,7 @@ public abstract class AbstractLogin { setActualStatus(CgeoApplication.getInstance().getString(R.string.err_login)); } + @NonNull public StatusCode login() { if (!Network.isNetworkConnected()) { return StatusCode.COMMUNICATION_ERROR; @@ -76,6 +78,7 @@ public abstract class AbstractLogin { return login(true); } + @NonNull protected abstract StatusCode login(boolean retry); } diff --git a/main/src/cgeo/geocaching/connector/ConnectorFactory.java b/main/src/cgeo/geocaching/connector/ConnectorFactory.java index d4d051f..b78b009 100644 --- a/main/src/cgeo/geocaching/connector/ConnectorFactory.java +++ b/main/src/cgeo/geocaching/connector/ConnectorFactory.java @@ -36,8 +36,8 @@ import java.util.Collections; import java.util.List; public final class ConnectorFactory { - private static final @NonNull UnknownConnector UNKNOWN_CONNECTOR = new UnknownConnector(); - private static final Collection<IConnector> CONNECTORS = Collections.unmodifiableCollection(Arrays.asList(new IConnector[] { + @NonNull private static final UnknownConnector UNKNOWN_CONNECTOR = new UnknownConnector(); + @NonNull private static final Collection<IConnector> CONNECTORS = Collections.unmodifiableCollection(Arrays.asList(new IConnector[] { GCConnector.getInstance(), ECConnector.getInstance(), new OCApiLiveConnector("opencaching.de", "www.opencaching.de", "OC", "CC BY-NC-ND, alle Logeinträge © jeweiliger Autor", @@ -71,22 +71,30 @@ public final class ConnectorFactory { })); @NonNull public static final UnknownTrackableConnector UNKNOWN_TRACKABLE_CONNECTOR = new UnknownTrackableConnector(); + + @NonNull private static final Collection<TrackableConnector> TRACKABLE_CONNECTORS = Collections.unmodifiableCollection(Arrays.asList(new TrackableConnector[] { new GeokretyConnector(), // GK must be first, as it overlaps with the secret codes of travel bugs TravelBugConnector.getInstance(), UNKNOWN_TRACKABLE_CONNECTOR // must be last })); + @NonNull private static final Collection<ISearchByViewPort> searchByViewPortConns = getMatchingConnectors(ISearchByViewPort.class); + @NonNull private static final Collection<ISearchByCenter> searchByCenterConns = getMatchingConnectors(ISearchByCenter.class); + @NonNull private static final Collection<ISearchByKeyword> searchByKeywordConns = getMatchingConnectors(ISearchByKeyword.class); + @NonNull private static final Collection<ISearchByOwner> SEARCH_BY_OWNER_CONNECTORS = getMatchingConnectors(ISearchByOwner.class); + @NonNull private static final Collection<ISearchByFinder> SEARCH_BY_FINDER_CONNECTORS = getMatchingConnectors(ISearchByFinder.class); + @NonNull @SuppressWarnings("unchecked") private static <T extends IConnector> Collection<T> getMatchingConnectors(final Class<T> clazz) { final List<T> matching = new ArrayList<>(); @@ -98,26 +106,32 @@ public final class ConnectorFactory { return Collections.unmodifiableCollection(matching); } + @NonNull public static Collection<IConnector> getConnectors() { return CONNECTORS; } + @NonNull public static Collection<ISearchByCenter> getSearchByCenterConnectors() { return searchByCenterConns; } + @NonNull public static Collection<ISearchByKeyword> getSearchByKeywordConnectors() { return searchByKeywordConns; } + @NonNull public static Collection<ISearchByOwner> getSearchByOwnerConnectors() { return SEARCH_BY_OWNER_CONNECTORS; } + @NonNull public static Collection<ISearchByFinder> getSearchByFinderConnectors() { return SEARCH_BY_FINDER_CONNECTORS; } + @NonNull public static ILogin[] getActiveLiveConnectors() { final List<ILogin> liveConns = new ArrayList<>(); for (final IConnector conn : CONNECTORS) { @@ -143,11 +157,12 @@ public final class ConnectorFactory { return false; } - public static @NonNull - IConnector getConnector(final Geocache cache) { + @NonNull + public static IConnector getConnector(final Geocache cache) { return getConnector(cache.getGeocode()); } + @NonNull public static TrackableConnector getConnector(final Trackable trackable) { return getTrackableConnector(trackable.getGeocode()); } @@ -162,8 +177,8 @@ public final class ConnectorFactory { return UNKNOWN_TRACKABLE_CONNECTOR; // avoid null checks by returning a non implementing connector } - public static @NonNull - IConnector getConnector(final String geocodeInput) { + @NonNull + public static IConnector getConnector(final String geocodeInput) { // this may come from user input final String geocode = StringUtils.trim(geocodeInput); if (geocode == null) { @@ -186,7 +201,8 @@ public final class ConnectorFactory { } /** @see ISearchByViewPort#searchByViewport */ - public static SearchResult searchByViewport(final @NonNull Viewport viewport, final MapTokens tokens) { + @NonNull + public static SearchResult searchByViewport(final @NonNull Viewport viewport, @NonNull final MapTokens tokens) { return SearchResult.parallelCombineActive(searchByViewPortConns, new Func1<ISearchByViewPort, SearchResult>() { @Override public SearchResult call(final ISearchByViewPort connector) { @@ -195,6 +211,7 @@ public final class ConnectorFactory { }); } + @Nullable public static String getGeocodeFromURL(final String url) { for (final IConnector connector : CONNECTORS) { final String geocode = connector.getGeocodeFromUrl(url); @@ -205,6 +222,7 @@ public final class ConnectorFactory { return null; } + @NonNull public static Collection<TrackableConnector> getTrackableConnectors() { return TRACKABLE_CONNECTORS; } @@ -215,6 +233,7 @@ public final class ConnectorFactory { * @param url * @return {@code null} if the URL cannot be decoded */ + @Nullable public static String getTrackableFromURL(final String url) { if (url == null) { return null; diff --git a/main/src/cgeo/geocaching/connector/GeocachingAustraliaConnector.java b/main/src/cgeo/geocaching/connector/GeocachingAustraliaConnector.java index 5535472..9377d6d 100644 --- a/main/src/cgeo/geocaching/connector/GeocachingAustraliaConnector.java +++ b/main/src/cgeo/geocaching/connector/GeocachingAustraliaConnector.java @@ -8,22 +8,25 @@ import org.eclipse.jdt.annotation.NonNull; class GeocachingAustraliaConnector extends AbstractConnector { @Override + @NonNull public String getName() { return "Geocaching Australia"; } @Override + @NonNull public String getCacheUrl(final @NonNull Geocache cache) { return getCacheUrlPrefix() + cache.getGeocode(); } @Override + @NonNull public String getHost() { return "geocaching.com.au"; } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { return false; } @@ -33,6 +36,7 @@ class GeocachingAustraliaConnector extends AbstractConnector { } @Override + @NonNull protected String getCacheUrlPrefix() { return "http://" + getHost() + "/cache/"; } diff --git a/main/src/cgeo/geocaching/connector/GeopeitusConnector.java b/main/src/cgeo/geocaching/connector/GeopeitusConnector.java index 0dc7a44..4340cac 100644 --- a/main/src/cgeo/geocaching/connector/GeopeitusConnector.java +++ b/main/src/cgeo/geocaching/connector/GeopeitusConnector.java @@ -8,22 +8,25 @@ import org.eclipse.jdt.annotation.NonNull; class GeopeitusConnector extends AbstractConnector { @Override + @NonNull public String getName() { return "geopeitus.ee"; } @Override + @NonNull public String getCacheUrl(final @NonNull Geocache cache) { return getCacheUrlPrefix() + StringUtils.stripStart(cache.getGeocode().substring(2), "0"); } @Override + @NonNull public String getHost() { return "www.geopeitus.ee"; } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { return false; } @@ -33,6 +36,7 @@ class GeopeitusConnector extends AbstractConnector { } @Override + @NonNull protected String getCacheUrlPrefix() { return "http://" + getHost() + "/aare/"; } diff --git a/main/src/cgeo/geocaching/connector/IConnector.java b/main/src/cgeo/geocaching/connector/IConnector.java index 9c1e8fc..0863723 100644 --- a/main/src/cgeo/geocaching/connector/IConnector.java +++ b/main/src/cgeo/geocaching/connector/IConnector.java @@ -16,6 +16,7 @@ public interface IConnector { * * @return */ + @NonNull public String getName(); /** @@ -33,6 +34,7 @@ public interface IConnector { * @param cache * @return */ + @NonNull public String getCacheUrl(final @NonNull Geocache cache); /** @@ -41,6 +43,7 @@ public interface IConnector { * @param cache * @return */ + @NonNull public String getLongCacheUrl(final @NonNull Geocache cache); /** @@ -56,7 +59,7 @@ public interface IConnector { * @param cache * @return True - success/False - failure */ - public boolean addToWatchlist(Geocache cache); + public boolean addToWatchlist(@NonNull Geocache cache); /** * Remove the cache from the watchlist @@ -64,14 +67,14 @@ public interface IConnector { * @param cache * @return True - success/False - failure */ - public boolean removeFromWatchlist(Geocache cache); + public boolean removeFromWatchlist(@NonNull Geocache cache); /** * enable/disable favorite points controls in cache details * * @return */ - public boolean supportsFavoritePoints(final Geocache cache); + public boolean supportsFavoritePoints(@NonNull final Geocache cache); /** * enable/disable logging controls in cache details @@ -92,13 +95,15 @@ public interface IConnector { * * @return */ - public ILoggingManager getLoggingManager(final LogCacheActivity activity, final Geocache cache); + @NonNull + public ILoggingManager getLoggingManager(@NonNull final LogCacheActivity activity, @NonNull final Geocache cache); /** * Get host name of the connector server for dynamic loading of data. * * @return */ + @NonNull public String getHost(); /** @@ -107,6 +112,7 @@ public interface IConnector { * @param cache * @return */ + @NonNull public String getLicenseText(final @NonNull Geocache cache); /** @@ -115,7 +121,7 @@ public interface IConnector { * @param fileName * @return */ - public boolean isZippedGPXFile(final String fileName); + public boolean isZippedGPXFile(@NonNull final String fileName); /** * return true if coordinates of a cache are reliable. only implemented by GC connector @@ -147,7 +153,7 @@ public interface IConnector { * @param cache * @return success */ - public boolean uploadPersonalNote(Geocache cache); + public boolean uploadPersonalNote(@NonNull Geocache cache); /** * enable/disable uploading modified coordinates to website @@ -162,7 +168,7 @@ public interface IConnector { * @param cache * @return success */ - public boolean deleteModifiedCoordinates(Geocache cache); + public boolean deleteModifiedCoordinates(@NonNull Geocache cache); /** * Uploading modified coordinates to website @@ -171,7 +177,7 @@ public interface IConnector { * @param wpt * @return success */ - public boolean uploadModifiedCoordinates(Geocache cache, Geopoint wpt); + public boolean uploadModifiedCoordinates(@NonNull Geocache cache, @NonNull Geopoint wpt); /** * Return {@code true} if this connector is active for online interaction (download details, do searches, ...). If @@ -188,7 +194,7 @@ public interface IConnector { * @param cache a cache that this connector must be able to handle * @return <code>true</code> if the current user is the cache owner, <code>false</code> otherwise */ - public boolean isOwner(final Geocache cache); + public boolean isOwner(@NonNull final Geocache cache); /** * Check if the cache information is complete enough to be @@ -197,7 +203,7 @@ public interface IConnector { * @param geocache * @return */ - public boolean canLog(Geocache geocache); + public boolean canLog(@NonNull Geocache geocache); /** * Return the marker id of the caches for this connector. This creates the different backgrounds for cache markers @@ -215,7 +221,8 @@ public interface IConnector { * @param geocache * @return */ - public List<LogType> getPossibleLogTypes(Geocache geocache); + @NonNull + public List<LogType> getPossibleLogTypes(@NonNull Geocache geocache); /** * Get the GPX id for a waypoint when exporting. For some connectors there is an inherent name logic, @@ -232,6 +239,7 @@ public interface IConnector { * @param name * @return */ + @NonNull public String getWaypointPrefix(String name); /** @@ -246,8 +254,9 @@ public interface IConnector { * * @return */ + @NonNull public Collection<String> getCapabilities(); - public @NonNull - List<UserAction> getUserActions(); + @NonNull + public List<UserAction> getUserActions(); } diff --git a/main/src/cgeo/geocaching/connector/ILoggingManager.java b/main/src/cgeo/geocaching/connector/ILoggingManager.java index a8caf69..40a5377 100644 --- a/main/src/cgeo/geocaching/connector/ILoggingManager.java +++ b/main/src/cgeo/geocaching/connector/ILoggingManager.java @@ -3,6 +3,9 @@ package cgeo.geocaching.connector; import cgeo.geocaching.TrackableLog; import cgeo.geocaching.enumerations.LogType; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + import android.net.Uri; import java.util.Calendar; @@ -12,7 +15,7 @@ public interface ILoggingManager { /** * Post a log for a cache online - * + * * @param logType * @param date * @param log @@ -21,12 +24,14 @@ public interface ILoggingManager { * @param trackableLogs * @return */ - LogResult postLog(LogType logType, - Calendar date, - String log, - String logPassword, - List<TrackableLog> trackableLogs); - + @NonNull + LogResult postLog(@NonNull LogType logType, + @NonNull Calendar date, + @NonNull String log, + @Nullable String logPassword, + @NonNull List<TrackableLog> trackableLogs); + + @NonNull ImageResult postLogImage(String logId, String imageCaption, String imageDescription, @@ -34,8 +39,10 @@ public interface ILoggingManager { public boolean hasLoaderError(); + @NonNull public List<TrackableLog> getTrackables(); + @NonNull 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 index 9314cad..ec11a6d 100644 --- a/main/src/cgeo/geocaching/connector/ImageResult.java +++ b/main/src/cgeo/geocaching/connector/ImageResult.java @@ -2,20 +2,26 @@ package cgeo.geocaching.connector; import cgeo.geocaching.enumerations.StatusCode; +import org.eclipse.jdt.annotation.NonNull; + public class ImageResult { + @NonNull private final StatusCode postResult; + @NonNull private final String imageUri; - public ImageResult(StatusCode postResult, String imageUri) { + public ImageResult(@NonNull final StatusCode postResult, @NonNull final String imageUri) { this.postResult = postResult; this.imageUri = imageUri; } + @NonNull public StatusCode getPostResult() { return postResult; } + @NonNull public String getImageUri() { return imageUri; } diff --git a/main/src/cgeo/geocaching/connector/LogResult.java b/main/src/cgeo/geocaching/connector/LogResult.java index 62111a4..9d0cb61 100644 --- a/main/src/cgeo/geocaching/connector/LogResult.java +++ b/main/src/cgeo/geocaching/connector/LogResult.java @@ -2,20 +2,26 @@ package cgeo.geocaching.connector; import cgeo.geocaching.enumerations.StatusCode; +import org.eclipse.jdt.annotation.NonNull; + public class LogResult { + @NonNull private final StatusCode postLogResult; + @NonNull private final String logId; - public LogResult(StatusCode postLogResult, String logId) { + public LogResult(@NonNull final StatusCode postLogResult, @NonNull final String logId) { this.postLogResult = postLogResult; this.logId = logId; } + @NonNull public StatusCode getPostLogResult() { return postLogResult; } + @NonNull public String getLogId() { return logId; } diff --git a/main/src/cgeo/geocaching/connector/NoLoggingManager.java b/main/src/cgeo/geocaching/connector/NoLoggingManager.java index ff8b33a..103db68 100644 --- a/main/src/cgeo/geocaching/connector/NoLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/NoLoggingManager.java @@ -4,6 +4,9 @@ import cgeo.geocaching.TrackableLog; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.StatusCode; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + import android.net.Uri; import java.util.Calendar; @@ -18,11 +21,13 @@ class NoLoggingManager extends AbstractLoggingManager { } @Override - public LogResult postLog(final LogType logType, final Calendar date, final String log, final String logPassword, final List<TrackableLog> trackableLogs) { + @NonNull + public LogResult postLog(@NonNull final LogType logType, @NonNull final Calendar date, @NonNull final String log, @Nullable final String logPassword, @NonNull final List<TrackableLog> trackableLogs) { return new LogResult(StatusCode.LOG_POST_ERROR, ""); } @Override + @NonNull public ImageResult postLogImage(final String logId, final String imageCaption, final String imageDescription, final Uri imageUri) { return new ImageResult(StatusCode.LOG_POST_ERROR, ""); } @@ -33,6 +38,7 @@ class NoLoggingManager extends AbstractLoggingManager { } @Override + @NonNull public List<LogType> getPossibleLogTypes() { return Collections.emptyList(); } diff --git a/main/src/cgeo/geocaching/connector/UnknownConnector.java b/main/src/cgeo/geocaching/connector/UnknownConnector.java index a88f2c5..8ed1da4 100644 --- a/main/src/cgeo/geocaching/connector/UnknownConnector.java +++ b/main/src/cgeo/geocaching/connector/UnknownConnector.java @@ -8,22 +8,25 @@ import org.eclipse.jdt.annotation.NonNull; class UnknownConnector extends AbstractConnector { @Override + @NonNull public String getName() { return "Unknown caches"; } @Override + @NonNull public String getCacheUrl(@NonNull final Geocache cache) { - return null; // we have no url for these caches + throw new IllegalStateException("getCacheUrl cannot be called on unknown caches"); } @Override + @NonNull public String getHost() { - return null; // we have no host for these caches + return StringUtils.EMPTY; // we have no host for these caches } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { return false; } @@ -33,7 +36,13 @@ class UnknownConnector extends AbstractConnector { } @Override + @NonNull protected String getCacheUrlPrefix() { + throw new IllegalStateException("getCacheUrl cannot be called on unknown caches"); + } + + @Override + public String getGeocodeFromUrl(final String url) { return null; } diff --git a/main/src/cgeo/geocaching/connector/UserAction.java b/main/src/cgeo/geocaching/connector/UserAction.java index e9ee4a3..082e32c 100644 --- a/main/src/cgeo/geocaching/connector/UserAction.java +++ b/main/src/cgeo/geocaching/connector/UserAction.java @@ -1,6 +1,7 @@ package cgeo.geocaching.connector; import org.eclipse.jdt.annotation.NonNull; + import rx.functions.Action1; import android.app.Activity; @@ -8,25 +9,26 @@ import android.app.Activity; public class UserAction { public static class Context { + @NonNull public final String userName; + @NonNull public final Activity activity; - public Context(String userName, Activity activity) { + public Context(@NonNull final String userName, @NonNull final Activity activity) { this.userName = userName; this.activity = activity; } } public final int displayResourceId; - private final @NonNull - Action1<Context> runnable; + @NonNull private final Action1<Context> runnable; - public UserAction(int displayResourceId, final @NonNull Action1<Context> runnable) { + public UserAction(final int displayResourceId, final @NonNull Action1<Context> runnable) { this.displayResourceId = displayResourceId; this.runnable = runnable; } - public void run(Context context) { + public void run(@NonNull final Context context) { runnable.call(context); } } diff --git a/main/src/cgeo/geocaching/connector/WaymarkingConnector.java b/main/src/cgeo/geocaching/connector/WaymarkingConnector.java index 2ed54a0..5a6f362 100644 --- a/main/src/cgeo/geocaching/connector/WaymarkingConnector.java +++ b/main/src/cgeo/geocaching/connector/WaymarkingConnector.java @@ -8,27 +8,31 @@ import org.eclipse.jdt.annotation.NonNull; class WaymarkingConnector extends AbstractConnector { @Override + @NonNull public String getName() { return "Waymarking"; } @Override + @NonNull public String getCacheUrl(@NonNull final Geocache cache) { return getCacheUrlPrefix() + cache.getGeocode(); } @Override + @NonNull public String getHost() { return "www.waymarking.com"; } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { // this connector has no user management return false; } @Override + @NonNull protected String getCacheUrlPrefix() { return "http://" + getHost() + "/waymarks/"; } diff --git a/main/src/cgeo/geocaching/connector/capability/FieldNotesCapability.java b/main/src/cgeo/geocaching/connector/capability/FieldNotesCapability.java index 4da9705..dd2bc8d 100644 --- a/main/src/cgeo/geocaching/connector/capability/FieldNotesCapability.java +++ b/main/src/cgeo/geocaching/connector/capability/FieldNotesCapability.java @@ -2,6 +2,8 @@ package cgeo.geocaching.connector.capability; import cgeo.geocaching.connector.IConnector; +import org.eclipse.jdt.annotation.NonNull; + import java.io.File; /** @@ -9,5 +11,5 @@ import java.io.File; * */ public interface FieldNotesCapability extends IConnector { - public boolean uploadFieldNotes(final File exportFile); + public boolean uploadFieldNotes(@NonNull final File exportFile); } diff --git a/main/src/cgeo/geocaching/connector/capability/ILogin.java b/main/src/cgeo/geocaching/connector/capability/ILogin.java index b8b4975..003ccf4 100644 --- a/main/src/cgeo/geocaching/connector/capability/ILogin.java +++ b/main/src/cgeo/geocaching/connector/capability/ILogin.java @@ -7,13 +7,10 @@ import android.os.Handler; public interface ILogin extends IConnector { - /** - * Contacts the server the connector belongs to - * and verifies/establishes authentication and - * retrieves information about the current user - * (Name, found caches) if applicable. - * + * Contacts the server the connector belongs to and verifies/establishes authentication and retrieves information + * about the current user (Name, found caches) if applicable. + * * @param handler * Handler to receive status feedback * @param fromActivity @@ -28,7 +25,7 @@ public interface ILogin extends IConnector { void logout(); /** - * Returns the status of the last {@link}login() request + * Returns the status of the last {@link #login(Handler, Context)} request. * * @return */ @@ -42,18 +39,17 @@ public interface ILogin extends IConnector { String getLoginStatusString(); /** - * Name the user has in this connector or empty string if not applicable - * It might be necessary to execute login before this information is valid. + * Name the user has in this connector or empty string if not applicable. + * It might be necessary to execute {@link #login(Handler, Context)} before this information is valid. * * @return */ String getUserName(); /** - * Number of caches the user has found in this connector - * Normally retrieved/updated with (@see login). - * Might be out dated as changes on the connectors site - * are generally not notified. + * Number of caches the user has found in this connector. + * Normally retrieved/updated with {@link #login(Handler, Context)}. + * Might be stale as changes on the connectors site are generally not notified. * * @return */ diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java b/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java index ef81c4c..0137c3b 100644 --- a/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java @@ -8,5 +8,6 @@ import cgeo.geocaching.location.Viewport; import org.eclipse.jdt.annotation.NonNull; public interface ISearchByViewPort extends IConnector { - public SearchResult searchByViewport(final @NonNull Viewport viewport, final MapTokens tokens); + @NonNull + public SearchResult searchByViewport(final @NonNull Viewport viewport, @NonNull final MapTokens tokens); } diff --git a/main/src/cgeo/geocaching/connector/ec/ECApi.java b/main/src/cgeo/geocaching/connector/ec/ECApi.java index 4d4ca76..86f7717 100644 --- a/main/src/cgeo/geocaching/connector/ec/ECApi.java +++ b/main/src/cgeo/geocaching/connector/ec/ECApi.java @@ -24,6 +24,8 @@ import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import java.io.IOException; import java.util.ArrayList; @@ -37,9 +39,13 @@ import java.util.TimeZone; final class ECApi { + @NonNull private static final String API_HOST = "https://extremcaching.com/exports/"; + @NonNull private static final ECLogin ecLogin = ECLogin.getInstance(); + + @NonNull private static final SynchronizedDateFormat LOG_DATE_FORMAT = new SynchronizedDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ", TimeZone.getTimeZone("UTC"), Locale.US); private ECApi() { @@ -50,6 +56,7 @@ final class ECApi { return StringUtils.removeStartIgnoreCase(geocode, "EC"); } + @Nullable static Geocache searchByGeoCode(final String geocode) { final Parameters params = new Parameters("id", getIdFromGeocode(geocode)); final HttpResponse response = apiRequest("gpx.php", params); @@ -61,6 +68,7 @@ final class ECApi { return null; } + @NonNull static Collection<Geocache> searchByBBox(final Viewport viewport) { if (viewport.getLatitudeSpan() == 0 || viewport.getLongitudeSpan() == 0) { @@ -77,7 +85,7 @@ final class ECApi { return importCachesFromJSON(response); } - + @NonNull static Collection<Geocache> searchByCenter(final Geopoint center) { final Parameters params = new Parameters("fnc", "center"); @@ -89,11 +97,13 @@ final class ECApi { return importCachesFromJSON(response); } - static LogResult postLog(final Geocache cache, final LogType logType, final Calendar date, final String log) { + @NonNull + static LogResult postLog(@NonNull final Geocache cache, @NonNull final LogType logType, @NonNull final Calendar date, @NonNull final String log) { return postLog(cache, logType, date, log, false); } - private static LogResult postLog(final Geocache cache, final LogType logType, final Calendar date, final String log, final boolean isRetry) { + @NonNull + private static LogResult postLog(@NonNull final Geocache cache, @NonNull final LogType logType, @NonNull final Calendar date, @NonNull final String log, final boolean isRetry) { final Parameters params = new Parameters("cache_id", cache.getGeocode()); params.add("type", logType.type); params.add("log", log); @@ -127,15 +137,17 @@ final class ECApi { return new LogResult(StatusCode.LOG_POST_ERROR, ""); } - + @Nullable private static HttpResponse apiRequest(final Parameters params) { return apiRequest("api.php", params); } + @Nullable private static HttpResponse apiRequest(final String uri, final Parameters params) { return apiRequest(uri, params, false); } + @Nullable private static HttpResponse apiRequest(final String uri, final Parameters params, final boolean isRetry) { // add session and cgeo marker on every request if (!isRetry) { @@ -160,6 +172,7 @@ final class ECApi { return response; } + @NonNull private static Collection<Geocache> importCachesFromGPXResponse(final HttpResponse response) { if (response == null) { return Collections.emptyList(); @@ -173,6 +186,7 @@ final class ECApi { } } + @NonNull private static List<Geocache> importCachesFromJSON(final HttpResponse response) { if (response != null) { try { @@ -196,6 +210,7 @@ final class ECApi { return Collections.emptyList(); } + @Nullable private static Geocache parseCache(final JsonNode response) { try { final Geocache cache = new Geocache(); @@ -216,6 +231,7 @@ final class ECApi { } } + @NonNull private static CacheType getCacheType(final String cacheType) { if (cacheType.equalsIgnoreCase("Tradi")) { return CacheType.TRADITIONAL; diff --git a/main/src/cgeo/geocaching/connector/ec/ECConnector.java b/main/src/cgeo/geocaching/connector/ec/ECConnector.java index e884f85..15c2dc2 100644 --- a/main/src/cgeo/geocaching/connector/ec/ECConnector.java +++ b/main/src/cgeo/geocaching/connector/ec/ECConnector.java @@ -36,13 +36,18 @@ import java.util.regex.Pattern; public class ECConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort, ILogin, ICredentials { + @NonNull private static final String CACHE_URL = "http://extremcaching.com/index.php/output-2/"; /** * Pattern for EC codes */ + @NonNull private final static Pattern PATTERN_EC_CODE = Pattern.compile("EC[0-9]+", Pattern.CASE_INSENSITIVE); + private final CgeoApplication app = CgeoApplication.getInstance(); + + @NonNull private final ECLogin ecLogin = ECLogin.getInstance(); private ECConnector() { @@ -56,27 +61,30 @@ public class ECConnector extends AbstractConnector implements ISearchByGeocode, private static final @NonNull ECConnector INSTANCE = new ECConnector(); } - public static @NonNull - ECConnector getInstance() { + @NonNull + public static ECConnector getInstance() { return Holder.INSTANCE; } @Override - public boolean canHandle(@NonNull String geocode) { + public boolean canHandle(@NonNull final String geocode) { return ECConnector.PATTERN_EC_CODE.matcher(geocode).matches(); } @Override - public String getCacheUrl(@NonNull Geocache cache) { + @NonNull + public String getCacheUrl(@NonNull final Geocache cache) { return CACHE_URL + cache.getGeocode().replace("EC", ""); } @Override + @NonNull public String getName() { return "extremcaching.com"; } @Override + @NonNull public String getHost() { return "extremcaching.com"; } @@ -94,31 +102,28 @@ public class ECConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public SearchResult searchByViewport(@NonNull Viewport viewport, final MapTokens tokens) { + @NonNull + public SearchResult searchByViewport(@NonNull final Viewport viewport, @NonNull final MapTokens tokens) { final Collection<Geocache> caches = ECApi.searchByBBox(viewport); - if (caches == null) { - return null; - } final SearchResult searchResult = new SearchResult(caches); return searchResult.filterSearchResults(false, false, Settings.getCacheType()); } @Override - public SearchResult searchByCenter(@NonNull Geopoint center, final @NonNull RecaptchaReceiver recaptchaReceiver) { + @NonNull + public SearchResult searchByCenter(@NonNull final Geopoint center, final @NonNull RecaptchaReceiver recaptchaReceiver) { final Collection<Geocache> caches = ECApi.searchByCenter(center); - if (caches == null) { - return null; - } final SearchResult searchResult = new SearchResult(caches); return searchResult.filterSearchResults(false, false, Settings.getCacheType()); } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { return false; } @Override + @NonNull protected String getCacheUrlPrefix() { return CACHE_URL; } @@ -129,7 +134,7 @@ public class ECConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public boolean login(Handler handler, Context fromActivity) { + public boolean login(final Handler handler, final Context fromActivity) { // login final StatusCode status = ecLogin.login(); @@ -166,7 +171,7 @@ public class ECConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public int getCacheMapMarkerId(boolean disabled) { + public int getCacheMapMarkerId(final boolean disabled) { final String icons = Settings.getECIconSet(); if (StringUtils.equals(icons, "1")) { return disabled ? R.drawable.marker_disabled_other : R.drawable.marker_other; @@ -175,6 +180,7 @@ public class ECConnector extends AbstractConnector implements ISearchByGeocode, } @Override + @NonNull public String getLicenseText(final @NonNull Geocache cache) { // NOT TO BE TRANSLATED return "© " + cache.getOwnerDisplayName() + ", <a href=\"" + getCacheUrl(cache) + "\">" + getName() + "</a>, CC BY-NC-ND 3.0, alle Logeinträge © jeweiliger Autor"; @@ -186,17 +192,19 @@ public class ECConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public boolean canLog(Geocache cache) { + public boolean canLog(@NonNull final Geocache cache) { return true; } @Override - public ILoggingManager getLoggingManager(final LogCacheActivity activity, final Geocache cache) { + @NonNull + public ILoggingManager getLoggingManager(@NonNull final LogCacheActivity activity, @NonNull final Geocache cache) { return new ECLoggingManager(activity, this, cache); } @Override - public List<LogType> getPossibleLogTypes(Geocache geocache) { + @NonNull + public List<LogType> getPossibleLogTypes(@NonNull final Geocache geocache) { final List<LogType> logTypes = new ArrayList<>(); if (geocache.isEventCache()) { logTypes.add(LogType.WILL_ATTEND); diff --git a/main/src/cgeo/geocaching/connector/ec/ECLoggingManager.java b/main/src/cgeo/geocaching/connector/ec/ECLoggingManager.java index f380a6b..34c3d1b 100644 --- a/main/src/cgeo/geocaching/connector/ec/ECLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/ec/ECLoggingManager.java @@ -7,6 +7,10 @@ import cgeo.geocaching.connector.AbstractLoggingManager; import cgeo.geocaching.connector.ImageResult; import cgeo.geocaching.connector.LogResult; import cgeo.geocaching.enumerations.LogType; +import cgeo.geocaching.enumerations.StatusCode; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import android.net.Uri; @@ -15,11 +19,16 @@ import java.util.List; class ECLoggingManager extends AbstractLoggingManager { + @NonNull private final ECConnector connector; + + @NonNull private final Geocache cache; + + @NonNull private final LogCacheActivity activity; - ECLoggingManager(final LogCacheActivity activity, final ECConnector connector, final Geocache cache) { + ECLoggingManager(@NonNull final LogCacheActivity activity, @NonNull final ECConnector connector, @NonNull final Geocache cache) { this.connector = connector; this.cache = cache; this.activity = activity; @@ -31,16 +40,19 @@ class ECLoggingManager extends AbstractLoggingManager { } @Override - public final LogResult postLog(final LogType logType, final Calendar date, final String log, final String logPassword, final List<TrackableLog> trackableLogs) { + @NonNull + public final LogResult postLog(@NonNull final LogType logType, @NonNull final Calendar date, @NonNull final String log, @Nullable final String logPassword, @NonNull final List<TrackableLog> trackableLogs) { return ECApi.postLog(cache, logType, date, log); } @Override + @NonNull public final ImageResult postLogImage(final String logId, final String imageCaption, final String imageDescription, final Uri imageUri) { - return null; + return new ImageResult(StatusCode.LOG_POST_ERROR, ""); } @Override + @NonNull public List<LogType> getPossibleLogTypes() { return connector.getPossibleLogTypes(cache); } diff --git a/main/src/cgeo/geocaching/connector/ec/ECLogin.java b/main/src/cgeo/geocaching/connector/ec/ECLogin.java index 342cbc6..3ae8298 100644 --- a/main/src/cgeo/geocaching/connector/ec/ECLogin.java +++ b/main/src/cgeo/geocaching/connector/ec/ECLogin.java @@ -16,6 +16,7 @@ import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import java.io.IOException; @@ -30,14 +31,17 @@ public class ECLogin extends AbstractLogin { } private static class SingletonHolder { + @NonNull private final static ECLogin INSTANCE = new ECLogin(); } + @NonNull public static ECLogin getInstance() { return SingletonHolder.INSTANCE; } @Override + @NonNull protected StatusCode login(final boolean retry) { final ImmutablePair<String, String> login = Settings.getCredentials(ECConnector.getInstance()); diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java index 099fd79..e43b9b5 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java @@ -49,18 +49,22 @@ import java.util.regex.Pattern; public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort, ISearchByKeyword, ILogin, ICredentials, ISearchByOwner, ISearchByFinder, FieldNotesCapability { + @NonNull private static final String CACHE_URL_SHORT = "http://coord.info/"; // Double slash is used to force open in browser + @NonNull private static final String CACHE_URL_LONG = "http://www.geocaching.com/seek/cache_details.aspx?wp="; /** * Pocket queries downloaded from the website use a numeric prefix. The pocket query creator Android app adds a * verbatim "pocketquery" prefix. */ + @NonNull private static final Pattern GPX_ZIP_FILE_PATTERN = Pattern.compile("((\\d{7,})|(pocketquery))" + "(_.+)?" + "\\.zip", Pattern.CASE_INSENSITIVE); /** * Pattern for GC codes */ + @NonNull private final static Pattern PATTERN_GC_CODE = Pattern.compile("GC[0-9A-Z]+", Pattern.CASE_INSENSITIVE); private GCConnector() { @@ -74,23 +78,25 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, private static final @NonNull GCConnector INSTANCE = new GCConnector(); } - public static @NonNull - GCConnector getInstance() { + @NonNull + public static GCConnector getInstance() { return Holder.INSTANCE; } @Override - public boolean canHandle(@NonNull String geocode) { + public boolean canHandle(@NonNull final String geocode) { return GCConnector.PATTERN_GC_CODE.matcher(geocode).matches(); } @Override - public String getLongCacheUrl(@NonNull Geocache cache) { + @NonNull + public String getLongCacheUrl(@NonNull final Geocache cache) { return CACHE_URL_LONG + cache.getGeocode(); } @Override - public String getCacheUrl(@NonNull Geocache cache) { + @NonNull + public String getCacheUrl(@NonNull final Geocache cache) { return CACHE_URL_SHORT + cache.getGeocode(); } @@ -120,21 +126,24 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public ILoggingManager getLoggingManager(final LogCacheActivity activity, final Geocache cache) { + @NonNull + public ILoggingManager getLoggingManager(@NonNull final LogCacheActivity activity, @NonNull final Geocache cache) { return new GCLoggingManager(activity, cache); } @Override - public boolean canLog(Geocache cache) { + public boolean canLog(@NonNull final Geocache cache) { return StringUtils.isNotBlank(cache.getCacheId()); } @Override + @NonNull public String getName() { return "geocaching.com"; } @Override + @NonNull public String getHost() { return "www.geocaching.com"; } @@ -177,28 +186,29 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public SearchResult searchByViewport(@NonNull Viewport viewport, final MapTokens tokens) { + @NonNull + public SearchResult searchByViewport(@NonNull final Viewport viewport, @NonNull final MapTokens tokens) { return GCMap.searchByViewport(viewport, tokens); } @Override - public boolean isZippedGPXFile(final String fileName) { + public boolean isZippedGPXFile(@NonNull final String fileName) { return GPX_ZIP_FILE_PATTERN.matcher(fileName).matches(); } @Override - public boolean isReliableLatLon(boolean cacheHasReliableLatLon) { + public boolean isReliableLatLon(final boolean cacheHasReliableLatLon) { return cacheHasReliableLatLon; } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { final String user = Settings.getUsername(); return StringUtils.isNotEmpty(user) && StringUtils.equalsIgnoreCase(cache.getOwnerUserId(), user); } @Override - public boolean addToWatchlist(Geocache cache) { + public boolean addToWatchlist(@NonNull final Geocache cache) { final boolean added = GCParser.addToWatchlist(cache); if (added) { DataStore.saveChangedCache(cache); @@ -207,7 +217,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public boolean removeFromWatchlist(Geocache cache) { + public boolean removeFromWatchlist(@NonNull final Geocache cache) { final boolean removed = GCParser.removeFromWatchlist(cache); if (removed) { DataStore.saveChangedCache(cache); @@ -225,7 +235,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, * @return <code>true</code> if the cache was successfully added, <code>false</code> otherwise */ - public static boolean addToFavorites(Geocache cache) { + public static boolean addToFavorites(final Geocache cache) { final boolean added = GCParser.addToFavorites(cache); if (added) { DataStore.saveChangedCache(cache); @@ -243,7 +253,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, * @return <code>true</code> if the cache was successfully added, <code>false</code> otherwise */ - public static boolean removeFromFavorites(Geocache cache) { + public static boolean removeFromFavorites(final Geocache cache) { final boolean removed = GCParser.removeFromFavorites(cache); if (removed) { DataStore.saveChangedCache(cache); @@ -252,7 +262,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public boolean uploadModifiedCoordinates(Geocache cache, Geopoint wpt) { + public boolean uploadModifiedCoordinates(@NonNull final Geocache cache, @NonNull final Geopoint wpt) { final boolean uploaded = GCParser.uploadModifiedCoordinates(cache, wpt); if (uploaded) { DataStore.saveChangedCache(cache); @@ -261,7 +271,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public boolean deleteModifiedCoordinates(Geocache cache) { + public boolean deleteModifiedCoordinates(@NonNull final Geocache cache) { final boolean deleted = GCParser.deleteModifiedCoordinates(cache); if (deleted) { DataStore.saveChangedCache(cache); @@ -270,7 +280,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public boolean uploadPersonalNote(Geocache cache) { + public boolean uploadPersonalNote(@NonNull final Geocache cache) { final boolean uploaded = GCParser.uploadPersonalNote(cache); if (uploaded) { DataStore.saveChangedCache(cache); @@ -279,22 +289,23 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public SearchResult searchByCenter(@NonNull Geopoint center, final @NonNull RecaptchaReceiver recaptchaReceiver) { + public SearchResult searchByCenter(@NonNull final Geopoint center, final @NonNull RecaptchaReceiver recaptchaReceiver) { return GCParser.searchByCoords(center, Settings.getCacheType(), Settings.isShowCaptcha(), recaptchaReceiver); } @Override - public boolean supportsFavoritePoints(final Geocache cache) { + public boolean supportsFavoritePoints(@NonNull final Geocache cache) { return !cache.getType().isEvent(); } @Override + @NonNull protected String getCacheUrlPrefix() { - return null; // UNUSED + return StringUtils.EMPTY; // UNUSED } @Override - public String getGeocodeFromUrl(String url) { + public String getGeocodeFromUrl(final String url) { // coord.info URLs String code = StringUtils.substringAfterLast(url, "coord.info/"); if (code != null && canHandle(code)) { @@ -314,7 +325,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public int getCacheMapMarkerId(boolean disabled) { + public int getCacheMapMarkerId(final boolean disabled) { if (disabled) { return R.drawable.marker_disabled; } @@ -322,7 +333,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public boolean login(Handler handler, Context fromActivity) { + public boolean login(final Handler handler, final Context fromActivity) { // login final StatusCode status = GCLogin.getInstance().login(); @@ -364,7 +375,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public String getWaypointGpxId(String prefix, String geocode) { + public String getWaypointGpxId(final String prefix, final String geocode) { String gpxId = prefix; if (StringUtils.isNotBlank(geocode) && geocode.length() > 2) { gpxId += geocode.substring(2); @@ -373,7 +384,8 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public String getWaypointPrefix(String name) { + @NonNull + public String getWaypointPrefix(final String name) { String prefix = name; if (StringUtils.isNotBlank(prefix) && prefix.length() >= 2) { prefix = name.substring(0, 2); @@ -382,7 +394,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public SearchResult searchByKeyword(@NonNull String keyword, final @NonNull RecaptchaReceiver recaptchaReceiver) { + public SearchResult searchByKeyword(@NonNull final String keyword, final @NonNull RecaptchaReceiver recaptchaReceiver) { return GCParser.searchByKeyword(keyword, Settings.getCacheType(), Settings.isShowCaptcha(), recaptchaReceiver); } @@ -399,18 +411,18 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, @Override public @NonNull List<UserAction> getUserActions() { - List<UserAction> actions = super.getUserActions(); + final List<UserAction> actions = super.getUserActions(); actions.add(new UserAction(R.string.user_menu_open_browser, new Action1<UserAction.Context>() { @Override - public void call(cgeo.geocaching.connector.UserAction.Context context) { + public void call(final cgeo.geocaching.connector.UserAction.Context context) { context.activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/profile/?u=" + Network.encode(context.userName)))); } })); actions.add(new UserAction(R.string.user_menu_send_message, new Action1<UserAction.Context>() { @Override - public void call(cgeo.geocaching.connector.UserAction.Context context) { + public void call(final cgeo.geocaching.connector.UserAction.Context context) { try { context.activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/email/?u=" + Network.encode(context.userName)))); } catch (final ActivityNotFoundException e) { @@ -433,7 +445,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public boolean uploadFieldNotes(final File exportFile) { + public boolean uploadFieldNotes(@NonNull final File exportFile) { if (!GCLogin.getInstance().isActualLoginStatus()) { // no need to upload (possibly large file) if we're not logged in final StatusCode loginState = GCLogin.getInstance().login(); diff --git a/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java index 057d97b..bdea155 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java @@ -19,6 +19,7 @@ import cgeo.geocaching.utils.TextUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import android.net.Uri; @@ -36,7 +37,7 @@ class GCLoggingManager extends AbstractLoggingManager implements LoaderManager.L private final Geocache cache; private String[] viewstates; - private List<TrackableLog> trackables; + @NonNull private List<TrackableLog> trackables = Collections.emptyList(); private List<LogType> possibleLogTypes; private boolean hasLoaderError = true; @@ -92,7 +93,8 @@ class GCLoggingManager extends AbstractLoggingManager implements LoaderManager.L } @Override - public LogResult postLog(final LogType logType, final Calendar date, final String log, final String logPassword, final List<TrackableLog> trackableLogs) { + @NonNull + public LogResult postLog(@NonNull final LogType logType, @NonNull final Calendar date, @NonNull final String log, @Nullable final String logPassword, @NonNull final List<TrackableLog> trackableLogs) { try { final ImmutablePair<StatusCode, String> postResult = GCParser.postLog(cache.getGeocode(), cache.getCacheId(), viewstates, logType, @@ -115,6 +117,7 @@ class GCLoggingManager extends AbstractLoggingManager implements LoaderManager.L } @Override + @NonNull public ImageResult postLogImage(final String logId, final String imageCaption, final String imageDescription, final Uri imageUri) { if (StringUtils.isNotBlank(imageUri.getPath())) { @@ -133,6 +136,7 @@ class GCLoggingManager extends AbstractLoggingManager implements LoaderManager.L } @Override + @NonNull public List<TrackableLog> getTrackables() { if (hasLoaderError) { return Collections.emptyList(); @@ -141,6 +145,7 @@ class GCLoggingManager extends AbstractLoggingManager implements LoaderManager.L } @Override + @NonNull public List<LogType> getPossibleLogTypes() { if (hasLoaderError) { return Collections.emptyList(); diff --git a/main/src/cgeo/geocaching/connector/gc/GCLogin.java b/main/src/cgeo/geocaching/connector/gc/GCLogin.java index 3d4f2af..035176b 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCLogin.java +++ b/main/src/cgeo/geocaching/connector/gc/GCLogin.java @@ -57,6 +57,7 @@ public class GCLogin extends AbstractLogin { } @Override + @NonNull protected StatusCode login(final boolean retry) { final ImmutablePair<String, String> credentials = Settings.getGcCredentials(); final String username = credentials.left; @@ -256,7 +257,7 @@ public class GCLogin extends AbstractLogin { final String avatarURL = TextUtils.getMatch(profile, GCConstants.PATTERN_AVATAR_IMAGE_PROFILE_PAGE, false, null); if (avatarURL != null) { - final HtmlImage imgGetter = new HtmlImage("", false, 0, false); + final HtmlImage imgGetter = new HtmlImage(HtmlImage.SHARED, false, 0, false); return imgGetter.fetchDrawable(avatarURL.replace("avatar", "user/large")).cast(Drawable.class); } // No match? There may be no avatar set by user. @@ -470,8 +471,8 @@ public class GCLogin extends AbstractLogin { * * @return first is user session, second is session token */ - public @NonNull - MapTokens getMapTokens() { + @NonNull + public MapTokens getMapTokens() { final String data = getRequestLogged(GCConstants.URL_LIVE_MAP, null); final String userSession = TextUtils.getMatch(data, GCConstants.PATTERN_USERSESSION, ""); final String sessionToken = TextUtils.getMatch(data, GCConstants.PATTERN_SESSIONTOKEN, ""); diff --git a/main/src/cgeo/geocaching/connector/gc/GCMap.java b/main/src/cgeo/geocaching/connector/gc/GCMap.java index 32c8b42..1571faa 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCMap.java +++ b/main/src/cgeo/geocaching/connector/gc/GCMap.java @@ -1,6 +1,5 @@ package cgeo.geocaching.connector.gc; -import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.SearchResult; @@ -9,12 +8,13 @@ import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.files.ParserException; import cgeo.geocaching.location.Geopoint; +import cgeo.geocaching.location.GeopointFormatter.Format; import cgeo.geocaching.location.Units; import cgeo.geocaching.location.Viewport; -import cgeo.geocaching.location.GeopointFormatter.Format; import cgeo.geocaching.maps.LiveMapStrategy.Strategy; import cgeo.geocaching.maps.LiveMapStrategy.StrategyFlag; import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Formatter; import cgeo.geocaching.utils.JsonUtils; @@ -27,6 +27,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import rx.Observable; import rx.functions.Func2; @@ -47,7 +48,7 @@ import java.util.Set; public class GCMap { private static Viewport lastSearchViewport = null; - public static SearchResult searchByGeocodes(Set<String> geocodes) { + public static SearchResult searchByGeocodes(final Set<String> geocodes) { final SearchResult result = new SearchResult(); final String geocodeList = StringUtils.join(geocodes.toArray(), "|"); @@ -109,7 +110,7 @@ public class GCMap { * Retrieved data. * @return SearchResult. Never null. */ - public static SearchResult parseMapJSON(final String data, Tile tile, Bitmap bitmap, final Strategy strategy) { + public static SearchResult parseMapJSON(final String data, final Tile tile, final Bitmap bitmap, final Strategy strategy) { final SearchResult searchResult = new SearchResult(); try { @@ -142,13 +143,13 @@ public class GCMap { } // iterate over the data and construct all caches in this tile - Map<String, List<UTFGridPosition>> positions = new HashMap<>(); // JSON id as key - Map<String, List<UTFGridPosition>> singlePositions = new HashMap<>(); // JSON id as key + final Map<String, List<UTFGridPosition>> positions = new HashMap<>(); // JSON id as key + final Map<String, List<UTFGridPosition>> singlePositions = new HashMap<>(); // JSON id as key for (final JsonNode rawKey: keys) { final String key = rawKey.asText(); if (StringUtils.isNotBlank(key)) { // index 0 is empty - UTFGridPosition pos = UTFGridPosition.fromString(key); + final UTFGridPosition pos = UTFGridPosition.fromString(key); final ArrayNode dataForKey = (ArrayNode) dataObject.get(key); for (final JsonNode cacheInfo: dataForKey) { final String id = cacheInfo.get("i").asText(); @@ -174,18 +175,18 @@ public class GCMap { } final ArrayList<Geocache> caches = new ArrayList<>(); - for (Entry<String, List<UTFGridPosition>> entry : positions.entrySet()) { - String id = entry.getKey(); - List<UTFGridPosition> pos = entry.getValue(); - UTFGridPosition xy = UTFGrid.getPositionInGrid(pos); - Geocache cache = new Geocache(); + for (final Entry<String, List<UTFGridPosition>> entry : positions.entrySet()) { + final String id = entry.getKey(); + final List<UTFGridPosition> pos = entry.getValue(); + final UTFGridPosition xy = UTFGrid.getPositionInGrid(pos); + final Geocache cache = new Geocache(); cache.setDetailed(false); cache.setReliableLatLon(false); cache.setGeocode(id); cache.setName(nameCache.get(id)); cache.setCoords(tile.getCoord(xy), tile.getZoomLevel()); if (strategy.flags.contains(StrategyFlag.PARSE_TILES) && bitmap != null) { - for (UTFGridPosition singlePos : singlePositions.get(id)) { + for (final UTFGridPosition singlePos : singlePositions.get(id)) { if (IconDecoder.parseMapPNG(cache, bitmap, singlePos, tile.getZoomLevel())) { break; // cache parsed } @@ -227,17 +228,18 @@ public class GCMap { * Live map tokens * @return */ + @NonNull public static SearchResult searchByViewport(final Viewport viewport, final MapTokens tokens) { - int speed = (int) CgeoApplication.getInstance().currentGeo().getSpeed() * 60 * 60 / 1000; // in km/h + final int speed = (int) Sensors.getInstance().currentGeo().getSpeed() * 60 * 60 / 1000; // in km/h Strategy strategy = Settings.getLiveMapStrategy(); if (strategy == Strategy.AUTO) { strategy = speed >= 30 ? Strategy.FAST : Strategy.DETAILED; } - SearchResult result = searchByViewport(viewport, tokens, strategy); + final SearchResult result = searchByViewport(viewport, tokens, strategy); if (Settings.isDebug()) { - StringBuilder text = new StringBuilder(Formatter.SEPARATOR).append(strategy.getL10n()).append(Formatter.SEPARATOR).append(Units.getSpeed(speed)); + final StringBuilder text = new StringBuilder(Formatter.SEPARATOR).append(strategy.getL10n()).append(Formatter.SEPARATOR).append(Units.getSpeed(speed)); result.setUrl(result.getUrl() + text); } @@ -257,6 +259,7 @@ public class GCMap { * Strategy for data retrieval and parsing, @see Strategy * @return */ + @NonNull private static SearchResult searchByViewport(final Viewport viewport, final MapTokens tokens, final Strategy strategy) { Log.d("GCMap.searchByViewport" + viewport.toString()); @@ -340,7 +343,7 @@ public class GCMap { final Geopoint center = viewport.getCenter(); if ((lastSearchViewport == null) || !lastSearchViewport.contains(center)) { //FIXME We don't have a RecaptchaReceiver!? - SearchResult search = GCParser.searchByCoords(center, Settings.getCacheType(), false, null); + final SearchResult search = GCParser.searchByCoords(center, Settings.getCacheType(), false, null); if (search != null && !search.isEmpty()) { final Set<String> geocodes = search.getGeocodes(); lastSearchViewport = DataStore.getBounds(geocodes); @@ -354,11 +357,11 @@ public class GCMap { /** * Creates a list of caches types to filter on the live map (exclusion string) - * + * * @param typeToDisplay * - cache type to omit from exclusion list so it gets displayed * @return - * + * * cache types for live map filter: * 2 = traditional, 9 = ape, 5 = letterbox * 3 = multi @@ -366,8 +369,8 @@ public class GCMap { * 4 = virtual, 11 = webcam, 137 = earth * 8 = mystery, 1858 = whereigo */ - private static String getCacheTypeFilter(CacheType typeToDisplay) { - Set<String> filterTypes = new HashSet<>(); + private static String getCacheTypeFilter(final CacheType typeToDisplay) { + final Set<String> filterTypes = new HashSet<>(); // Put all types in set, remove what should be visible in a second step filterTypes.addAll(Arrays.asList("2", "9", "5", "3", "6", "453", "13", "1304", "4", "11", "137", "8", "1858")); switch (typeToDisplay) { diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java index 33447c7..a25cba8 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCParser.java +++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java @@ -78,10 +78,16 @@ import java.util.Set; import java.util.regex.Pattern; public abstract class GCParser { + @NonNull private final static SynchronizedDateFormat DATE_TB_IN_1 = new SynchronizedDateFormat("EEEEE, dd MMMMM yyyy", Locale.ENGLISH); // Saturday, 28 March 2009 + + @NonNull private final static SynchronizedDateFormat DATE_TB_IN_2 = new SynchronizedDateFormat("EEEEE, MMMMM dd, yyyy", Locale.ENGLISH); // Saturday, March 28, 2009 + + @NonNull private final static ImmutablePair<StatusCode, Geocache> UNKNOWN_PARSE_ERROR = ImmutablePair.of(StatusCode.UNKNOWN_ERROR, null); + @Nullable private static SearchResult parseSearch(final String url, final String pageContent, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { if (StringUtils.isBlank(pageContent)) { Log.e("GCParser.parseSearch: No page given"); @@ -349,11 +355,13 @@ public abstract class GCParser { return searchResult; } + @Nullable private static Float parseStars(final String value) { final float floatValue = Float.parseFloat(StringUtils.replaceChars(value, ',', '.')); return floatValue >= 0.5 && floatValue <= 5.0 ? floatValue : null; } + @Nullable static SearchResult parseCache(final String page, final CancellableHandler handler) { final ImmutablePair<StatusCode, Geocache> parsed = parseCacheFromText(page, handler); // attention: parseCacheFromText already stores implicitly through searchResult.addCache @@ -379,6 +387,7 @@ public abstract class GCParser { return new SearchResult(cache); } + @NonNull static SearchResult parseAndSaveCacheFromText(final String page, @Nullable final CancellableHandler handler) { final ImmutablePair<StatusCode, Geocache> parsed = parseCacheFromText(page, handler); final SearchResult result = new SearchResult(parsed.left); @@ -771,8 +780,6 @@ public abstract class GCParser { } } - cache.parseWaypointsFromNote(); - // last check for necessary cache conditions if (StringUtils.isBlank(cache.getGeocode())) { return UNKNOWN_PARSE_ERROR; @@ -782,10 +789,12 @@ public abstract class GCParser { return ImmutablePair.of(StatusCode.NO_ERROR, cache); } + @Nullable private static String getNumberString(final String numberWithPunctuation) { return StringUtils.replaceChars(numberWithPunctuation, ".,", ""); } + @Nullable public static SearchResult searchByNextPage(final SearchResult search, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { if (search == null) { return null; @@ -858,7 +867,7 @@ public abstract class GCParser { } @Nullable - private static SearchResult searchByAny(final CacheType cacheType, final boolean my, final boolean showCaptcha, final Parameters params, final RecaptchaReceiver recaptchaReceiver) { + private static SearchResult searchByAny(@NonNull final CacheType cacheType, final boolean my, final boolean showCaptcha, final Parameters params, final RecaptchaReceiver recaptchaReceiver) { insertCacheType(params, cacheType); final String uri = "http://www.geocaching.com/seek/nearest.aspx"; @@ -885,12 +894,12 @@ public abstract class GCParser { return search; } - public static SearchResult searchByCoords(final @NonNull Geopoint coords, final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { + public static SearchResult searchByCoords(final @NonNull Geopoint coords, @NonNull final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { final Parameters params = new Parameters("lat", Double.toString(coords.getLatitude()), "lng", Double.toString(coords.getLongitude())); return searchByAny(cacheType, false, showCaptcha, params, recaptchaReceiver); } - public static SearchResult searchByKeyword(final @NonNull String keyword, final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { + public static SearchResult searchByKeyword(final @NonNull String keyword, @NonNull final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { if (StringUtils.isBlank(keyword)) { Log.e("GCParser.searchByKeyword: No keyword given"); return null; @@ -908,7 +917,7 @@ public abstract class GCParser { return false; } - public static SearchResult searchByUsername(final String userName, final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { + public static SearchResult searchByUsername(final String userName, @NonNull final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { if (StringUtils.isBlank(userName)) { Log.e("GCParser.searchByUsername: No user name given"); return null; @@ -919,7 +928,7 @@ public abstract class GCParser { return searchByAny(cacheType, isSearchForMyCaches(userName), showCaptcha, params, recaptchaReceiver); } - public static SearchResult searchByPocketQuery(final String pocketGuid, final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { + public static SearchResult searchByPocketQuery(final String pocketGuid, @NonNull final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { if (StringUtils.isBlank(pocketGuid)) { Log.e("GCParser.searchByPocket: No guid name given"); return null; @@ -930,7 +939,7 @@ public abstract class GCParser { return searchByAny(cacheType, false, showCaptcha, params, recaptchaReceiver); } - public static SearchResult searchByOwner(final String userName, final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { + public static SearchResult searchByOwner(final String userName, @NonNull final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { if (StringUtils.isBlank(userName)) { Log.e("GCParser.searchByOwner: No user name given"); return null; @@ -940,30 +949,6 @@ public abstract class GCParser { return searchByAny(cacheType, isSearchForMyCaches(userName), showCaptcha, params, recaptchaReceiver); } - public static SearchResult searchByAddress(final String address, final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { - if (StringUtils.isBlank(address)) { - Log.e("GCParser.searchByAddress: No address given"); - return null; - } - - final ObjectNode response = Network.requestJSON("http://www.geocaching.com/api/geocode", new Parameters("q", address)); - if (response == null) { - return null; - } - - if (!StringUtils.equalsIgnoreCase(response.path("status").asText(), "success")) { - return null; - } - - final JsonNode data = response.path("data"); - final JsonNode latNode = data.get("lat"); - final JsonNode lngNode = data.get("lng"); - if (latNode == null || lngNode == null) { - return null; - } - return searchByCoords(new Geopoint(latNode.asDouble(), lngNode.asDouble()), cacheType, showCaptcha, recaptchaReceiver); - } - @Nullable public static Trackable searchTrackable(final String geocode, final String guid, final String id) { if (StringUtils.isBlank(geocode) && StringUtils.isBlank(guid) && StringUtils.isBlank(id)) { @@ -1816,9 +1801,10 @@ public abstract class GCParser { return types; } + @NonNull public static List<TrackableLog> parseTrackableLog(final String page) { if (StringUtils.isEmpty(page)) { - return null; + return Collections.emptyList(); } String table = StringUtils.substringBetween(page, "<table id=\"tblTravelBugs\"", "</table>"); @@ -1831,7 +1817,7 @@ public abstract class GCParser { table = StringUtils.substringBetween(table, "<tbody>", "</tbody>"); if (StringUtils.isBlank(table)) { Log.e("GCParser.parseTrackableLog: tbody not found on page"); - return null; + return Collections.emptyList(); } final List<TrackableLog> trackableLogs = new ArrayList<>(); @@ -1922,7 +1908,7 @@ public abstract class GCParser { } // Wait for completion of logs parsing, retrieving and merging - mergedLogs.toBlocking().last(); + RxUtils.waitForCompletion(mergedLogs); } /** diff --git a/main/src/cgeo/geocaching/connector/gc/GCSmiliesProvider.java b/main/src/cgeo/geocaching/connector/gc/GCSmiliesProvider.java index eba9301..071c3b0 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCSmiliesProvider.java +++ b/main/src/cgeo/geocaching/connector/gc/GCSmiliesProvider.java @@ -1,5 +1,7 @@ package cgeo.geocaching.connector.gc; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; public class GCSmiliesProvider { public enum Smiley { @@ -24,9 +26,10 @@ public class GCSmiliesProvider { DISAPPROVE("V"), QUESTION("?"); + @NonNull public final String text; - Smiley(final String text) { + Smiley(@NonNull final String text) { this.text = text; } @@ -35,12 +38,14 @@ public class GCSmiliesProvider { } } + @NonNull public static Smiley[] getSmilies() { return Smiley.values(); } - public static Smiley getSmiley(int itemId) { - for (Smiley smiley : getSmilies()) { + @Nullable + public static Smiley getSmiley(final int itemId) { + for (final Smiley smiley : getSmilies()) { if (smiley.getItemId() == itemId) { return smiley; } diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java index 9e9ec7f..a2322e0 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java @@ -48,6 +48,7 @@ public class OCApiConnector extends OCConnector implements ISearchByGeocode { } @Override + @NonNull public String getLicenseText(final @NonNull Geocache cache) { // NOT TO BE TRANSLATED return "© " + cache.getOwnerDisplayName() + ", <a href=\"" + getCacheUrl(cache) + "\">" + getName() + "</a>, " + licenseString; diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java index e8f20c9..2c783fb 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java @@ -17,6 +17,7 @@ import cgeo.geocaching.connector.oc.UserInfo.UserInfoStatus; import cgeo.geocaching.loaders.RecaptchaReceiver; import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.location.Viewport; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.CryptUtils; @@ -49,7 +50,8 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente } @Override - public SearchResult searchByViewport(@NonNull final Viewport viewport, final MapTokens tokens) { + @NonNull + public SearchResult searchByViewport(@NonNull final Viewport viewport, @NonNull final MapTokens tokens) { return new SearchResult(OkapiClient.getCachesBBox(viewport, this)); } @@ -98,7 +100,7 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente } @Override - public boolean addToWatchlist(final Geocache cache) { + public boolean addToWatchlist(@NonNull final Geocache cache) { final boolean added = OkapiClient.setWatchState(cache, true, this); if (added) { @@ -109,7 +111,7 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente } @Override - public boolean removeFromWatchlist(final Geocache cache) { + public boolean removeFromWatchlist(@NonNull final Geocache cache) { final boolean removed = OkapiClient.setWatchState(cache, false, this); if (removed) { @@ -125,12 +127,13 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente } @Override - public ILoggingManager getLoggingManager(final LogCacheActivity activity, final Geocache cache) { + @NonNull + public ILoggingManager getLoggingManager(@NonNull final LogCacheActivity activity, @NonNull final Geocache cache) { return new OkapiLoggingManager(activity, this, cache); } @Override - public boolean canLog(final Geocache cache) { + public boolean canLog(@NonNull final Geocache cache) { return true; } @@ -149,7 +152,7 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { return StringUtils.isNotEmpty(getUserName()) && StringUtils.equals(cache.getOwnerDisplayName(), getUserName()); } @@ -175,8 +178,7 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente @Override public SearchResult searchByKeyword(final @NonNull String name, final @NonNull RecaptchaReceiver recaptchaReceiver) { - final Geopoint currentPos = CgeoApplication.getInstance().currentGeo().getCoords(); - return new SearchResult(OkapiClient.getCachesNamed(currentPos, name, this)); + return new SearchResult(OkapiClient.getCachesNamed(Sensors.getInstance().currentGeo().getCoords(), name, this)); } @Override diff --git a/main/src/cgeo/geocaching/connector/oc/OCConnector.java b/main/src/cgeo/geocaching/connector/oc/OCConnector.java index 9c79f71..8ac457b 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCConnector.java @@ -28,42 +28,46 @@ public class OCConnector extends AbstractConnector { } @Override - public boolean canHandle(@NonNull String geocode) { + public boolean canHandle(@NonNull final String geocode) { return codePattern.matcher(geocode).matches(); } @Override + @NonNull public String getName() { return name; } @Override - public String getCacheUrl(@NonNull Geocache cache) { + @NonNull + public String getCacheUrl(@NonNull final Geocache cache) { return getCacheUrlPrefix() + cache.getGeocode(); } @Override + @NonNull public String getHost() { return host; } @Override - public boolean isZippedGPXFile(String fileName) { + public boolean isZippedGPXFile(@NonNull final String fileName) { return GPX_ZIP_FILE_PATTERN.matcher(fileName).matches(); } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { return false; } @Override + @NonNull protected String getCacheUrlPrefix() { return "http://" + host + "/viewcache.php?wp="; } @Override - public int getCacheMapMarkerId(boolean disabled) { + public int getCacheMapMarkerId(final boolean disabled) { if (disabled) { return R.drawable.marker_disabled_oc; } @@ -71,7 +75,8 @@ public class OCConnector extends AbstractConnector { } @Override - public final List<LogType> getPossibleLogTypes(Geocache cache) { + @NonNull + public final List<LogType> getPossibleLogTypes(@NonNull final Geocache cache) { if (cache.isEventCache()) { return EVENT_LOG_TYPES; } diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java index 1d0f991..ee095e3 100644 --- a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java +++ b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java @@ -397,7 +397,6 @@ final class OkapiClient { } if (response.hasNonNull(CACHE_MY_NOTES)) { cache.setPersonalNote(response.get(CACHE_MY_NOTES).asText()); - cache.parseWaypointsFromNote(); } cache.setLogPasswordRequired(response.get(CACHE_REQ_PASSWORD).asBoolean()); @@ -620,6 +619,7 @@ final class OkapiClient { cache.setCoords(new Geopoint(latitude, longitude)); } + @NonNull private static CacheSize getCacheSize(final ObjectNode response) { if (!response.has(CACHE_SIZE2)) { return getCacheSizeDeprecated(response); @@ -633,6 +633,7 @@ final class OkapiClient { } } + @NonNull private static CacheSize getCacheSizeDeprecated(final ObjectNode response) { if (!response.has(CACHE_SIZE_DEPRECATED)) { return CacheSize.NOT_CHOSEN; diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java b/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java index 75bc987..76e597c 100644 --- a/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java @@ -9,6 +9,9 @@ import cgeo.geocaching.connector.LogResult; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.StatusCode; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + import android.net.Uri; import java.util.Calendar; @@ -19,7 +22,7 @@ public class OkapiLoggingManager extends AbstractLoggingManager { private final OCApiLiveConnector connector; private final Geocache cache; - private LogCacheActivity activity; + private final LogCacheActivity activity; private boolean hasLoaderError = true; public OkapiLoggingManager(final LogCacheActivity activity, final OCApiLiveConnector connector, final Geocache cache) { @@ -37,18 +40,21 @@ public class OkapiLoggingManager extends AbstractLoggingManager { } @Override - public final LogResult postLog(final LogType logType, final Calendar date, final String log, final String logPassword, final List<TrackableLog> trackableLogs) { + @NonNull + public final LogResult postLog(@NonNull final LogType logType, @NonNull final Calendar date, @NonNull final String log, @Nullable final String logPassword, @NonNull final List<TrackableLog> trackableLogs) { final LogResult result = OkapiClient.postLog(cache, logType, date, log, logPassword, connector); connector.login(null, null); return result; } @Override + @NonNull public final ImageResult postLogImage(final String logId, final String imageCaption, final String imageDescription, final Uri imageUri) { return new ImageResult(StatusCode.LOG_POST_ERROR, ""); } @Override + @NonNull public List<LogType> getPossibleLogTypes() { if (hasLoaderError) { return Collections.emptyList(); diff --git a/main/src/cgeo/geocaching/connector/ox/OXConnector.java b/main/src/cgeo/geocaching/connector/ox/OXConnector.java index dd048f9..41035d1 100644 --- a/main/src/cgeo/geocaching/connector/ox/OXConnector.java +++ b/main/src/cgeo/geocaching/connector/ox/OXConnector.java @@ -29,33 +29,37 @@ public class OXConnector extends AbstractConnector implements ISearchByCenter, I private static final Pattern PATTERN_GEOCODE = Pattern.compile("OX[A-Z0-9]+", Pattern.CASE_INSENSITIVE); @Override - public boolean canHandle(@NonNull String geocode) { + public boolean canHandle(@NonNull final String geocode) { return PATTERN_GEOCODE.matcher(geocode).matches(); } @Override - public String getCacheUrl(@NonNull Geocache cache) { + @NonNull + public String getCacheUrl(@NonNull final Geocache cache) { return getCacheUrlPrefix() + cache.getGeocode(); } @Override + @NonNull public String getName() { return "OpenCaching.com"; } @Override + @NonNull public String getHost() { return "www.opencaching.com"; } @Override - public String getLicenseText(@NonNull Geocache cache) { + @NonNull + public String getLicenseText(@NonNull final Geocache cache) { // NOT TO BE TRANSLATED return "<a href=\"" + getCacheUrl(cache) + "\">" + getName() + "</a> data licensed under the Creative Commons CC-BY-SA 3.0 License"; } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { return false; } @@ -73,17 +77,19 @@ public class OXConnector extends AbstractConnector implements ISearchByCenter, I } @Override - public SearchResult searchByCenter(@NonNull Geopoint center, final @NonNull RecaptchaReceiver recaptchaReceiver) { + public SearchResult searchByCenter(@NonNull final Geopoint center, final @NonNull RecaptchaReceiver recaptchaReceiver) { return createSearchResult(OpenCachingApi.searchByCenter(center)); } @Override + @NonNull protected String getCacheUrlPrefix() { return "http://www.opencaching.com/#!geocache/"; } @Override - public SearchResult searchByViewport(@NonNull Viewport viewport, final MapTokens tokens) { + @NonNull + public SearchResult searchByViewport(@NonNull final Viewport viewport, @NonNull final MapTokens tokens) { return createSearchResult(OpenCachingApi.searchByBoundingBox(viewport)); } @@ -97,7 +103,7 @@ public class OXConnector extends AbstractConnector implements ISearchByCenter, I return createSearchResult(OpenCachingApi.searchByKeyword(name)); } - private static SearchResult createSearchResult(Collection<Geocache> caches) { + private static SearchResult createSearchResult(final Collection<Geocache> caches) { if (caches == null) { return null; } diff --git a/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java b/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java index fb554b9..c3a7437 100644 --- a/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java +++ b/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java @@ -16,14 +16,14 @@ public abstract class AbstractTrackableConnector implements TrackableConnector { } @Override - public @Nullable - String getTrackableCodeFromUrl(@NonNull String url) { + @Nullable + public String getTrackableCodeFromUrl(@NonNull final String url) { return null; } @Override - public @NonNull - List<UserAction> getUserActions() { + @NonNull + public List<UserAction> getUserActions() { return AbstractConnector.getDefaultUserActions(); } } diff --git a/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java b/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java index 03052f9..b6792f0 100644 --- a/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java +++ b/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java @@ -15,17 +15,18 @@ public class GeokretyConnector extends AbstractTrackableConnector { private static final Pattern PATTERN_GK_CODE = Pattern.compile("GK[0-9A-F]{4,}"); @Override - public boolean canHandleTrackable(String geocode) { + public boolean canHandleTrackable(final String geocode) { return geocode != null && PATTERN_GK_CODE.matcher(geocode).matches(); } @Override - public String getUrl(Trackable trackable) { + @NonNull + public String getUrl(@NonNull final Trackable trackable) { return "http://geokrety.org/konkret.php?id=" + getId(trackable.getGeocode()); } @Override - public Trackable searchTrackable(String geocode, String guid, String id) { + public Trackable searchTrackable(final String geocode, final String guid, final String id) { final String page = Network.getResponseData(Network.getRequest("http://geokrety.org/export2.php?gkid=" + getId(geocode))); if (page == null) { return null; @@ -33,7 +34,7 @@ public class GeokretyConnector extends AbstractTrackableConnector { return GeokretyParser.parse(page); } - protected static int getId(String geocode) { + protected static int getId(final String geocode) { try { final String hex = geocode.substring(2); return Integer.parseInt(hex, 16); @@ -45,9 +46,9 @@ public class GeokretyConnector extends AbstractTrackableConnector { @Override public @Nullable - String getTrackableCodeFromUrl(@NonNull String url) { + String getTrackableCodeFromUrl(@NonNull final String url) { // http://geokrety.org/konkret.php?id=38545 - String id = StringUtils.substringAfterLast(url, "konkret.php?id="); + final String id = StringUtils.substringAfterLast(url, "konkret.php?id="); if (StringUtils.isNumeric(id)) { return geocode(Integer.parseInt(id)); } diff --git a/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java b/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java index 6071b5f..01c1897 100644 --- a/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java +++ b/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java @@ -16,16 +16,17 @@ public interface TrackableConnector { public boolean canHandleTrackable(final String geocode); - public String getUrl(final Trackable trackable); + @NonNull + public String getUrl(@NonNull final Trackable trackable); public boolean isLoggable(); public Trackable searchTrackable(String geocode, String guid, String id); - public @Nullable - String getTrackableCodeFromUrl(final @NonNull String url); + @Nullable + public String getTrackableCodeFromUrl(final @NonNull String url); - public @NonNull - List<UserAction> getUserActions(); + @NonNull + public List<UserAction> getUserActions(); } diff --git a/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java b/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java index 3307481..5d825a3 100644 --- a/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java +++ b/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java @@ -25,7 +25,8 @@ public class TravelBugConnector extends AbstractTrackableConnector { } @Override - public String getUrl(final Trackable trackable) { + @NonNull + public String getUrl(@NonNull final Trackable trackable) { return "http://www.geocaching.com//track/details.aspx?tracker=" + trackable.getGeocode(); } diff --git a/main/src/cgeo/geocaching/connector/trackable/UnknownTrackableConnector.java b/main/src/cgeo/geocaching/connector/trackable/UnknownTrackableConnector.java index 0295927..7e7e1b6 100644 --- a/main/src/cgeo/geocaching/connector/trackable/UnknownTrackableConnector.java +++ b/main/src/cgeo/geocaching/connector/trackable/UnknownTrackableConnector.java @@ -2,22 +2,23 @@ package cgeo.geocaching.connector.trackable; import cgeo.geocaching.Trackable; -import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; public class UnknownTrackableConnector extends AbstractTrackableConnector { @Override - public boolean canHandleTrackable(String geocode) { + public boolean canHandleTrackable(final String geocode) { return false; } @Override - public String getUrl(Trackable trackable) { - return StringUtils.EMPTY; + @NonNull + public String getUrl(@NonNull final Trackable trackable) { + throw new IllegalStateException("getUrl cannot be called on unknown trackables"); } @Override - public Trackable searchTrackable(String geocode, String guid, String id) { + public Trackable searchTrackable(final String geocode, final String guid, final String id) { return null; } diff --git a/main/src/cgeo/geocaching/enumerations/CacheAttribute.java b/main/src/cgeo/geocaching/enumerations/CacheAttribute.java index 771a508..823ea7e 100644 --- a/main/src/cgeo/geocaching/enumerations/CacheAttribute.java +++ b/main/src/cgeo/geocaching/enumerations/CacheAttribute.java @@ -4,6 +4,8 @@ import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import android.util.SparseArray; @@ -163,7 +165,10 @@ public enum CacheAttribute { enabled ? stringIdYes : stringIdNo); } + @NonNull private final static Map<String, CacheAttribute> FIND_BY_GCRAWNAME; + + @NonNull private final static SparseArray<CacheAttribute> FIND_BY_OCACODE = new SparseArray<>(); static { final HashMap<String, CacheAttribute> mapGcRawNames = new HashMap<>(); @@ -176,14 +181,17 @@ public enum CacheAttribute { FIND_BY_GCRAWNAME = Collections.unmodifiableMap(mapGcRawNames); } + @Nullable public static CacheAttribute getByRawName(final String rawName) { return rawName != null ? FIND_BY_GCRAWNAME.get(rawName) : null; } + @Nullable public static CacheAttribute getByOcACode(final int ocAcode) { return FIND_BY_OCACODE.get(ocAcode); } + @NonNull public static String trimAttributeName(final String attributeName) { if (null == attributeName) { return ""; diff --git a/main/src/cgeo/geocaching/enumerations/CacheSize.java b/main/src/cgeo/geocaching/enumerations/CacheSize.java index 3a2f379..37446b9 100644 --- a/main/src/cgeo/geocaching/enumerations/CacheSize.java +++ b/main/src/cgeo/geocaching/enumerations/CacheSize.java @@ -3,6 +3,8 @@ package cgeo.geocaching.enumerations; import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.Collections; import java.util.HashMap; import java.util.Locale; @@ -38,6 +40,7 @@ public enum CacheSize { this.ocSize2 = ocSize2; } + @NonNull final private static Map<String, CacheSize> FIND_BY_ID; static { final HashMap<String, CacheSize> mapping = new HashMap<>(); @@ -50,6 +53,7 @@ public enum CacheSize { FIND_BY_ID = Collections.unmodifiableMap(mapping); } + @NonNull public static CacheSize getById(final String id) { if (id == null) { return UNKNOWN; @@ -73,6 +77,7 @@ public enum CacheSize { * @param id * @return */ + @NonNull private static CacheSize getByNumber(final String id) { try { final int numerical = Integer.parseInt(id); diff --git a/main/src/cgeo/geocaching/enumerations/CacheType.java b/main/src/cgeo/geocaching/enumerations/CacheType.java index 995c436..f551c52 100644 --- a/main/src/cgeo/geocaching/enumerations/CacheType.java +++ b/main/src/cgeo/geocaching/enumerations/CacheType.java @@ -4,6 +4,8 @@ import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.Collections; import java.util.HashMap; import java.util.Locale; @@ -56,9 +58,15 @@ public enum CacheType { this.markerId = markerId; } + @NonNull private final static Map<String, CacheType> FIND_BY_ID; + + @NonNull private final static Map<String, CacheType> FIND_BY_PATTERN; + + @NonNull private final static Map<String, CacheType> FIND_BY_GUID; + static { final HashMap<String, CacheType> mappingId = new HashMap<>(); final HashMap<String, CacheType> mappingPattern = new HashMap<>(); @@ -80,6 +88,7 @@ public enum CacheType { FIND_BY_GUID = Collections.unmodifiableMap(mappingGuid); } + @NonNull public static CacheType getById(final String id) { final CacheType result = (id != null) ? CacheType.FIND_BY_ID.get(id.toLowerCase(Locale.US).trim()) : null; if (result == null) { @@ -88,6 +97,7 @@ public enum CacheType { return result; } + @NonNull public static CacheType getByPattern(final String pattern) { final CacheType result = (pattern != null) ? CacheType.FIND_BY_PATTERN.get(pattern.toLowerCase(Locale.US).trim()) : null; if (result == null) { @@ -96,6 +106,7 @@ public enum CacheType { return result; } + @NonNull public static CacheType getByGuid(final String id) { final CacheType result = (id != null) ? CacheType.FIND_BY_GUID.get(id) : null; if (result == null) { diff --git a/main/src/cgeo/geocaching/enumerations/LogType.java b/main/src/cgeo/geocaching/enumerations/LogType.java index 5345611..4c83321 100644 --- a/main/src/cgeo/geocaching/enumerations/LogType.java +++ b/main/src/cgeo/geocaching/enumerations/LogType.java @@ -3,6 +3,8 @@ package cgeo.geocaching.enumerations; import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.Collections; import java.util.HashMap; import java.util.Locale; @@ -65,7 +67,10 @@ public enum LogType { this(id, iconName, type, oc_type, stringId, R.drawable.mark_gray); } + @NonNull private final static Map<String, LogType> FIND_BY_ICONNAME; + + @NonNull private final static Map<String, LogType> FIND_BY_TYPE; static { final HashMap<String, LogType> mappingPattern = new HashMap<>(); @@ -80,6 +85,7 @@ public enum LogType { FIND_BY_TYPE = Collections.unmodifiableMap(mappingType); } + @NonNull public static LogType getById(final int id) { for (final LogType logType : values()) { if (logType.id == id) { @@ -89,6 +95,7 @@ public enum LogType { return UNKNOWN; } + @NonNull public static LogType getByIconName(final String imageType) { // Special case for post reviewer note, which appears sometimes as 18.png (in individual entries) or as 68.png // (in logs counts). @@ -102,6 +109,7 @@ public enum LogType { return result; } + @NonNull public static LogType getByType(final String type) { final LogType result = type != null ? LogType.FIND_BY_TYPE.get(type.toLowerCase(Locale.US).trim()) : null; if (result == null) { diff --git a/main/src/cgeo/geocaching/enumerations/LogTypeTrackable.java b/main/src/cgeo/geocaching/enumerations/LogTypeTrackable.java index e008294..fefeb62 100644 --- a/main/src/cgeo/geocaching/enumerations/LogTypeTrackable.java +++ b/main/src/cgeo/geocaching/enumerations/LogTypeTrackable.java @@ -3,15 +3,17 @@ package cgeo.geocaching.enumerations; import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + public enum LogTypeTrackable { DO_NOTHING("", R.string.log_tb_nothing), VISITED("_Visited", R.string.log_tb_visit), DROPPED_OFF("_DroppedOff", R.string.log_tb_drop); - final public String action; + @NonNull final public String action; final private int resourceId; - LogTypeTrackable(String action, int resourceId) { + LogTypeTrackable(@NonNull final String action, final int resourceId) { this.action = action; this.resourceId = resourceId; } diff --git a/main/src/cgeo/geocaching/enumerations/WaypointType.java b/main/src/cgeo/geocaching/enumerations/WaypointType.java index 1805635..81c0e4d 100644 --- a/main/src/cgeo/geocaching/enumerations/WaypointType.java +++ b/main/src/cgeo/geocaching/enumerations/WaypointType.java @@ -3,6 +3,8 @@ package cgeo.geocaching.enumerations; import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -22,11 +24,12 @@ public enum WaypointType { WAYPOINT("waypoint", R.string.wp_waypoint, R.drawable.waypoint_waypoint), ORIGINAL("original", R.string.wp_original, R.drawable.waypoint_waypoint); + @NonNull public final String id; public final int stringId; public final int markerId; - WaypointType(String id, int stringId, int markerId) { + WaypointType(@NonNull final String id, final int stringId, final int markerId) { this.id = id; this.stringId = stringId; this.markerId = markerId; @@ -36,11 +39,14 @@ public enum WaypointType { * inverse lookup of waypoint IDs<br/> * non public so that <code>null</code> handling can be handled centrally in the enum type itself */ + @NonNull private static final Map<String, WaypointType> FIND_BY_ID; + + @NonNull public static final Set<WaypointType> ALL_TYPES_EXCEPT_OWN_AND_ORIGINAL = new HashSet<>(); static { final HashMap<String, WaypointType> mapping = new HashMap<>(); - for (WaypointType wt : values()) { + for (final WaypointType wt : values()) { mapping.put(wt.id, wt); if (wt != WaypointType.OWN && wt != WaypointType.ORIGINAL) { ALL_TYPES_EXCEPT_OWN_AND_ORIGINAL.add(wt); @@ -53,11 +59,12 @@ public enum WaypointType { * inverse lookup of waypoint IDs<br/> * here the <code>null</code> handling shall be done */ + @NonNull public static WaypointType findById(final String id) { if (null == id) { return WAYPOINT; } - WaypointType waypointType = FIND_BY_ID.get(id); + final WaypointType waypointType = FIND_BY_ID.get(id); if (null == waypointType) { return WAYPOINT; } diff --git a/main/src/cgeo/geocaching/export/FieldnoteExport.java b/main/src/cgeo/geocaching/export/FieldnoteExport.java index c03b848..c4a6adc 100644 --- a/main/src/cgeo/geocaching/export/FieldnoteExport.java +++ b/main/src/cgeo/geocaching/export/FieldnoteExport.java @@ -59,10 +59,11 @@ public class FieldnoteExport extends AbstractExport { final AlertDialog.Builder builder = new AlertDialog.Builder(activity); final Context themedContext; - if (Settings.isLightSkin() && VERSION.SDK_INT < VERSION_CODES.HONEYCOMB) + if (Settings.isLightSkin() && VERSION.SDK_INT < VERSION_CODES.HONEYCOMB) { themedContext = new ContextThemeWrapper(activity, R.style.dark); - else + } else { themedContext = activity; + } final View layout = View.inflate(themedContext, R.layout.fieldnote_export_dialog, null); builder.setView(layout); @@ -135,8 +136,10 @@ public class FieldnoteExport extends AbstractExport { for (final Geocache cache : caches) { if (ConnectorFactory.getConnector(cache).equals(connector) && cache.isLogOffline()) { final LogEntry log = DataStore.loadLogOffline(cache.getGeocode()); - if (!onlyNew || log.date > Settings.getFieldnoteExportDate()) { - fieldNotes.add(cache, log); + if (log != null) { + if (!onlyNew || log.date > Settings.getFieldnoteExportDate()) { + fieldNotes.add(cache, log); + } } } publishProgress(++i); diff --git a/main/src/cgeo/geocaching/files/AbstractFileListActivity.java b/main/src/cgeo/geocaching/files/AbstractFileListActivity.java index fa84df9..427fd48 100644 --- a/main/src/cgeo/geocaching/files/AbstractFileListActivity.java +++ b/main/src/cgeo/geocaching/files/AbstractFileListActivity.java @@ -9,6 +9,7 @@ import cgeo.geocaching.utils.Log; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import android.app.ProgressDialog; import android.content.DialogInterface; @@ -203,7 +204,7 @@ public abstract class AbstractFileListActivity<T extends ArrayAdapter<File>> ext * @param filename * @return <code>true</code> if the filename belongs to the list */ - protected boolean filenameBelongsToList(final String filename) { + protected boolean filenameBelongsToList(@NonNull final String filename) { for (final String ext : extensions) { if (StringUtils.endsWithIgnoreCase(filename, ext)) { return true; diff --git a/main/src/cgeo/geocaching/files/FileParser.java b/main/src/cgeo/geocaching/files/FileParser.java index 9521c70..9c70a0d 100644 --- a/main/src/cgeo/geocaching/files/FileParser.java +++ b/main/src/cgeo/geocaching/files/FileParser.java @@ -16,7 +16,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Collection; -import java.util.Date; import java.util.concurrent.CancellationException; public abstract class FileParser { @@ -24,25 +23,22 @@ public abstract class FileParser { * Parses caches from input stream. * * @param stream + * the input stream * @param progressHandler - * for reporting parsing progress (in bytes read from input stream) + * for reporting parsing progress (in bytes read from input stream) * @return collection of caches * @throws IOException - * if the input stream can't be read + * if the input stream can't be read * @throws ParserException - * if the input stream contains data not matching the file format of the parser + * if the input stream contains data not matching the file format of the parser */ + @NonNull public abstract Collection<Geocache> parse(@NonNull final InputStream stream, @Nullable final CancellableHandler progressHandler) throws IOException, ParserException; /** * Convenience method for parsing a file. - * - * @param file - * @param progressHandler - * @return - * @throws IOException - * @throws ParserException */ + @NonNull public Collection<Geocache> parse(final File file, final CancellableHandler progressHandler) throws IOException, ParserException { final BufferedInputStream stream = new BufferedInputStream(new FileInputStream(file)); try { @@ -52,6 +48,7 @@ public abstract class FileParser { } } + @NonNull protected static StringBuilder readStream(@NonNull final InputStream is, @Nullable final CancellableHandler progressHandler) throws IOException { final StringBuilder buffer = new StringBuilder(); final ProgressInputStream progressInputStream = new ProgressInputStream(is); @@ -84,7 +81,7 @@ public abstract class FileParser { } else { cache.setInventoryItems(0); } - final long time = new Date().getTime(); + final long time = System.currentTimeMillis(); cache.setUpdated(time); cache.setDetailedUpdate(time); } diff --git a/main/src/cgeo/geocaching/files/GPXParser.java b/main/src/cgeo/geocaching/files/GPXParser.java index cd99ae9..7dd5c4a 100644 --- a/main/src/cgeo/geocaching/files/GPXParser.java +++ b/main/src/cgeo/geocaching/files/GPXParser.java @@ -274,6 +274,7 @@ public abstract class GPXParser extends FileParser { } @Override + @NonNull public Collection<Geocache> parse(@NonNull final InputStream stream, @Nullable final CancellableHandler progressHandler) throws IOException, ParserException { resetCache(); final RootElement root = new RootElement(namespace, "gpx"); diff --git a/main/src/cgeo/geocaching/files/LocParser.java b/main/src/cgeo/geocaching/files/LocParser.java index bca7dcf..a5a4b9c 100644 --- a/main/src/cgeo/geocaching/files/LocParser.java +++ b/main/src/cgeo/geocaching/files/LocParser.java @@ -31,8 +31,10 @@ import java.util.Set; public final class LocParser extends FileParser { + @NonNull private static final String NAME_OWNER_SEPARATOR = " by "; + @NonNull private static final CacheSize[] SIZES = { CacheSize.NOT_CHOSEN, // 1 CacheSize.MICRO, // 2 @@ -45,16 +47,17 @@ public final class LocParser extends FileParser { }; // Used so that the initial value of the geocache is not null. Never filled. + @NonNull private static final Geocache DUMMY_GEOCACHE = new Geocache(); - private int listId; + private final int listId; public static void parseLoc(final SearchResult searchResult, final String fileContent, final Set<Geocache> caches) { final Map<String, Geocache> cidCoords = parseLoc(fileContent); // save found cache coordinates final HashSet<String> contained = new HashSet<>(); - for (String geocode : searchResult.getGeocodes()) { + for (final String geocode : searchResult.getGeocodes()) { if (cidCoords.containsKey(geocode)) { contained.add(geocode); } @@ -70,10 +73,12 @@ public final class LocParser extends FileParser { } } + @NonNull private static Map<String, Geocache> parseLoc(final String content) { return parseLoc(new ByteArrayInputStream(content.getBytes(Charsets.UTF_8))); } + @NonNull private static Map<String, Geocache> parseLoc(final InputStream content) { try { final XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); @@ -146,22 +151,24 @@ public final class LocParser extends FileParser { cache.setOwnerUserId(coord.getOwnerUserId()); } + @NonNull public static Geopoint parsePoint(final String latitude, final String longitude) { // the loc file contains the coordinates as plain floating point values, therefore avoid using the GeopointParser try { return new Geopoint(Double.valueOf(latitude), Double.valueOf(longitude)); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("LOC format has changed", e); } // fall back to parser, just in case the format changes return new Geopoint(latitude, longitude); } - public LocParser(int listId) { + public LocParser(final int listId) { this.listId = listId; } @Override + @NonNull public Collection<Geocache> parse(@NonNull final InputStream stream, @Nullable final CancellableHandler progressHandler) throws IOException, ParserException { final int maxSize = stream.available(); final Map<String, Geocache> coords = parseLoc(stream); diff --git a/main/src/cgeo/geocaching/files/LocalStorage.java b/main/src/cgeo/geocaching/files/LocalStorage.java index 8896833..7fce27d 100644 --- a/main/src/cgeo/geocaching/files/LocalStorage.java +++ b/main/src/cgeo/geocaching/files/LocalStorage.java @@ -45,7 +45,7 @@ public final class LocalStorage { public static final String HEADER_ETAG = "etag"; /** Name of the local private directory used to hold cached information */ - public final static String cache = ".cgeo"; + public final static String CACHE_DIRNAME = ".cgeo"; private static File internalStorageBase; @@ -74,7 +74,7 @@ public final class LocalStorage { private static File getStorageSpecific(final boolean secondary) { return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) ^ secondary ? getExternalStorageBase() : - new File(getInternalStorageBase(), LocalStorage.cache); + new File(getInternalStorageBase(), LocalStorage.CACHE_DIRNAME); } public static File getExternalDbDirectory() { @@ -86,7 +86,7 @@ public final class LocalStorage { } private static File getExternalStorageBase() { - return new File(Environment.getExternalStorageDirectory(), LocalStorage.cache); + return new File(Environment.getExternalStorageDirectory(), LocalStorage.CACHE_DIRNAME); } private static File getInternalStorageBase() { @@ -105,7 +105,7 @@ public final class LocalStorage { * @return the file extension, including the leading dot, or the empty string if none could be determined */ static String getExtension(final String url) { - String urlExt; + final String urlExt; if (url.startsWith("data:")) { // "data:image/png;base64,i53…" -> ".png" urlExt = StringUtils.substringAfter(StringUtils.substringBefore(url, ";"), "/"); @@ -124,7 +124,7 @@ public final class LocalStorage { * the geocode * @return the cache directory */ - public static File getStorageDir(@Nullable final String geocode) { + public static File getStorageDir(@NonNull final String geocode) { return storageDir(getStorage(), geocode); } @@ -136,12 +136,12 @@ public final class LocalStorage { * the geocode * @return the cache directory */ - private static File getStorageSecDir(@Nullable final String geocode) { + private static File getStorageSecDir(@NonNull final String geocode) { return storageDir(getStorageSec(), geocode); } - private static File storageDir(final File base, @Nullable final String geocode) { - return new File(base, StringUtils.defaultIfEmpty(geocode, "_others")); + private static File storageDir(final File base, @NonNull final String geocode) { + return new File(base, geocode); } /** @@ -157,7 +157,7 @@ public final class LocalStorage { * true if an url was given, false if a file name was given * @return the file */ - public static File getStorageFile(@Nullable final String geocode, final String fileNameOrUrl, final boolean isUrl, final boolean createDirs) { + public static File getStorageFile(@NonNull final String geocode, final String fileNameOrUrl, final boolean isUrl, final boolean createDirs) { return buildFile(getStorageDir(geocode), fileNameOrUrl, isUrl, createDirs); } @@ -244,14 +244,14 @@ public final class LocalStorage { public static String getSavedHeader(final File baseFile, final String name) { try { final File file = filenameForHeader(baseFile, name); - final Reader f = new InputStreamReader(new FileInputStream(file), CharEncoding.UTF_8); + final Reader reader = new InputStreamReader(new FileInputStream(file), CharEncoding.UTF_8); try { // No header will be more than 256 bytes final char[] value = new char[256]; - final int count = f.read(value); + final int count = reader.read(value); return new String(value, 0, count); } finally { - f.close(); + reader.close(); } } catch (final FileNotFoundException ignored) { // Do nothing, the file does not exist @@ -442,10 +442,10 @@ public final class LocalStorage { try { fr = new InputStreamReader(new FileInputStream(file), CharEncoding.UTF_8); br = new BufferedReader(fr); - String s = br.readLine(); - while (s != null) { - if (s.startsWith("dev_mount")) { - final String[] tokens = StringUtils.split(s); + String str = br.readLine(); + while (str != null) { + if (str.startsWith("dev_mount")) { + final String[] tokens = StringUtils.split(str); if (tokens.length >= 3) { final String path = tokens[2]; // mountpoint if (!extStorage.equals(path)) { @@ -456,7 +456,7 @@ public final class LocalStorage { } } } - s = br.readLine(); + str = br.readLine(); } } catch (final IOException e) { Log.e("Could not get additional mount points for user content. " + diff --git a/main/src/cgeo/geocaching/filter/AbstractFilter.java b/main/src/cgeo/geocaching/filter/AbstractFilter.java index e602b0f..248c9c2 100644 --- a/main/src/cgeo/geocaching/filter/AbstractFilter.java +++ b/main/src/cgeo/geocaching/filter/AbstractFilter.java @@ -2,6 +2,8 @@ package cgeo.geocaching.filter; import cgeo.geocaching.Geocache; +import org.eclipse.jdt.annotation.NonNull; + import java.util.ArrayList; import java.util.List; @@ -13,9 +15,9 @@ abstract class AbstractFilter implements IFilter { } @Override - public void filter(final List<Geocache> list) { + public void filter(@NonNull final List<Geocache> list) { final List<Geocache> itemsToRemove = new ArrayList<>(); - for (Geocache item : list) { + for (final Geocache item : list) { if (!accepts(item)) { itemsToRemove.add(item); } diff --git a/main/src/cgeo/geocaching/filter/AttributeFilter.java b/main/src/cgeo/geocaching/filter/AttributeFilter.java index b59ab29..2fc6eb2 100644 --- a/main/src/cgeo/geocaching/filter/AttributeFilter.java +++ b/main/src/cgeo/geocaching/filter/AttributeFilter.java @@ -4,6 +4,8 @@ import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import android.content.res.Resources; import java.util.LinkedList; @@ -25,13 +27,14 @@ class AttributeFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cache.getAttributes().contains(attribute); } public static class Factory implements IFilterFactory { @Override + @NonNull public List<IFilter> getFilters() { final String packageName = CgeoApplication.getInstance().getBaseContext().getPackageName(); final Resources res = CgeoApplication.getInstance().getResources(); diff --git a/main/src/cgeo/geocaching/filter/DifficultyFilter.java b/main/src/cgeo/geocaching/filter/DifficultyFilter.java index 175ad75..7989560 100644 --- a/main/src/cgeo/geocaching/filter/DifficultyFilter.java +++ b/main/src/cgeo/geocaching/filter/DifficultyFilter.java @@ -3,6 +3,8 @@ package cgeo.geocaching.filter; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.ArrayList; import java.util.List; @@ -13,7 +15,7 @@ class DifficultyFilter extends AbstractRangeFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { final float difficulty = cache.getDifficulty(); return rangeMin <= difficulty && difficulty < rangeMax; } @@ -24,6 +26,7 @@ class DifficultyFilter extends AbstractRangeFilter { private static final int DIFFICULTY_MAX = 5; @Override + @NonNull public List<IFilter> getFilters() { final ArrayList<IFilter> filters = new ArrayList<>(DIFFICULTY_MAX); for (int difficulty = DIFFICULTY_MIN; difficulty <= DIFFICULTY_MAX; difficulty++) { diff --git a/main/src/cgeo/geocaching/filter/DistanceFilter.java b/main/src/cgeo/geocaching/filter/DistanceFilter.java index 3890571..f1ba7f8 100644 --- a/main/src/cgeo/geocaching/filter/DistanceFilter.java +++ b/main/src/cgeo/geocaching/filter/DistanceFilter.java @@ -2,9 +2,12 @@ package cgeo.geocaching.filter; import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; -import cgeo.geocaching.sensors.GeoData; import cgeo.geocaching.R; import cgeo.geocaching.location.Geopoint; +import cgeo.geocaching.sensors.GeoData; +import cgeo.geocaching.sensors.Sensors; + +import org.eclipse.jdt.annotation.NonNull; import java.util.ArrayList; import java.util.List; @@ -14,15 +17,15 @@ class DistanceFilter extends AbstractFilter { private final int minDistance; private final int maxDistance; - public DistanceFilter(String name, final int minDistance, final int maxDistance) { + public DistanceFilter(final String name, final int minDistance, final int maxDistance) { super(name); this.minDistance = minDistance; this.maxDistance = maxDistance; - geo = CgeoApplication.getInstance().currentGeo(); + geo = Sensors.getInstance().currentGeo(); } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { final Geopoint currentPos = new Geopoint(geo); final Geopoint coords = cache.getCoords(); if (coords == null) { @@ -39,6 +42,7 @@ class DistanceFilter extends AbstractFilter { private static final int[] KILOMETERS = { 0, 2, 5, 10, 20, 50 }; @Override + @NonNull public List<IFilter> getFilters() { final List<IFilter> filters = new ArrayList<>(KILOMETERS.length); for (int i = 0; i < KILOMETERS.length; i++) { diff --git a/main/src/cgeo/geocaching/filter/FilterUserInterface.java b/main/src/cgeo/geocaching/filter/FilterUserInterface.java index 9f1d563..590a726 100644 --- a/main/src/cgeo/geocaching/filter/FilterUserInterface.java +++ b/main/src/cgeo/geocaching/filter/FilterUserInterface.java @@ -54,12 +54,12 @@ public final class FilterUserInterface { register(R.string.cache_attributes, AttributeFilter.Factory.class); register(R.string.cache_status, StateFilter.Factory.class); register(R.string.caches_filter_track, TrackablesFilter.class); - register(R.string.caches_filter_modified, ModifiedFilter.class); register(R.string.caches_filter_origin, OriginFilter.Factory.class); register(R.string.caches_filter_distance, DistanceFilter.Factory.class); - register(R.string.caches_filter_personal_note, PersonalNoteFilter.class); register(R.string.caches_filter_popularity, PopularityFilter.Factory.class); register(R.string.caches_filter_popularity_ratio, PopularityRatioFilter.Factory.class); + register(R.string.caches_filter_personal_data, PersonalDataFilterFactory.class); + register(R.string.caches_filter_rating, RatingFilter.class); // sort by localized names Collections.sort(registry, new Comparator<FactoryEntry>() { @@ -87,16 +87,16 @@ public final class FilterUserInterface { builder.setAdapter(adapter, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int itemIndex) { - FactoryEntry entry = adapter.getItem(itemIndex); + final FactoryEntry entry = adapter.getItem(itemIndex); // reset? if (entry.filterFactory == null) { runAfterwards.call(null); } else { try { - IFilterFactory factoryInstance = entry.filterFactory.newInstance(); + final IFilterFactory factoryInstance = entry.filterFactory.newInstance(); selectFromFactory(factoryInstance, entry.name, runAfterwards); - } catch (Exception e) { + } catch (final Exception e) { Log.e("selectFilter", e); } } diff --git a/main/src/cgeo/geocaching/filter/IFilter.java b/main/src/cgeo/geocaching/filter/IFilter.java index 4fafe6f..de39e5a 100644 --- a/main/src/cgeo/geocaching/filter/IFilter.java +++ b/main/src/cgeo/geocaching/filter/IFilter.java @@ -2,6 +2,8 @@ package cgeo.geocaching.filter; import cgeo.geocaching.Geocache; +import org.eclipse.jdt.annotation.NonNull; + import java.util.List; public interface IFilter { @@ -12,7 +14,7 @@ public interface IFilter { * @param cache * @return true if the filter accepts the cache, false otherwise */ - boolean accepts(final Geocache cache); + boolean accepts(@NonNull final Geocache cache); - void filter(final List<Geocache> list); + void filter(@NonNull final List<Geocache> list); }
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/filter/IFilterFactory.java b/main/src/cgeo/geocaching/filter/IFilterFactory.java index afc99af..82e69da 100644 --- a/main/src/cgeo/geocaching/filter/IFilterFactory.java +++ b/main/src/cgeo/geocaching/filter/IFilterFactory.java @@ -1,7 +1,10 @@ package cgeo.geocaching.filter; +import org.eclipse.jdt.annotation.NonNull; + import java.util.List; interface IFilterFactory { + @NonNull List<? extends IFilter> getFilters(); } diff --git a/main/src/cgeo/geocaching/filter/ModifiedFilter.java b/main/src/cgeo/geocaching/filter/ModifiedFilter.java index 2ac088a..c224cb4 100644 --- a/main/src/cgeo/geocaching/filter/ModifiedFilter.java +++ b/main/src/cgeo/geocaching/filter/ModifiedFilter.java @@ -4,6 +4,8 @@ import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.Collections; import java.util.List; @@ -14,12 +16,13 @@ class ModifiedFilter extends AbstractFilter implements IFilterFactory { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { // modified on GC return cache.hasUserModifiedCoords() || cache.hasFinalDefined(); } @Override + @NonNull public List<ModifiedFilter> getFilters() { return Collections.singletonList(this); } diff --git a/main/src/cgeo/geocaching/filter/OriginFilter.java b/main/src/cgeo/geocaching/filter/OriginFilter.java index 99d1c05..4fb3301 100644 --- a/main/src/cgeo/geocaching/filter/OriginFilter.java +++ b/main/src/cgeo/geocaching/filter/OriginFilter.java @@ -4,6 +4,8 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.IConnector; +import org.eclipse.jdt.annotation.NonNull; + import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -19,16 +21,17 @@ public class OriginFilter extends AbstractFilter { } @Override - public final boolean accepts(final Geocache cache) { + public final boolean accepts(@NonNull final Geocache cache) { return ConnectorFactory.getConnector(cache) == connector; } public static final class Factory implements IFilterFactory { @Override + @NonNull public List<OriginFilter> getFilters() { final ArrayList<OriginFilter> filters = new ArrayList<>(); - for (IConnector connector : ConnectorFactory.getConnectors()) { + for (final IConnector connector : ConnectorFactory.getConnectors()) { filters.add(new OriginFilter(connector)); } diff --git a/main/src/cgeo/geocaching/filter/OwnRatingFilter.java b/main/src/cgeo/geocaching/filter/OwnRatingFilter.java new file mode 100644 index 0000000..0c468a9 --- /dev/null +++ b/main/src/cgeo/geocaching/filter/OwnRatingFilter.java @@ -0,0 +1,35 @@ +package cgeo.geocaching.filter; + +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.Geocache; +import cgeo.geocaching.R; +import cgeo.geocaching.gcvote.GCVote; + +import org.eclipse.jdt.annotation.NonNull; + +import java.util.Collections; +import java.util.List; + +/** + * Filter {@link Geocache}s if they have a locally stored <b>own</b> {@link GCVote} rating. This filter will not do any + * network request to find potentially missing local votes. + * + */ +public class OwnRatingFilter extends AbstractFilter implements IFilterFactory { + + protected OwnRatingFilter() { + super(CgeoApplication.getInstance().getString(R.string.caches_filter_own_rating)); + } + + @Override + public boolean accepts(@NonNull final Geocache cache) { + return cache.getMyVote() > 0; + } + + @Override + @NonNull + public List<OwnRatingFilter> getFilters() { + return Collections.singletonList(this); + } + +} diff --git a/main/src/cgeo/geocaching/filter/PersonalDataFilterFactory.java b/main/src/cgeo/geocaching/filter/PersonalDataFilterFactory.java new file mode 100644 index 0000000..6c6186b --- /dev/null +++ b/main/src/cgeo/geocaching/filter/PersonalDataFilterFactory.java @@ -0,0 +1,16 @@ +package cgeo.geocaching.filter; + +import org.eclipse.jdt.annotation.NonNull; + +import java.util.Arrays; +import java.util.List; + +public class PersonalDataFilterFactory implements IFilterFactory { + + @Override + @NonNull + public List<? extends IFilter> getFilters() { + return Arrays.asList(new OwnRatingFilter(), new PersonalNoteFilter(), new ModifiedFilter()); + } + +} diff --git a/main/src/cgeo/geocaching/filter/PersonalNoteFilter.java b/main/src/cgeo/geocaching/filter/PersonalNoteFilter.java index 15d262f..978ad6b 100644 --- a/main/src/cgeo/geocaching/filter/PersonalNoteFilter.java +++ b/main/src/cgeo/geocaching/filter/PersonalNoteFilter.java @@ -5,10 +5,14 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.R; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import java.util.Collections; import java.util.List; +/** + * Filter that accepts {@link Geocache}s with a non empty personal note stored locally. + */ public class PersonalNoteFilter extends AbstractFilter implements IFilterFactory { protected PersonalNoteFilter() { @@ -16,11 +20,12 @@ public class PersonalNoteFilter extends AbstractFilter implements IFilterFactory } @Override - public boolean accepts(Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return StringUtils.isNotBlank(cache.getPersonalNote()); } @Override + @NonNull public List<PersonalNoteFilter> getFilters() { return Collections.singletonList(this); } diff --git a/main/src/cgeo/geocaching/filter/PopularityFilter.java b/main/src/cgeo/geocaching/filter/PopularityFilter.java index 0fc807d..bb564e8 100644 --- a/main/src/cgeo/geocaching/filter/PopularityFilter.java +++ b/main/src/cgeo/geocaching/filter/PopularityFilter.java @@ -4,6 +4,8 @@ import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.ArrayList; import java.util.List; @@ -11,14 +13,14 @@ class PopularityFilter extends AbstractFilter { private final int minFavorites; private final int maxFavorites; - public PopularityFilter(String name, final int minFavorites, final int maxFavorites) { + public PopularityFilter(final String name, final int minFavorites, final int maxFavorites) { super(name); this.minFavorites = minFavorites; this.maxFavorites = maxFavorites; } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return (cache.getFavoritePoints() > minFavorites) && (cache.getFavoritePoints() <= maxFavorites); } @@ -27,6 +29,7 @@ class PopularityFilter extends AbstractFilter { private static final int[] FAVORITES = { 10, 20, 50, 100, 200, 500 }; @Override + @NonNull public List<IFilter> getFilters() { final List<IFilter> filters = new ArrayList<>(FAVORITES.length); for (final int minRange : FAVORITES) { diff --git a/main/src/cgeo/geocaching/filter/PopularityRatioFilter.java b/main/src/cgeo/geocaching/filter/PopularityRatioFilter.java index f7ac4db..53904f1 100644 --- a/main/src/cgeo/geocaching/filter/PopularityRatioFilter.java +++ b/main/src/cgeo/geocaching/filter/PopularityRatioFilter.java @@ -6,6 +6,8 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.R; import cgeo.geocaching.enumerations.LogType; +import org.eclipse.jdt.annotation.NonNull; + import java.util.ArrayList; import java.util.List; @@ -23,7 +25,7 @@ class PopularityRatioFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { final int finds = getFindsCount(cache); if (finds == 0) { // Prevent division by zero @@ -51,6 +53,7 @@ class PopularityRatioFilter extends AbstractFilter { private static final int[] RATIOS = { 10, 20, 30, 40, 50, 75 }; @Override + @NonNull public List<IFilter> getFilters() { final List<IFilter> filters = new ArrayList<>(RATIOS.length); for (final int minRange : RATIOS) { diff --git a/main/src/cgeo/geocaching/filter/RatingFilter.java b/main/src/cgeo/geocaching/filter/RatingFilter.java new file mode 100644 index 0000000..3edfcb6 --- /dev/null +++ b/main/src/cgeo/geocaching/filter/RatingFilter.java @@ -0,0 +1,35 @@ +package cgeo.geocaching.filter; + +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.Geocache; +import cgeo.geocaching.R; +import cgeo.geocaching.gcvote.GCVote; + +import org.eclipse.jdt.annotation.NonNull; + +import java.util.Collections; +import java.util.List; + +/** + * Filter {@link Geocache}s if they have a locally stored {@link GCVote} rating. This filter will not do any network + * request to find potentially missing local votes. + * + */ +public class RatingFilter extends AbstractFilter implements IFilterFactory { + + protected RatingFilter() { + super(CgeoApplication.getInstance().getString(R.string.caches_filter_rating)); + } + + @Override + public boolean accepts(@NonNull final Geocache cache) { + return cache.getRating() > 0; + } + + @Override + @NonNull + public List<RatingFilter> getFilters() { + return Collections.singletonList(this); + } + +} diff --git a/main/src/cgeo/geocaching/filter/SizeFilter.java b/main/src/cgeo/geocaching/filter/SizeFilter.java index f02874c..4c7c122 100644 --- a/main/src/cgeo/geocaching/filter/SizeFilter.java +++ b/main/src/cgeo/geocaching/filter/SizeFilter.java @@ -3,6 +3,8 @@ package cgeo.geocaching.filter; import cgeo.geocaching.Geocache; import cgeo.geocaching.enumerations.CacheSize; +import org.eclipse.jdt.annotation.NonNull; + import java.util.LinkedList; import java.util.List; @@ -15,7 +17,7 @@ class SizeFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cacheSize == cache.getSize(); } @@ -27,10 +29,11 @@ class SizeFilter extends AbstractFilter { public static class Factory implements IFilterFactory { @Override + @NonNull public List<IFilter> getFilters() { final CacheSize[] cacheSizes = CacheSize.values(); final List<IFilter> filters = new LinkedList<>(); - for (CacheSize cacheSize : cacheSizes) { + for (final CacheSize cacheSize : cacheSizes) { if (cacheSize != CacheSize.UNKNOWN) { filters.add(new SizeFilter(cacheSize)); } diff --git a/main/src/cgeo/geocaching/filter/StateFilter.java b/main/src/cgeo/geocaching/filter/StateFilter.java index ebe133c..f574045 100644 --- a/main/src/cgeo/geocaching/filter/StateFilter.java +++ b/main/src/cgeo/geocaching/filter/StateFilter.java @@ -4,6 +4,8 @@ import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import android.content.res.Resources; import java.util.ArrayList; @@ -26,7 +28,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cache.isFound(); } @@ -39,7 +41,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return !cache.isFound(); } @@ -51,7 +53,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cache.isArchived(); } } @@ -62,7 +64,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cache.isDisabled(); } } @@ -73,7 +75,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cache.isPremiumMembersOnly(); } } @@ -84,7 +86,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return !cache.isPremiumMembersOnly(); } } @@ -95,7 +97,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cache.isLogOffline(); } } @@ -106,7 +108,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cache.isOffline(); } } @@ -117,7 +119,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return !cache.isOffline(); } } @@ -125,6 +127,7 @@ abstract class StateFilter extends AbstractFilter { public static class Factory implements IFilterFactory { @Override + @NonNull public List<StateFilter> getFilters() { final List<StateFilter> filters = new ArrayList<>(6); filters.add(new StateFoundFilter()); diff --git a/main/src/cgeo/geocaching/filter/TerrainFilter.java b/main/src/cgeo/geocaching/filter/TerrainFilter.java index 7da6a19..977e4a5 100644 --- a/main/src/cgeo/geocaching/filter/TerrainFilter.java +++ b/main/src/cgeo/geocaching/filter/TerrainFilter.java @@ -3,6 +3,8 @@ package cgeo.geocaching.filter; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.ArrayList; import java.util.List; @@ -13,7 +15,7 @@ class TerrainFilter extends AbstractRangeFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { final float terrain = cache.getTerrain(); return rangeMin <= terrain && terrain < rangeMax; } @@ -23,6 +25,7 @@ class TerrainFilter extends AbstractRangeFilter { private static final int TERRAIN_MAX = 7; @Override + @NonNull public List<IFilter> getFilters() { final ArrayList<IFilter> filters = new ArrayList<>(TERRAIN_MAX); for (int terrain = TERRAIN_MIN; terrain <= TERRAIN_MAX; terrain++) { diff --git a/main/src/cgeo/geocaching/filter/TrackablesFilter.java b/main/src/cgeo/geocaching/filter/TrackablesFilter.java index d836a0f..7ad06a1 100644 --- a/main/src/cgeo/geocaching/filter/TrackablesFilter.java +++ b/main/src/cgeo/geocaching/filter/TrackablesFilter.java @@ -4,6 +4,8 @@ import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.Collections; import java.util.List; @@ -13,11 +15,12 @@ class TrackablesFilter extends AbstractFilter implements IFilterFactory { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cache.hasTrackables(); } @Override + @NonNull public List<TrackablesFilter> getFilters() { return Collections.singletonList(this); } diff --git a/main/src/cgeo/geocaching/filter/TypeFilter.java b/main/src/cgeo/geocaching/filter/TypeFilter.java index d363d39..412cbc2 100644 --- a/main/src/cgeo/geocaching/filter/TypeFilter.java +++ b/main/src/cgeo/geocaching/filter/TypeFilter.java @@ -3,6 +3,8 @@ package cgeo.geocaching.filter; import cgeo.geocaching.Geocache; import cgeo.geocaching.enumerations.CacheType; +import org.eclipse.jdt.annotation.NonNull; + import java.util.LinkedList; import java.util.List; @@ -15,7 +17,7 @@ class TypeFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cacheType == cache.getType(); } @@ -27,10 +29,11 @@ class TypeFilter extends AbstractFilter { public static class Factory implements IFilterFactory { @Override + @NonNull public List<IFilter> getFilters() { final CacheType[] types = CacheType.values(); final List<IFilter> filters = new LinkedList<>(); - for (CacheType cacheType : types) { + for (final CacheType cacheType : types) { if (cacheType != CacheType.ALL) { filters.add(new TypeFilter(cacheType)); } diff --git a/main/src/cgeo/geocaching/gcvote/GCVote.java b/main/src/cgeo/geocaching/gcvote/GCVote.java index eaf7687..2985e89 100644 --- a/main/src/cgeo/geocaching/gcvote/GCVote.java +++ b/main/src/cgeo/geocaching/gcvote/GCVote.java @@ -131,8 +131,11 @@ public final class GCVote { * @return {@code true} if the rating was submitted successfully */ public static boolean setRating(final Geocache cache, final float rating) { - if (!isVotingPossible(cache) || !isValidRating(rating)) { - throw new IllegalArgumentException(!isVotingPossible(cache) ? "voting is not possible for " + cache : "invalid rating " + rating); + if (!isVotingPossible(cache)) { + throw new IllegalArgumentException("voting is not possible for " + cache); + } + if (!isValidRating(rating)) { + throw new IllegalArgumentException("invalid rating " + rating); } final ImmutablePair<String, String> login = Settings.getGCvoteLogin(); @@ -199,7 +202,7 @@ public final class GCVote { return rating >= MIN_RATING && rating <= MAX_RATING; } - public static boolean isVotingPossible(final Geocache cache) { + public static boolean isVotingPossible(@NonNull final Geocache cache) { return Settings.isGCvoteLogin() && StringUtils.isNotBlank(cache.getGuid()) && cache.supportsGCVote(); } diff --git a/main/src/cgeo/geocaching/gcvote/GCVoteDialog.java b/main/src/cgeo/geocaching/gcvote/GCVoteDialog.java new file mode 100644 index 0000000..da14e0b --- /dev/null +++ b/main/src/cgeo/geocaching/gcvote/GCVoteDialog.java @@ -0,0 +1,74 @@ +package cgeo.geocaching.gcvote; + +import cgeo.geocaching.Geocache; +import cgeo.geocaching.R; +import cgeo.geocaching.gcvote.GCVoteRatingBarUtil.OnRatingChangeListener; +import cgeo.geocaching.settings.Settings; + +import org.eclipse.jdt.annotation.Nullable; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.view.ContextThemeWrapper; +import android.view.View; +import android.widget.Button; + +/** + * Small dialog showing only a rating bar to vote on GCVote.com. Confirming the dialog will send the vote over the + * network (in the background). + */ +public class GCVoteDialog { + + public static void show(final Activity context, final Geocache cache, final @Nullable Runnable afterVoteSent) { + final Context themedContext; + + if (Settings.isLightSkin() && VERSION.SDK_INT < VERSION_CODES.HONEYCOMB) { + themedContext = new ContextThemeWrapper(context, R.style.dark); + } else { + themedContext = context; + } + + final View votingLayout = View.inflate(themedContext, R.layout.gcvote_dialog, null); + + final AlertDialog.Builder builder = new AlertDialog.Builder(themedContext); + builder.setView(votingLayout); + builder.setPositiveButton(R.string.cache_menu_vote, new OnClickListener() { + + @Override + public void onClick(final DialogInterface dialog, final int which) { + vote(cache, GCVoteRatingBarUtil.getRating(votingLayout), afterVoteSent); + } + }); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(final DialogInterface dialog, final int whichButton) { + dialog.dismiss(); + } + }); + final AlertDialog dialog = builder.create(); + + GCVoteRatingBarUtil.initializeRatingBar(cache, votingLayout, new OnRatingChangeListener() { + + @Override + public void onRatingChanged(final float stars) { + final Button button = dialog.getButton(DialogInterface.BUTTON_POSITIVE); + // this listener might be fired already while the dialog is not yet shown + if (button != null) { + button.setEnabled(GCVote.isValidRating(stars)); + } + } + }); + dialog.show(); + dialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(GCVote.isValidRating(cache.getMyVote())); + } + + protected static void vote(final Geocache cache, final float rating, final @Nullable Runnable afterVoteSent) { + new GCVotePoster(cache, rating, afterVoteSent).execute(); + } + +} diff --git a/main/src/cgeo/geocaching/gcvote/GCVotePoster.java b/main/src/cgeo/geocaching/gcvote/GCVotePoster.java new file mode 100644 index 0000000..924aa56 --- /dev/null +++ b/main/src/cgeo/geocaching/gcvote/GCVotePoster.java @@ -0,0 +1,53 @@ +package cgeo.geocaching.gcvote; + +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.DataStore; +import cgeo.geocaching.Geocache; +import cgeo.geocaching.R; +import cgeo.geocaching.utils.Log; + +import org.eclipse.jdt.annotation.Nullable; + +import android.os.AsyncTask; +import android.widget.Toast; + +class GCVotePoster extends AsyncTask<Void, Void, Boolean> { + + private final Geocache cache; + private final float rating; + private final @Nullable Runnable afterVoteSent; + + public GCVotePoster(final Geocache cache, final float rating, final @Nullable Runnable afterVoteSent) { + this.cache = cache; + this.rating = rating; + this.afterVoteSent = afterVoteSent; + } + + @Override + protected Boolean doInBackground(final Void... inputs) { + try { + if (GCVote.isValidRating(rating) && GCVote.isVotingPossible(cache)) { + // store locally + cache.setMyVote(rating); + DataStore.saveChangedCache(cache); + + // send over network + return GCVote.setRating(cache, rating); + } + } catch (final RuntimeException e) { + Log.e("GCVoteAsyncTask.doInBackground", e); + } + + return false; + } + + @Override + protected void onPostExecute(final Boolean status) { + final CgeoApplication context = CgeoApplication.getInstance(); + final String text = context.getString(status ? R.string.gcvote_sent : R.string.err_gcvote_send_rating); + Toast.makeText(context, text, Toast.LENGTH_SHORT).show(); + if (afterVoteSent != null) { + afterVoteSent.run(); + } + } +}
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/gcvote/GCVoteRatingBarUtil.java b/main/src/cgeo/geocaching/gcvote/GCVoteRatingBarUtil.java new file mode 100644 index 0000000..2d485bd --- /dev/null +++ b/main/src/cgeo/geocaching/gcvote/GCVoteRatingBarUtil.java @@ -0,0 +1,58 @@ +package cgeo.geocaching.gcvote; + +import butterknife.ButterKnife; + +import cgeo.geocaching.Geocache; +import cgeo.geocaching.R; + +import org.eclipse.jdt.annotation.Nullable; + +import android.view.View; +import android.widget.RatingBar; +import android.widget.RatingBar.OnRatingBarChangeListener; +import android.widget.TextView; + +/** + * TODO: convert to fragment + * + */ +public final class GCVoteRatingBarUtil { + public interface OnRatingChangeListener { + public void onRatingChanged(final float stars); + } + + private GCVoteRatingBarUtil() { + // utility class + } + + public static void initializeRatingBar(final Geocache cache, final View parentView, @Nullable final OnRatingChangeListener changeListener) { + if (GCVote.isVotingPossible(cache)) { + final RatingBar ratingBar = ButterKnife.findById(parentView, R.id.gcvoteRating); + final TextView label = ButterKnife.findById(parentView, R.id.gcvoteLabel); + ratingBar.setVisibility(View.VISIBLE); + label.setVisibility(View.VISIBLE); + ratingBar.setOnRatingBarChangeListener(new OnRatingBarChangeListener() { + + @Override + public void onRatingChanged(final RatingBar ratingBar, final float stars, final boolean fromUser) { + // 0.5 is not a valid rating, therefore we must limit + final float rating = GCVote.isValidRating(stars) ? stars : 0; + if (rating < stars) { + ratingBar.setRating(rating); + } + label.setText(GCVote.getDescription(rating)); + if (changeListener != null) { + changeListener.onRatingChanged(rating); + } + } + }); + ratingBar.setRating(cache.getMyVote()); + } + } + + public static float getRating(final View parentView) { + final RatingBar ratingBar = ButterKnife.findById(parentView, R.id.gcvoteRating); + return ratingBar.getRating(); + } + +} diff --git a/main/src/cgeo/geocaching/list/StoredList.java b/main/src/cgeo/geocaching/list/StoredList.java index 4291a0a..0ffd58a 100644 --- a/main/src/cgeo/geocaching/list/StoredList.java +++ b/main/src/cgeo/geocaching/list/StoredList.java @@ -106,11 +106,8 @@ public final class StoredList extends AbstractList { final List<AbstractList> lists = new ArrayList<>(); lists.addAll(getSortedLists()); - if (exceptListId > StoredList.TEMPORARY_LIST.id) { - final StoredList exceptList = DataStore.getList(exceptListId); - if (exceptList != null) { - lists.remove(exceptList); - } + if (exceptListId == StoredList.STANDARD_LIST_ID || exceptListId >= DataStore.customListIdOffset) { + lists.remove(DataStore.getList(exceptListId)); } if (!onlyConcreteLists) { diff --git a/main/src/cgeo/geocaching/loaders/AddressGeocacheListLoader.java b/main/src/cgeo/geocaching/loaders/AddressGeocacheListLoader.java deleted file mode 100644 index e1573c9..0000000 --- a/main/src/cgeo/geocaching/loaders/AddressGeocacheListLoader.java +++ /dev/null @@ -1,23 +0,0 @@ -package cgeo.geocaching.loaders; - -import cgeo.geocaching.SearchResult; -import cgeo.geocaching.connector.gc.GCParser; -import cgeo.geocaching.settings.Settings; - -import android.content.Context; - -public class AddressGeocacheListLoader extends AbstractSearchLoader { - - private final String address; - - public AddressGeocacheListLoader(Context context, String address) { - super(context); - this.address = address; - } - - @Override - public SearchResult runSearch() { - return GCParser.searchByAddress(address, Settings.getCacheType(), Settings.isShowCaptcha(), this); - } - -} diff --git a/main/src/cgeo/geocaching/location/AndroidGeocoder.java b/main/src/cgeo/geocaching/location/AndroidGeocoder.java new file mode 100644 index 0000000..98ea285 --- /dev/null +++ b/main/src/cgeo/geocaching/location/AndroidGeocoder.java @@ -0,0 +1,59 @@ +package cgeo.geocaching.location; + +import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.RxUtils; + +import org.apache.commons.collections4.CollectionUtils; +import org.eclipse.jdt.annotation.NonNull; + +import rx.Observable; +import rx.functions.Func0; + +import android.content.Context; +import android.location.Address; +import android.location.Geocoder; + +import java.util.List; +import java.util.Locale; + +/** + * Encapsulation of the Android {@link Geocoder} with default error handling. All methods of this class + * are blocking and will do network lookups. + * + */ +public class AndroidGeocoder { + private final Geocoder geocoder; + + public AndroidGeocoder(final Context context) { + geocoder = new Geocoder(context, Locale.getDefault()); + } + + /** + * Retrieve addresses from a textual location using Android geocoding API. The work happens on the network + * scheduler. + * + * @param keyword + * the location + * @return an observable containing zero or more locations + * + * @see Geocoder#getFromLocationName(String, int) + */ + public Observable<Address> getFromLocationName(@NonNull final String keyword) { + return Observable.defer(new Func0<Observable<Address>>() { + @Override + public Observable<Address> call() { + try { + final List<Address> addresses = geocoder.getFromLocationName(keyword, 20); + if (CollectionUtils.isEmpty(addresses)) { + return Observable.error(new RuntimeException("no result from Android geocoder")); + } + return Observable.from(addresses); + } catch (final Exception e) { + Log.i("Unable to use Android geocoder: " + e.getMessage()); + return Observable.error(e); + } + } + }).subscribeOn(RxUtils.networkScheduler); + } + +} diff --git a/main/src/cgeo/geocaching/location/GCGeocoder.java b/main/src/cgeo/geocaching/location/GCGeocoder.java new file mode 100644 index 0000000..549044f --- /dev/null +++ b/main/src/cgeo/geocaching/location/GCGeocoder.java @@ -0,0 +1,65 @@ +package cgeo.geocaching.location; + +import cgeo.geocaching.network.Network; +import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.RxUtils; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; + +import rx.Observable; +import rx.functions.Func0; + +import android.location.Address; + +import java.util.Locale; + +public class GCGeocoder { + + private GCGeocoder() { + // Do not instantiate + } + + /** + * Retrieve addresses from a textual location using geocaching.com geocoding API. The work happens on the network + * scheduler. + * + * @param address + * the location + * @return an observable containing zero or more locations + * + * @see android.location.Geocoder#getFromLocationName(String, int) + */ + public static Observable<Address> getFromLocationName(@NonNull final String address) { + return Observable.defer(new Func0<Observable<Address>>() { + @Override + public Observable<Address> call() { + if (!Settings.isGCConnectorActive()) { + return Observable.error(new RuntimeException("geocaching.com connector is not active")); + } + final ObjectNode response = Network.requestJSON("https://www.geocaching.com/api/geocode", new Parameters("q", address)); + if (response == null || !StringUtils.equalsIgnoreCase(response.path("status").asText(), "success")) { + return Observable.error(new RuntimeException("unable to use geocaching.com geocoder")); + } + + final JsonNode data = response.path("data"); + final Address geocodedAddress = new Address(Locale.getDefault()); + try { + geocodedAddress.setLatitude(data.get("lat").asDouble()); + geocodedAddress.setLongitude(data.get("lng").asDouble()); + geocodedAddress.setAddressLine(0, address); + return Observable.just(geocodedAddress); + } catch (final Exception e) { + Log.e("unable to decode answer from geocaching.com geocoder", e); + return Observable.error(e); + } + } + }).subscribeOn(RxUtils.networkScheduler); + } + +} diff --git a/main/src/cgeo/geocaching/location/Geocoder.java b/main/src/cgeo/geocaching/location/Geocoder.java deleted file mode 100644 index 1582daa..0000000 --- a/main/src/cgeo/geocaching/location/Geocoder.java +++ /dev/null @@ -1,61 +0,0 @@ -package cgeo.geocaching.location; - -import cgeo.geocaching.utils.Log; - -import org.apache.commons.lang3.StringUtils; -import org.eclipse.jdt.annotation.NonNull; - -import android.content.Context; -import android.location.Address; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Locale; - -/** - * Encapsulation of the Android {@link android.location.Geocoder} with default error handling. All methods of this class - * are blocking and will do network lookups. - * - */ -public class Geocoder { - private final android.location.Geocoder geocoder; - - public Geocoder(final Context context) { - geocoder = new android.location.Geocoder(context, Locale.getDefault()); - } - - /** - * @param keyword - * @return - * - * @see android.location.Geocoder#getFromLocationName(String, int) - */ - public @NonNull List<Address> getFromLocationName(final String keyword) { - try { - return geocoder.getFromLocationName(keyword, 20); - } catch (final Exception e) { - handleException(e); - return Collections.emptyList(); - } - } - - public @NonNull List<Address> getFromLocation(final Geopoint coords) { - try { - return geocoder.getFromLocation(coords.getLatitude(), coords.getLongitude(), 20); - } catch (final IOException e) { - handleException(e); - return Collections.emptyList(); - } - } - - private static void handleException(final Exception e) { - // non Google devices come without the geocoder - if (StringUtils.containsIgnoreCase(e.getMessage(), "Service not Available")) { - Log.i("No geocoder available"); - } - else { - Log.e("Geocoder", e); - } - } -} diff --git a/main/src/cgeo/geocaching/location/GeopointParser.java b/main/src/cgeo/geocaching/location/GeopointParser.java index e73e787..a6b8e45 100644 --- a/main/src/cgeo/geocaching/location/GeopointParser.java +++ b/main/src/cgeo/geocaching/location/GeopointParser.java @@ -122,7 +122,7 @@ class GeopointParser { // Nothing found with "N 52...", try to match string as decimal degree parts (i.e. multiple doubles) try { - final String[] items = StringUtils.split(text.trim()); + final String[] items = StringUtils.split(StringUtils.trimToEmpty(text)); if (items.length > 0 && items.length <= 2) { final int index = (latlon == LatLon.LON ? items.length - 1 : 0); final String textPart = items[index]; diff --git a/main/src/cgeo/geocaching/location/MapQuestGeocoder.java b/main/src/cgeo/geocaching/location/MapQuestGeocoder.java new file mode 100644 index 0000000..537ae40 --- /dev/null +++ b/main/src/cgeo/geocaching/location/MapQuestGeocoder.java @@ -0,0 +1,117 @@ +package cgeo.geocaching.location; + +import cgeo.geocaching.network.Network; +import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.RxUtils; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; + +import rx.Observable; +import rx.Observable.OnSubscribe; +import rx.Subscriber; +import rx.functions.Func0; + +import android.location.Address; + +import java.util.Locale; + +public class MapQuestGeocoder { + + private static final String MAPQUEST_KEY = "Fmjtd|luurn1u2n9,bs=o5-9wynua"; + + private MapQuestGeocoder() { + // Do not instantiate + } + + /** + * Retrieve addresses from a textual location using MapQuest geocoding API. The work happens on the network + * scheduler. + * + * @param address + * the location + * @return an observable containing zero or more locations + * + * @see android.location.Geocoder#getFromLocationName(String, int) + */ + public static Observable<Address> getFromLocationName(@NonNull final String address) { + return Observable.defer(new Func0<Observable<Address>>() { + @Override + public Observable<Address> call() { + final ObjectNode response = Network.requestJSON("https://www.mapquestapi.com/geocoding/v1/address", + new Parameters("key", MAPQUEST_KEY, "location", address, "maxResults", "20", "thumbMaps", "false")); + if (response == null) { + Log.w("MapQuest decoder error: no response"); + return Observable.error(new RuntimeException("no answer from MapQuest geocoder")); + } + final int statusCode = response.path("info").path("statuscode").asInt(-1); + if (statusCode != 0) { + Log.w("MapQuest decoder error: statuscode is not 0"); + return Observable.error(new RuntimeException("no correct answer from MapQuest geocoder")); + } + return Observable.create(new OnSubscribe<Address>() { + @Override + public void call(final Subscriber<? super Address> subscriber) { + try { + for (final JsonNode address: response.get("results").get(0).get("locations")) { + subscriber.onNext(mapquestToAddress(address)); + } + subscriber.onCompleted(); + } catch (final Exception e) { + Log.e("Error decoding MapQuest address", e); + subscriber.onError(e); + } + } + }); + } + }).subscribeOn(RxUtils.networkScheduler); + } + + private static Address mapquestToAddress(final JsonNode mapquestAddress) { + final Address address = new Address(Locale.getDefault()); + for (int i = 1; i <= 6; i++) { + final String adminAreaName = "adminArea" + i; + setComponent(address, mapquestAddress, adminAreaName, mapquestAddress.path(adminAreaName + "Type").asText()); + } + setComponent(address, mapquestAddress, "postalCode", "PostalCode"); + int index = 0; + for (final String addressComponent: new String[]{ mapquestAddress.path("street").asText(), address.getSubLocality(), address.getLocality(), + address.getPostalCode(), address.getSubAdminArea(), address.getAdminArea(), address.getCountryCode() }) { + if (StringUtils.isNotBlank(addressComponent)) { + address.setAddressLine(index++, addressComponent); + } + } + address.setLatitude(mapquestAddress.get("latLng").get("lat").asDouble()); + address.setLongitude(mapquestAddress.get("latLng").get("lng").asDouble()); + return address; + } + + private static void setComponent(final Address address, final JsonNode mapquestAddress, final String adminArea, final String adminAreaType) { + final String content = StringUtils.trimToNull(mapquestAddress.path(adminArea).asText()); + switch (adminAreaType) { + case "City": + address.setLocality(content); + break; + case "Neighborhood": + address.setSubLocality(content); + break; + case "PostalCode": + address.setPostalCode(content); + break; + case "State": + address.setAdminArea(content); + break; + case "County": + address.setSubAdminArea(content); + break; + case "Country": + address.setCountryCode(content); + break; + } + } + +} diff --git a/main/src/cgeo/geocaching/location/Viewport.java b/main/src/cgeo/geocaching/location/Viewport.java index e482828..b885336 100644 --- a/main/src/cgeo/geocaching/location/Viewport.java +++ b/main/src/cgeo/geocaching/location/Viewport.java @@ -87,7 +87,7 @@ public final class Viewport { */ public int count(final @NonNull Collection<? extends ICoordinates> points) { int total = 0; - for (ICoordinates point: points) { + for (final ICoordinates point: points) { if (point != null && contains(point)) { total += 1; } @@ -102,7 +102,7 @@ public final class Viewport { /** * Check whether another viewport is fully included into the current one. - * + * * @param vp * the other viewport * @return true if the viewport is fully included into this one, false otherwise @@ -118,6 +118,7 @@ public final class Viewport { * the database table to use as prefix, or null if no prefix is required * @return the string without the "where" keyword */ + @NonNull public StringBuilder sqlWhere(@Nullable final String dbTable) { final String prefix = dbTable == null ? "" : (dbTable + "."); return new StringBuilder(prefix).append("latitude >= ").append(getLatitudeMin()).append(" and ") diff --git a/main/src/cgeo/geocaching/maps/CGeoMap.java b/main/src/cgeo/geocaching/maps/CGeoMap.java index 2868679..f2a5146 100644 --- a/main/src/cgeo/geocaching/maps/CGeoMap.java +++ b/main/src/cgeo/geocaching/maps/CGeoMap.java @@ -34,6 +34,7 @@ import cgeo.geocaching.maps.interfaces.MapViewImpl; import cgeo.geocaching.maps.interfaces.OnMapDragListener; import cgeo.geocaching.sensors.GeoData; import cgeo.geocaching.sensors.GeoDirHandler; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.dialog.LiveMapInfoDialogBuilder; import cgeo.geocaching.utils.AngleUtils; @@ -133,7 +134,6 @@ public class CGeoMap extends AbstractMap implements ViewFactory { // Those are initialized in onCreate() and will never be null afterwards private Resources res; private Activity activity; - private CgeoApplication app; private MapItemFactory mapItemFactory; private String mapTitle; final private LeastRecentlyUsedSet<Geocache> caches = new LeastRecentlyUsedSet<>(MAX_CACHES + DataStore.getAllCachesCount()); @@ -382,7 +382,6 @@ public class CGeoMap extends AbstractMap implements ViewFactory { // class init res = this.getResources(); activity = this.getActivity(); - app = (CgeoApplication) activity.getApplication(); final MapProvider mapProvider = Settings.getMapProvider(); mapItemFactory = mapProvider.getMapItemFactory(); @@ -491,7 +490,7 @@ public class CGeoMap extends AbstractMap implements ViewFactory { }); } - if (!app.isLiveMapHintShownInThisSession() && Settings.getLiveMapHintShowCount() <= 3) { + if (!CgeoApplication.getInstance().isLiveMapHintShownInThisSession() && Settings.getLiveMapHintShowCount() <= 3) { LiveMapInfoDialogBuilder.create(activity).show(); } } @@ -955,7 +954,7 @@ public class CGeoMap extends AbstractMap implements ViewFactory { // minimum change of location in fraction of map width/height (whatever is smaller) for position overlay update private static final float MIN_LOCATION_DELTA = 0.01f; - Location currentLocation = CgeoApplication.getInstance().currentGeo(); + Location currentLocation = Sensors.getInstance().currentGeo(); float currentHeading; private long timeLastPositionOverlayCalculation = 0; @@ -1539,7 +1538,7 @@ public class CGeoMap extends AbstractMap implements ViewFactory { if (myLocSwitch != null) { myLocSwitch.setChecked(followMyLocation); if (followMyLocation) { - myLocationInMiddle(app.currentGeo()); + myLocationInMiddle(Sensors.getInstance().currentGeo()); } } } diff --git a/main/src/cgeo/geocaching/network/HtmlImage.java b/main/src/cgeo/geocaching/network/HtmlImage.java index d1d458d..97e0e45 100644 --- a/main/src/cgeo/geocaching/network/HtmlImage.java +++ b/main/src/cgeo/geocaching/network/HtmlImage.java @@ -44,7 +44,6 @@ import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.util.Date; /** * All-purpose image getter that can also be used as a ImageGetter interface when displaying caches. @@ -70,7 +69,7 @@ public class HtmlImage implements Html.ImageGetter { }; public static final String SHARED = "shared"; - final private String geocode; + @NonNull final private String geocode; /** * on error: return large error image, if {@code true}, otherwise empty 1x1 image */ @@ -91,34 +90,45 @@ public class HtmlImage implements Html.ImageGetter { // Background loading final private PublishSubject<Observable<String>> loading = PublishSubject.create(); - final private Observable<String> waitForEnd = Observable.merge(loading).publish().refCount(); + final private Observable<String> waitForEnd = Observable.merge(loading).cache(); final private CompositeSubscription subscription = new CompositeSubscription(waitForEnd.subscribe()); /** - * Create a new HtmlImage object with different behaviours depending on <tt>onlySave</tt> and <tt>view</tt> values. + * Create a new HtmlImage object with different behaviors depending on <tt>onlySave</tt> and <tt>view</tt> values. * There are the three possible use cases: * <ul> - * <li>If onlySave is true, getDrawable() will return null immediately and will queue the image retrieval - * and saving in the loading subject. Downloads will start in parallel when the blocking - * waitForBackgroundLoading() method is called, and they can be cancelled through the given handler.</li> - * <li>If onlySave is false and the instance is called through fetchDrawable(), then an observable for the - * given URL will be returned. This observable will emit the local copy of the image if it is present</li> - * regardless of its freshness, then if needed an updated fresher copy after retrieving it from the network. - * <li>If onlySave is false and the instance is used as an ImageGetter, only the final version of the - * image will be returned, unless a view has been provided. If it has, then a dummy drawable is returned - * and is updated when the image is available, possibly several times if we had a stale copy of the image - * and then got a new one from the network.</li> + * <li>If onlySave is true, {@link #getDrawable(String)} will return <tt>null</tt> immediately and will queue the + * image retrieval and saving in the loading subject. Downloads will start in parallel when the blocking + * {@link #waitForEndObservable(cgeo.geocaching.utils.CancellableHandler)} method is called, and they can be + * cancelled through the given handler.</li> + * <li>If <tt>onlySave</tt> is <tt>false</tt> and the instance is called through {@link #fetchDrawable(String)}, + * then an observable for the given URL will be returned. This observable will emit the local copy of the image if + * it is present regardless of its freshness, then if needed an updated fresher copy after retrieving it from the + * network.</li> + * <li>If <tt>onlySave</tt> is <tt>false</tt> and the instance is used as an {@link android.text.Html.ImageGetter}, + * only the final version of the image will be returned, unless a view has been provided. If it has, then a dummy + * drawable is returned and is updated when the image is available, possibly several times if we had a stale copy of + * the image and then got a new one from the network.</li> * </ul> * - * @param geocode the geocode of the item for which we are requesting the image - * @param returnErrorImage set to <tt>true</tt> if an error image should be returned in case of a problem, - * <tt>false</tt> to get a transparent 1x1 image instead - * @param listId the list this cache belongs to, used to determine if an older image for the offline case can be used or not - * @param onlySave if set to <tt>true</tt>, {@link #getDrawable(String)} will only fetch and store the image, not return it - * @param view if non-null, {@link #getDrawable(String)} will return an initially empty drawable which will be redrawn when - * the image is ready through an invalidation of the given view + * @param geocode + * the geocode of the item for which we are requesting the image, or {@link #SHARED} to use the shared + * cache directory + * @param returnErrorImage + * set to <tt>true</tt> if an error image should be returned in case of a problem, <tt>false</tt> to get + * a transparent 1x1 image instead + * @param listId + * the list this cache belongs to, used to determine if an older image for the offline case can be used + * or not + * @param onlySave + * if set to <tt>true</tt>, {@link #getDrawable(String)} will only fetch and store the image, not return + * it + * @param view + * if non-null, {@link #getDrawable(String)} will return an initially empty drawable which will be + * redrawn when + * the image is ready through an invalidation of the given view */ - public HtmlImage(final String geocode, final boolean returnErrorImage, final int listId, final boolean onlySave, final TextView view) { + public HtmlImage(@NonNull final String geocode, final boolean returnErrorImage, final int listId, final boolean onlySave, final TextView view) { this.geocode = geocode; this.returnErrorImage = returnErrorImage; this.listId = listId; @@ -132,12 +142,12 @@ public class HtmlImage implements Html.ImageGetter { } /** - * Create a new HtmlImage object with different behaviours depending on <tt>onlySave</tt> value. No view object + * Create a new HtmlImage object with different behaviors depending on <tt>onlySave</tt> value. No view object * will be tied to this HtmlImage. * * For documentation, see {@link #HtmlImage(String, boolean, int, boolean, TextView)}. */ - public HtmlImage(final String geocode, final boolean returnErrorImage, final int listId, final boolean onlySave) { + public HtmlImage(@NonNull final String geocode, final boolean returnErrorImage, final int listId, final boolean onlySave) { this(geocode, returnErrorImage, listId, onlySave, null); } @@ -243,23 +253,23 @@ public class HtmlImage implements Html.ImageGetter { } if (onlySave) { subscriber.onCompleted(); - } else { - RxUtils.computationScheduler.createWorker().schedule(new Action0() { - @Override - public void call() { - final ImmutablePair<BitmapDrawable, Boolean> loaded = loadFromDisk(); - final BitmapDrawable image = loaded.left; - if (image != null) { - subscriber.onNext(image); - } else { - subscriber.onNext(returnErrorImage ? - new BitmapDrawable(resources, BitmapFactory.decodeResource(resources, R.drawable.image_not_loaded)) : - ImageUtils.getTransparent1x1Drawable(resources)); - } - subscriber.onCompleted(); - } - }); + return; } + RxUtils.computationScheduler.createWorker().schedule(new Action0() { + @Override + public void call() { + final ImmutablePair<BitmapDrawable, Boolean> loaded = loadFromDisk(); + final BitmapDrawable image = loaded.left; + if (image != null) { + subscriber.onNext(image); + } else { + subscriber.onNext(returnErrorImage ? + new BitmapDrawable(resources, BitmapFactory.decodeResource(resources, R.drawable.image_not_loaded)) : + ImageUtils.getTransparent1x1Drawable(resources)); + } + subscriber.onCompleted(); + } + }); } }); } @@ -338,7 +348,7 @@ public class HtmlImage implements Html.ImageGetter { * @return A pair whose first element is the bitmap if available, and the second one is <code>true</code> if the image is present and fresh enough. */ @NonNull - private ImmutablePair<Bitmap, Boolean> loadImageFromStorage(final String url, final String pseudoGeocode, final boolean forceKeep) { + private ImmutablePair<Bitmap, Boolean> loadImageFromStorage(final String url, @NonNull final String pseudoGeocode, final boolean forceKeep) { try { final File file = LocalStorage.getStorageFile(pseudoGeocode, url, true, false); final ImmutablePair<Bitmap, Boolean> image = loadCachedImage(file, forceKeep); @@ -390,7 +400,7 @@ public class HtmlImage implements Html.ImageGetter { @NonNull private ImmutablePair<Bitmap, Boolean> loadCachedImage(final File file, final boolean forceKeep) { if (file.exists()) { - final boolean freshEnough = listId >= StoredList.STANDARD_LIST_ID || file.lastModified() > (new Date().getTime() - (24 * 60 * 60 * 1000)) || forceKeep; + final boolean freshEnough = listId >= StoredList.STANDARD_LIST_ID || file.lastModified() > (System.currentTimeMillis() - (24 * 60 * 60 * 1000)) || forceKeep; if (freshEnough && onlySave) { return ImmutablePair.of((Bitmap) null, true); } diff --git a/main/src/cgeo/geocaching/network/OAuth.java b/main/src/cgeo/geocaching/network/OAuth.java index c23ffbf..4f1fcc0 100644 --- a/main/src/cgeo/geocaching/network/OAuth.java +++ b/main/src/cgeo/geocaching/network/OAuth.java @@ -8,7 +8,6 @@ import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNull; import java.util.ArrayList; -import java.util.Date; import java.util.List; public class OAuth { @@ -24,7 +23,7 @@ public class OAuth { "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), + "oauth_timestamp", Long.toString(System.currentTimeMillis() / 1000), "oauth_token", StringUtils.defaultString(tokens.getTokenPublic()), "oauth_version", "1.0"); params.sort(); @@ -41,10 +40,7 @@ public class OAuth { } /** - * percent encode following http://tools.ietf.org/html/rfc5849#section-3.6 - * - * @param url - * @return + * Percent encode following http://tools.ietf.org/html/rfc5849#section-3.6 */ static String percentEncode(@NonNull final String url) { return StringUtils.replace(Network.rfc3986URLEncode(url), "*", "%2A"); diff --git a/main/src/cgeo/geocaching/sensors/GeoData.java b/main/src/cgeo/geocaching/sensors/GeoData.java index 880efd0..b8b16fd 100644 --- a/main/src/cgeo/geocaching/sensors/GeoData.java +++ b/main/src/cgeo/geocaching/sensors/GeoData.java @@ -91,7 +91,7 @@ public class GeoData extends Location { final String homeLocationStr = Settings.getHomeLocation(); if (StringUtils.isNotBlank(homeLocationStr)) { try { - assert (homeLocationStr != null); + assert homeLocationStr != null; final Geopoint homeLocation = new Geopoint(homeLocationStr); Log.i("No last known location available, using home location"); final Location initialLocation = new Location(HOME_PROVIDER); diff --git a/main/src/cgeo/geocaching/sensors/GeoDirHandler.java b/main/src/cgeo/geocaching/sensors/GeoDirHandler.java index d043a4a..4743140 100644 --- a/main/src/cgeo/geocaching/sensors/GeoDirHandler.java +++ b/main/src/cgeo/geocaching/sensors/GeoDirHandler.java @@ -1,16 +1,11 @@ package cgeo.geocaching.sensors; -import cgeo.geocaching.CgeoApplication; -import cgeo.geocaching.settings.Settings; -import cgeo.geocaching.utils.AngleUtils; - import org.apache.commons.lang3.tuple.ImmutablePair; import rx.Observable; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; import rx.functions.Action1; -import rx.functions.Func1; import rx.functions.Func2; import rx.subscriptions.CompositeSubscription; @@ -34,8 +29,6 @@ public abstract class GeoDirHandler { public static final int UPDATE_GEODIR = 1 << 3; public static final int LOW_POWER = 1 << 4; - private static final CgeoApplication app = CgeoApplication.getInstance(); - /** * Update method called when new geodata is available. This method is called on the UI thread. * {@link #start(int)} must be called with the {@link #UPDATE_GEODATA} flag set. @@ -67,22 +60,6 @@ public abstract class GeoDirHandler { public void updateGeoDir(final GeoData geoData, final float direction) { } - private static Observable<Float> fixedDirection() { - return app.directionObservable().map(new Func1<Float, Float>() { - @Override - public Float call(final Float direction) { - final GeoData geoData = app.currentGeo(); - return fixDirection(geoData, direction); - } - }); - - } - - private static float fixDirection(final GeoData geoData, final float direction) { - final boolean useGPSBearing = !Settings.isUseCompass() || geoData.getSpeed() > 5; - return useGPSBearing ? AngleUtils.reverseDirectionNow(geoData.getBearing()) : direction; - } - private static <T> Observable<T> throttleIfNeeded(final Observable<T> observable, final long windowDuration, final TimeUnit unit) { return windowDuration > 0 ? observable.throttleFirst(windowDuration, unit) : observable; } @@ -108,8 +85,10 @@ public abstract class GeoDirHandler { public Subscription start(final int flags, final long windowDuration, final TimeUnit unit) { final CompositeSubscription subscriptions = new CompositeSubscription(); final boolean lowPower = (flags & LOW_POWER) != 0; + final Sensors sensors = Sensors.getInstance(); + if ((flags & UPDATE_GEODATA) != 0) { - subscriptions.add(throttleIfNeeded(app.geoDataObservable(lowPower), windowDuration, unit).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<GeoData>() { + subscriptions.add(throttleIfNeeded(sensors.geoDataObservable(lowPower), windowDuration, unit).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<GeoData>() { @Override public void call(final GeoData geoData) { updateGeoData(geoData); @@ -117,7 +96,7 @@ public abstract class GeoDirHandler { })); } if ((flags & UPDATE_DIRECTION) != 0) { - subscriptions.add(throttleIfNeeded(fixedDirection(), windowDuration, unit).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<Float>() { + subscriptions.add(throttleIfNeeded(sensors.directionObservable(), windowDuration, unit).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<Float>() { @Override public void call(final Float direction) { updateDirection(direction); @@ -126,10 +105,10 @@ public abstract class GeoDirHandler { } if ((flags & UPDATE_GEODIR) != 0) { // combineOnLatest() does not implement backpressure handling, so we need to explicitely use a backpressure operator there. - subscriptions.add(throttleIfNeeded(Observable.combineLatest(app.geoDataObservable(lowPower), app.directionObservable(), new Func2<GeoData, Float, ImmutablePair<GeoData, Float>>() { + subscriptions.add(throttleIfNeeded(Observable.combineLatest(sensors.geoDataObservable(lowPower), sensors.directionObservable(), new Func2<GeoData, Float, ImmutablePair<GeoData, Float>>() { @Override public ImmutablePair<GeoData, Float> call(final GeoData geoData, final Float direction) { - return ImmutablePair.of(geoData, fixDirection(geoData, direction)); + return ImmutablePair.of(geoData, direction); } }), windowDuration, unit).onBackpressureDrop().observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<ImmutablePair<GeoData, Float>>() { @Override diff --git a/main/src/cgeo/geocaching/sensors/Sensors.java b/main/src/cgeo/geocaching/sensors/Sensors.java new file mode 100644 index 0000000..498ec0e --- /dev/null +++ b/main/src/cgeo/geocaching/sensors/Sensors.java @@ -0,0 +1,160 @@ +package cgeo.geocaching.sensors; + +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.playservices.LocationProvider; +import cgeo.geocaching.sensors.GpsStatusProvider.Status; +import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.utils.AngleUtils; +import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.RxUtils; + +import org.eclipse.jdt.annotation.NonNull; + +import rx.Observable; +import rx.functions.Action1; +import rx.functions.Func1; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorManager; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class Sensors { + + private Observable<GeoData> geoDataObservable; + private Observable<GeoData> geoDataObservableLowPower; + private Observable<Float> directionObservable; + private Observable<Status> gpsStatusObservable; + @NonNull private volatile GeoData currentGeo = GeoData.DUMMY_LOCATION; + private volatile float currentDirection = 0.0f; + private volatile boolean hasValidLocation = false; + private final boolean hasMagneticFieldSensor; + private final CgeoApplication app = CgeoApplication.getInstance(); + + private static class InstanceHolder { + static final Sensors INSTANCE = new Sensors(); + } + + private final Action1<GeoData> rememberGeodataAction = new Action1<GeoData>() { + @Override + public void call(final GeoData geoData) { + currentGeo = geoData; + hasValidLocation = true; + } + }; + + private final Action1<Float> onNextrememberDirectionAction = new Action1<Float>() { + @Override + public void call(final Float direction) { + currentDirection = direction; + } + }; + + private Sensors() { + gpsStatusObservable = GpsStatusProvider.create(app).replay(1).refCount(); + final SensorManager sensorManager = (SensorManager) app.getSystemService(Context.SENSOR_SERVICE); + hasMagneticFieldSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null; + } + + public static final Sensors getInstance() { + return InstanceHolder.INSTANCE; + } + + public void setupGeoDataObservables(final boolean useGooglePlayServices, final boolean useLowPowerLocation) { + if (useGooglePlayServices) { + geoDataObservable = LocationProvider.getMostPrecise(app).doOnNext(rememberGeodataAction); + if (useLowPowerLocation) { + geoDataObservableLowPower = LocationProvider.getLowPower(app).doOnNext(rememberGeodataAction); + } else { + geoDataObservableLowPower = geoDataObservable; + } + } else { + geoDataObservable = RxUtils.rememberLast(GeoDataProvider.create(app).doOnNext(rememberGeodataAction), null); + geoDataObservableLowPower = geoDataObservable; + } + } + + private static final Func1<GeoData, Float> GPS_TO_DIRECTION = new Func1<GeoData, Float>() { + @Override + public Float call(final GeoData geoData) { + return AngleUtils.reverseDirectionNow(geoData.getBearing()); + } + }; + + public void setupDirectionObservable(final boolean useLowPower) { + // If we have no magnetic sensor, there is no point in trying to setup any, we will always get the direction from the GPS. + if (!hasMagneticFieldSensor) { + Log.i("No magnetic field sensor, using only the GPS for the orientation"); + directionObservable = RxUtils.rememberLast(geoDataObservableLowPower.map(GPS_TO_DIRECTION).doOnNext(onNextrememberDirectionAction), 0f); + return; + } + + // Combine the magnetic direction observable with the GPS when compass is disabled or speed is high enough. + + final AtomicBoolean useDirectionFromGps = new AtomicBoolean(false); + + final Observable<Float> magneticDirectionObservable = RotationProvider.create(app, useLowPower).onErrorResumeNext(new Func1<Throwable, Observable<? extends Float>>() { + @Override + public Observable<? extends Float> call(final Throwable throwable) { + return OrientationProvider.create(app); + } + }).onErrorResumeNext(new Func1<Throwable, Observable<? extends Float>>() { + @Override + public Observable<? extends Float> call(final Throwable throwable) { + Log.e("Device orientation will not be available as no suitable sensors were found, disabling compass"); + Settings.setUseCompass(false); + return Observable.<Float>never().startWith(0.0f); + } + }).filter(new Func1<Float, Boolean>() { + @Override + public Boolean call(final Float aFloat) { + return Settings.isUseCompass() && !useDirectionFromGps.get(); + } + }); + + final Observable<Float> directionFromGpsObservable = geoDataObservable(true).filter(new Func1<GeoData, Boolean>() { + @Override + public Boolean call(final GeoData geoData) { + final boolean useGps = geoData.getSpeed() > 5.0f; + useDirectionFromGps.set(useGps); + return useGps || !Settings.isUseCompass(); + } + }).map(GPS_TO_DIRECTION); + + directionObservable = RxUtils.rememberLast(Observable.merge(magneticDirectionObservable, directionFromGpsObservable).doOnNext(onNextrememberDirectionAction), 0f); + } + + public Observable<GeoData> geoDataObservable(final boolean lowPower) { + return lowPower ? geoDataObservableLowPower : geoDataObservable; + } + + public Observable<Float> directionObservable() { + return directionObservable; + } + + public Observable<Status> gpsStatusObservable() { + if (gpsStatusObservable == null) { + gpsStatusObservable = GpsStatusProvider.create(app).share(); + } + return gpsStatusObservable; + } + + @NonNull + public GeoData currentGeo() { + return currentGeo; + } + + public boolean hasValidLocation() { + return hasValidLocation; + } + + public float currentDirection() { + return currentDirection; + } + + public boolean hasMagneticFieldSensor() { + return hasMagneticFieldSensor; + } + +} diff --git a/main/src/cgeo/geocaching/settings/Settings.java b/main/src/cgeo/geocaching/settings/Settings.java index b678d3b..ad15d81 100644 --- a/main/src/cgeo/geocaching/settings/Settings.java +++ b/main/src/cgeo/geocaching/settings/Settings.java @@ -627,7 +627,7 @@ public class Settings { } public static boolean isMapTrail() { - return getBoolean(R.string.pref_maptrail, true); + return getBoolean(R.string.pref_maptrail, false); } public static void setMapTrail(final boolean showTrail) { @@ -808,6 +808,7 @@ public class Settings { * @return The cache type used for filtering or ALL if no filter is active. * Returns never null */ + @NonNull public static CacheType getCacheType() { return CacheType.getById(getString(R.string.pref_cachetype, CacheType.ALL.id)); } diff --git a/main/src/cgeo/geocaching/settings/SettingsActivity.java b/main/src/cgeo/geocaching/settings/SettingsActivity.java index 662ba33..99de30f 100644 --- a/main/src/cgeo/geocaching/settings/SettingsActivity.java +++ b/main/src/cgeo/geocaching/settings/SettingsActivity.java @@ -12,6 +12,7 @@ import cgeo.geocaching.connector.gc.GCConnector; import cgeo.geocaching.files.SimpleDirChooser; import cgeo.geocaching.maps.MapProviderFactory; import cgeo.geocaching.maps.interfaces.MapSource; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.utils.DatabaseBackupUtils; import cgeo.geocaching.utils.DebugUtils; import cgeo.geocaching.utils.Log; @@ -421,11 +422,12 @@ public class SettingsActivity extends PreferenceActivity { } private void initGeoDirPreferences() { + final Sensors sensors = Sensors.getInstance(); final Preference playServices = getPreference(R.string.pref_googleplayservices); playServices.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(final Preference preference, final Object newValue) { - CgeoApplication.getInstance().setupGeoDataObservables((Boolean) newValue, Settings.useLowPowerMode()); + sensors.setupGeoDataObservables((Boolean) newValue, Settings.useLowPowerMode()); return true; } }); @@ -433,10 +435,9 @@ public class SettingsActivity extends PreferenceActivity { getPreference(R.string.pref_lowpowermode).setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(final Preference preference, final Object newValue) { - final CgeoApplication app = CgeoApplication.getInstance(); final Boolean useLowPower = (Boolean) newValue; - app.setupGeoDataObservables(Settings.useGooglePlayServices(), useLowPower); - app.setupDirectionObservable(useLowPower); + sensors.setupGeoDataObservables(Settings.useGooglePlayServices(), useLowPower); + sensors.setupDirectionObservable(useLowPower); return true; } }); diff --git a/main/src/cgeo/geocaching/sorting/DateComparator.java b/main/src/cgeo/geocaching/sorting/DateComparator.java index 6d294db..af50213 100644 --- a/main/src/cgeo/geocaching/sorting/DateComparator.java +++ b/main/src/cgeo/geocaching/sorting/DateComparator.java @@ -1,7 +1,7 @@ package cgeo.geocaching.sorting; -import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; +import cgeo.geocaching.sensors.Sensors; import java.util.ArrayList; import java.util.Date; @@ -22,7 +22,7 @@ class DateComparator extends AbstractCacheComparator { final ArrayList<Geocache> list = new ArrayList<>(); list.add(cache1); list.add(cache2); - final DistanceComparator distanceComparator = new DistanceComparator(CgeoApplication.getInstance().currentGeo().getCoords(), list); + final DistanceComparator distanceComparator = new DistanceComparator(Sensors.getInstance().currentGeo().getCoords(), list); return distanceComparator.compare(cache1, cache2); } return dateDifference; diff --git a/main/src/cgeo/geocaching/sorting/SizeComparator.java b/main/src/cgeo/geocaching/sorting/SizeComparator.java index 9e911f5..8cb5178 100644 --- a/main/src/cgeo/geocaching/sorting/SizeComparator.java +++ b/main/src/cgeo/geocaching/sorting/SizeComparator.java @@ -9,11 +9,6 @@ import cgeo.geocaching.Geocache; class SizeComparator extends AbstractCacheComparator { @Override - protected boolean canCompare(final Geocache cache) { - return cache.getSize() != null; - } - - @Override protected int compareCaches(final Geocache cache1, final Geocache cache2) { return cache2.getSize().comparable - cache1.getSize().comparable; } diff --git a/main/src/cgeo/geocaching/ui/AddressListAdapter.java b/main/src/cgeo/geocaching/ui/AddressListAdapter.java index 691c8d2..901bffc 100644 --- a/main/src/cgeo/geocaching/ui/AddressListAdapter.java +++ b/main/src/cgeo/geocaching/ui/AddressListAdapter.java @@ -3,12 +3,13 @@ package cgeo.geocaching.ui; import butterknife.InjectView; import cgeo.geocaching.CacheListActivity; -import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.location.Units; +import cgeo.geocaching.sensors.Sensors; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import android.app.Activity; import android.location.Address; @@ -23,7 +24,7 @@ import java.util.ArrayList; public class AddressListAdapter extends ArrayAdapter<Address> { final private LayoutInflater inflater; - final private Geopoint location; + @NonNull final private Geopoint location; protected static final class ViewHolder extends AbstractViewHolder { @InjectView(R.id.label) protected TextView label; @@ -37,7 +38,7 @@ public class AddressListAdapter extends ArrayAdapter<Address> { public AddressListAdapter(final Activity context) { super(context, 0); inflater = context.getLayoutInflater(); - location = CgeoApplication.getInstance().currentGeo().getCoords(); + location = Sensors.getInstance().currentGeo().getCoords(); } @Override @@ -72,7 +73,7 @@ public class AddressListAdapter extends ArrayAdapter<Address> { } private CharSequence getDistanceText(final Address address) { - if (location != null && address.hasLatitude() && address.hasLongitude()) { + if (address.hasLatitude() && address.hasLongitude()) { return Units.getDistanceFromKilometers(location.distanceTo(new Geopoint(address.getLatitude(), address.getLongitude()))); } diff --git a/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java b/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java index 13887c0..e2af419 100644 --- a/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java +++ b/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java @@ -2,13 +2,13 @@ package cgeo.geocaching.ui; import butterknife.ButterKnife; -import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.ICoordinates; import cgeo.geocaching.R; import cgeo.geocaching.Waypoint; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.location.Units; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.utils.Formatter; import org.apache.commons.lang3.StringUtils; @@ -18,11 +18,9 @@ import org.eclipse.jdt.annotation.NonNull; import android.annotation.SuppressLint; import android.app.Activity; import android.content.res.Resources; -import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.LinearLayout; +import android.widget.RatingBar; import android.widget.RelativeLayout; import android.widget.TextView; @@ -74,33 +72,18 @@ public final class CacheDetailsCreator { final RelativeLayout layout = (RelativeLayout) activity.getLayoutInflater().inflate(R.layout.cache_information_item, null, false); final TextView nameView = ButterKnife.findById(layout, R.id.name); lastValueView = ButterKnife.findById(layout, R.id.value); - final LinearLayout layoutStars = ButterKnife.findById(layout, R.id.stars); nameView.setText(activity.getResources().getString(nameId)); lastValueView.setText(String.format("%.1f", value) + ' ' + activity.getResources().getString(R.string.cache_rating_of) + " " + String.format("%d", max)); - createStarImages(layoutStars, value, max); + + final RatingBar layoutStars = ButterKnife.findById(layout, R.id.stars); + layoutStars.setRating(value); layoutStars.setVisibility(View.VISIBLE); parentView.addView(layout); return layout; } - private void createStarImages(final ViewGroup starsContainer, final float value, final int max) { - final LayoutInflater inflater = LayoutInflater.from(activity); - - for (int i = 0; i < max; i++) { - final ImageView star = (ImageView) inflater.inflate(R.layout.star_image, starsContainer, false); - if (value - i >= 0.75) { - star.setImageResource(R.drawable.star_on); - } else if (value - i >= 0.25) { - star.setImageResource(R.drawable.star_half); - } else { - star.setImageResource(R.drawable.star_off); - } - starsContainer.addView(star); - } - } - public void addCacheState(final Geocache cache) { if (cache.isLogOffline() || cache.isArchived() || cache.isDisabled() || cache.isPremiumMembersOnly() || cache.isFound()) { final List<String> states = new ArrayList<>(5); @@ -135,7 +118,7 @@ public final class CacheDetailsCreator { if (target.getCoords() == null) { return null; } - return CgeoApplication.getInstance().currentGeo().getCoords().distanceTo(target); + return Sensors.getInstance().currentGeo().getCoords().distanceTo(target); } public void addRating(final Geocache cache) { @@ -150,7 +133,7 @@ public final class CacheDetailsCreator { } public void addSize(final Geocache cache) { - if (null != cache.getSize() && cache.showSize()) { + if (cache.showSize()) { add(R.string.cache_size, cache.getSize().getL10n()); } } diff --git a/main/src/cgeo/geocaching/ui/CacheListAdapter.java b/main/src/cgeo/geocaching/ui/CacheListAdapter.java index f080761..34cac01 100644 --- a/main/src/cgeo/geocaching/ui/CacheListAdapter.java +++ b/main/src/cgeo/geocaching/ui/CacheListAdapter.java @@ -3,7 +3,6 @@ package cgeo.geocaching.ui; import butterknife.InjectView; import cgeo.geocaching.CacheDetailActivity; -import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; import cgeo.geocaching.enumerations.CacheListType; @@ -11,6 +10,7 @@ import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.filter.IFilter; import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.sensors.GeoData; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.sorting.CacheComparator; import cgeo.geocaching.sorting.DistanceComparator; @@ -119,7 +119,7 @@ public class CacheListAdapter extends ArrayAdapter<Geocache> { public CacheListAdapter(final Activity activity, final List<Geocache> list, final CacheListType cacheListType) { super(activity, 0, list); - final GeoData currentGeo = CgeoApplication.getInstance().currentGeo(); + final GeoData currentGeo = Sensors.getInstance().currentGeo(); coords = currentGeo.getCoords(); this.res = activity.getResources(); this.list = list; diff --git a/main/src/cgeo/geocaching/ui/ImagesList.java b/main/src/cgeo/geocaching/ui/ImagesList.java index 458f8db..32e29bb 100644 --- a/main/src/cgeo/geocaching/ui/ImagesList.java +++ b/main/src/cgeo/geocaching/ui/ImagesList.java @@ -106,7 +106,7 @@ public class ImagesList { for (final Image img : images) { final LinearLayout rowView = (LinearLayout) inflater.inflate(R.layout.cache_image_item, imagesView, false); - assert(rowView != null); + assert rowView != null; if (StringUtils.isNotBlank(img.getTitle())) { final TextView titleView = ButterKnife.findById(rowView, R.id.title); @@ -121,7 +121,7 @@ public class ImagesList { } final ImageView imageView = (ImageView) inflater.inflate(R.layout.image_item, rowView, false); - assert(imageView != null); + assert imageView != null; subscriptions.add(AndroidObservable.bindActivity(activity, imgGetter.fetchDrawable(img.getUrl())).subscribe(new Action1<BitmapDrawable>() { @Override public void call(final BitmapDrawable image) { diff --git a/main/src/cgeo/geocaching/ui/WrappingGridView.java b/main/src/cgeo/geocaching/ui/WrappingGridView.java new file mode 100644 index 0000000..2c85887 --- /dev/null +++ b/main/src/cgeo/geocaching/ui/WrappingGridView.java @@ -0,0 +1,38 @@ +package cgeo.geocaching.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.GridView; + +/** + * GridView that will adjust its height to really use wrap_content. The standard GridView only shows one line of items. + * + * @see <a href="https://gist.github.com/runemart/9781609">https://gist.github.com/runemart/9781609</a> + * + */ +public class WrappingGridView extends GridView { + + public WrappingGridView(final Context context) { + super(context); + } + + public WrappingGridView(final Context context, final AttributeSet attrs) { + super(context, attrs); + } + + public WrappingGridView(final Context context, final AttributeSet attrs, final int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { + int heightSpec = heightMeasureSpec; + if (getLayoutParams().height == android.view.ViewGroup.LayoutParams.WRAP_CONTENT) { + // The two leftmost bits in the height measure spec have + // a special meaning, hence we can't use them to describe height. + heightSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); + } + super.onMeasure(widthMeasureSpec, heightSpec); + } + +}
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/utils/CryptUtils.java b/main/src/cgeo/geocaching/utils/CryptUtils.java index f2ff0c2..70d5895 100644 --- a/main/src/cgeo/geocaching/utils/CryptUtils.java +++ b/main/src/cgeo/geocaching/utils/CryptUtils.java @@ -23,8 +23,8 @@ public final class CryptUtils { } private static final byte[] EMPTY = {}; - private static char[] BASE64MAP1 = new char[64]; - private static byte[] BASE64MAP2 = new byte[128]; + private static final char[] BASE64MAP1 = new char[64]; + private static final byte[] BASE64MAP2 = new byte[128]; static { int i = 0; diff --git a/main/src/cgeo/geocaching/utils/Log.java b/main/src/cgeo/geocaching/utils/Log.java index f338a8e..2f51e42 100644 --- a/main/src/cgeo/geocaching/utils/Log.java +++ b/main/src/cgeo/geocaching/utils/Log.java @@ -43,56 +43,60 @@ public final class Log { Log.isDebug = isDebug; } + private static String addThreadInfo(final String msg) { + return new StringBuilder("[").append(Thread.currentThread().getName()).append("] ").append(msg).toString(); + } + public static void v(final String msg) { if (isDebug) { - android.util.Log.v(TAG, msg); + android.util.Log.v(TAG, addThreadInfo(msg)); } } public static void v(final String msg, final Throwable t) { if (isDebug) { - android.util.Log.v(TAG, msg, t); + android.util.Log.v(TAG, addThreadInfo(msg), t); } } public static void d(final String msg) { if (isDebug) { - android.util.Log.d(TAG, msg); + android.util.Log.d(TAG, addThreadInfo(msg)); } } public static void d(final String msg, final Throwable t) { if (isDebug) { - android.util.Log.d(TAG, msg, t); + android.util.Log.d(TAG, addThreadInfo(msg), t); } } public static void i(final String msg) { if (isDebug) { - android.util.Log.i(TAG, msg); + android.util.Log.i(TAG, addThreadInfo(msg)); } } public static void i(final String msg, final Throwable t) { if (isDebug) { - android.util.Log.i(TAG, msg, t); + android.util.Log.i(TAG, addThreadInfo(msg), t); } } public static void w(final String msg) { - android.util.Log.w(TAG, msg); + android.util.Log.w(TAG, addThreadInfo(msg)); } public static void w(final String msg, final Throwable t) { - android.util.Log.w(TAG, msg, t); + android.util.Log.w(TAG, addThreadInfo(msg), t); } public static void e(final String msg) { - android.util.Log.e(TAG, msg); + android.util.Log.e(TAG, addThreadInfo(msg)); } public static void e(final String msg, final Throwable t) { - android.util.Log.e(TAG, msg, t); + android.util.Log.e(TAG, addThreadInfo(msg), t); } /** @@ -116,7 +120,7 @@ public final class Log { Writer writer = null; try { writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true), CharEncoding.UTF_8)); - writer.write(msg); + writer.write(addThreadInfo(msg)); } catch (final IOException e) { Log.e("logToFile: cannot write to " + file, e); } finally { diff --git a/main/src/cgeo/geocaching/utils/RxUtils.java b/main/src/cgeo/geocaching/utils/RxUtils.java index beb5e18..af58214 100644 --- a/main/src/cgeo/geocaching/utils/RxUtils.java +++ b/main/src/cgeo/geocaching/utils/RxUtils.java @@ -11,6 +11,7 @@ import rx.functions.Action0; import rx.functions.Action1; import rx.functions.Func0; import rx.functions.Func1; +import rx.internal.util.RxThreadFactory; import rx.observables.BlockingObservable; import rx.observers.Subscribers; import rx.schedulers.Schedulers; @@ -25,8 +26,7 @@ import android.os.Process; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -39,12 +39,12 @@ public class RxUtils { public final static Scheduler computationScheduler = Schedulers.computation(); - public static final Scheduler networkScheduler = Schedulers.from(new ThreadPoolExecutor(10, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>())); + public static final Scheduler networkScheduler = Schedulers.from(Executors.newFixedThreadPool(10, new RxThreadFactory("network-"))); - public static final Scheduler refreshScheduler = Schedulers.from(new ThreadPoolExecutor(3, 3, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>())); + public static final Scheduler refreshScheduler = Schedulers.from(Executors.newFixedThreadPool(3, new RxThreadFactory("refresh-"))); private static final HandlerThread looperCallbacksThread = - new HandlerThread("Looper callbacks thread", Process.THREAD_PRIORITY_DEFAULT); + new HandlerThread("looper callbacks", Process.THREAD_PRIORITY_DEFAULT); static { looperCallbacksThread.start(); @@ -158,8 +158,8 @@ public class RxUtils { }; } - public static<T> Observable<T> rememberLast(final Observable<T> observable) { - final AtomicReference<T> lastValue = new AtomicReference<>(null); + public static<T> Observable<T> rememberLast(final Observable<T> observable, final T initialValue) { + final AtomicReference<T> lastValue = new AtomicReference<>(initialValue); return observable.doOnNext(new Action1<T>() { @Override public void call(final T value) { @@ -224,7 +224,7 @@ public class RxUtils { if (cached.containsKey(key)) { return cached.get(key); } - final Observable<V> value = func.call(key).replay().refCount(); + final Observable<V> value = func.call(key).share(); cached.put(key, value); return value; } diff --git a/mapswithme-api/build.gradle b/mapswithme-api/build.gradle index 34b3132..86d5c77 100644 --- a/mapswithme-api/build.gradle +++ b/mapswithme-api/build.gradle @@ -6,7 +6,7 @@ dependencies { android { compileSdkVersion 19 - buildToolsVersion "21.1" + buildToolsVersion "21.1.2" sourceSets { main { diff --git a/showcaseview/build.gradle b/showcaseview/build.gradle index 87e48c8..e5b0945 100644 --- a/showcaseview/build.gradle +++ b/showcaseview/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'android-library' android { compileSdkVersion 19 - buildToolsVersion "21.1" + buildToolsVersion "21.1.2" sourceSets { main { diff --git a/showcaseview/showcaseview.iml b/showcaseview/showcaseview.iml index e47f7c6..1cd3269 100644 --- a/showcaseview/showcaseview.iml +++ b/showcaseview/showcaseview.iml @@ -4,7 +4,7 @@ <facet type="android" name="Android"> <configuration> <option name="LIBRARY_PROJECT" value="true" /> - <option name="UPDATE_PROPERTY_FILES" value="true" /> + <option name="UPDATE_PROPERTY_FILES" value="false" /> </configuration> </facet> </component> @@ -17,5 +17,4 @@ <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> </component> -</module> - +</module>
\ No newline at end of file diff --git a/tests/.settings/org.eclipse.jdt.core.prefs b/tests/.settings/org.eclipse.jdt.core.prefs index 52075af..0c3bd0c 100644 --- a/tests/.settings/org.eclipse.jdt.core.prefs +++ b/tests/.settings/org.eclipse.jdt.core.prefs @@ -40,8 +40,8 @@ org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore diff --git a/tests/src/cgeo/geocaching/CgeoApplicationTest.java b/tests/src/cgeo/geocaching/CgeoApplicationTest.java index c00f549..bb61b34 100644 --- a/tests/src/cgeo/geocaching/CgeoApplicationTest.java +++ b/tests/src/cgeo/geocaching/CgeoApplicationTest.java @@ -72,7 +72,7 @@ public class CgeoApplicationTest extends CGeoTestCase { public static void testSearchTrackable() { final Trackable tb = GCParser.searchTrackable("TB2J1VZ", null, null); assertThat(tb).isNotNull(); - assert (tb != null); // eclipse bug + assert tb != null; // eclipse bug // fix data assertThat(tb.getGuid()).isEqualTo("aefffb86-099f-444f-b132-605436163aa8"); assertThat(tb.getGeocode()).isEqualTo("TB2J1VZ"); @@ -172,7 +172,7 @@ public class CgeoApplicationTest extends CGeoTestCase { final Geocache searchedCache = search.getFirstCacheFromResult(LoadFlags.LOAD_CACHE_OR_DB); // coords must be null if the user is not logged in assertThat(searchedCache).isNotNull(); - assert (searchedCache != null); // eclipse bug + assert searchedCache != null; // eclipse bug assertThat(searchedCache.getCoords()).isNull(); // premium cache. Not visible to guests @@ -312,6 +312,8 @@ public class CgeoApplicationTest extends CGeoTestCase { assertThat(search).isNotNull(); assertThat(search.getGeocodes().contains(mockedCache.getGeocode())).isTrue(); Geocache parsedCache = DataStore.loadCache(mockedCache.getGeocode(), LoadFlags.LOAD_CACHE_OR_DB); + assert parsedCache != null; + assertThat(parsedCache).isNotNull(); assertThat(mockedCache.getCoords().equals(parsedCache.getCoords())).isEqualTo(Settings.isGCPremiumMember()); assertThat(parsedCache.isReliableLatLon()).isEqualTo(Settings.isGCPremiumMember()); @@ -324,6 +326,8 @@ public class CgeoApplicationTest extends CGeoTestCase { assertThat(search).isNotNull(); assertThat(search.getGeocodes().contains(mockedCache.getGeocode())).isTrue(); parsedCache = DataStore.loadCache(mockedCache.getGeocode(), LoadFlags.LOAD_CACHE_OR_DB); + assert parsedCache != null; + assertThat(parsedCache).isNotNull(); assertThat(mockedCache.getCoords().equals(parsedCache.getCoords())).isEqualTo(Settings.isGCPremiumMember()); assertThat(parsedCache.isReliableLatLon()).isEqualTo(Settings.isGCPremiumMember()); @@ -366,6 +370,8 @@ public class CgeoApplicationTest extends CGeoTestCase { assertThat(search.getGeocodes().contains(cache.getGeocode())).isTrue(); // coords differ final Geocache cacheFromViewport = DataStore.loadCache(cache.getGeocode(), LoadFlags.LOAD_CACHE_OR_DB); + assert cacheFromViewport != null; + assertThat(cacheFromViewport).isNotNull(); Log.d("cgeoApplicationTest.testSearchByViewportNotLoggedIn: Coords expected = " + cache.getCoords()); Log.d("cgeoApplicationTest.testSearchByViewportNotLoggedIn: Coords actual = " + cacheFromViewport.getCoords()); assertThat(cache.getCoords().distanceTo(cacheFromViewport.getCoords()) <= 1e-3).isFalse(); diff --git a/tests/src/cgeo/geocaching/DataStoreTest.java b/tests/src/cgeo/geocaching/DataStoreTest.java index 03713ac..9ef5dda 100644 --- a/tests/src/cgeo/geocaching/DataStoreTest.java +++ b/tests/src/cgeo/geocaching/DataStoreTest.java @@ -59,6 +59,7 @@ public class DataStoreTest extends CGeoTestCase { // get list final StoredList list1 = DataStore.getList(listId1); + assertThat(list1).isNotNull(); assertThat(list1.title).isEqualTo("DataStore Test (renamed)"); // move to list (cache1=listId2, cache2=listId2) @@ -123,6 +124,8 @@ public class DataStoreTest extends CGeoTestCase { try { DataStore.saveCache(cache, EnumSet.of(SaveFlag.DB)); final Geocache loadedCache = DataStore.loadCache(GEOCODE_CACHE, LoadFlags.LOAD_ALL_DB_ONLY); + assert loadedCache != null; + assertThat(loadedCache).isNotNull(); assertThat(loadedCache).overridingErrorMessage("Cache was not saved.").isNotNull(); assertThat(loadedCache.getInventory()).hasSize(1); } finally { diff --git a/tests/src/cgeo/geocaching/connector/gc/GCLoginTest.java b/tests/src/cgeo/geocaching/connector/gc/GCLoginTest.java index 07ba646..fc23ab3 100644 --- a/tests/src/cgeo/geocaching/connector/gc/GCLoginTest.java +++ b/tests/src/cgeo/geocaching/connector/gc/GCLoginTest.java @@ -6,10 +6,10 @@ import cgeo.geocaching.enumerations.StatusCode; import org.apache.commons.lang3.StringUtils; -import junit.framework.TestCase; - import android.test.suitebuilder.annotation.Suppress; +import junit.framework.TestCase; + public class GCLoginTest extends TestCase { final GCLogin instance = GCLogin.getInstance(); @@ -20,7 +20,7 @@ public class GCLoginTest extends TestCase { assertThat(instance.login()).isEqualTo(StatusCode.NO_ERROR); } - public void testHomeLocation() { + public static void testHomeLocation() { assertThat(StringUtils.isNotBlank(GCLogin.retrieveHomeLocation())).isTrue(); } diff --git a/tests/src/cgeo/geocaching/connector/gc/GCParserTest.java b/tests/src/cgeo/geocaching/connector/gc/GCParserTest.java index fb7224b..058163a 100644 --- a/tests/src/cgeo/geocaching/connector/gc/GCParserTest.java +++ b/tests/src/cgeo/geocaching/connector/gc/GCParserTest.java @@ -57,7 +57,7 @@ public class GCParserTest extends AbstractResourceInstrumentationTestCase { assertThat(result.getCount()).isEqualTo(1); final Geocache cache = result.getFirstCacheFromResult(LoadFlags.LOAD_CACHE_OR_DB); assertThat(cache).isNotNull(); - assert (cache != null); // eclipse bug + assert cache != null; // eclipse bug assertThat(cache.getName()).isEqualTo(cacheName); } @@ -93,7 +93,7 @@ public class GCParserTest extends AbstractResourceInstrumentationTestCase { } /** - * Test {@link GCParser#parseCacheFromText(String, CancellableHandler)} with "mocked" data + * Test {@link GCParser#parseAndSaveCacheFromText(String, CancellableHandler)} with "mocked" data * */ @MediumTest @@ -160,7 +160,7 @@ public class GCParserTest extends AbstractResourceInstrumentationTestCase { final String page = GCParser.requestHtmlPage(cache.getGeocode(), null, "n"); final Geocache cache2 = GCParser.parseAndSaveCacheFromText(page, null).getFirstCacheFromResult(LoadFlags.LOAD_CACHE_ONLY); assertThat(cache2).isNotNull(); - assert (cache2 != null); // eclipse bug + assert cache2 != null; // eclipse bug assertThat(cache2.hasUserModifiedCoords()).isTrue(); assertEquals(new Geopoint("N51 21.544", "E07 02.566"), cache2.getCoords()); // delete coordinates @@ -169,7 +169,7 @@ public class GCParserTest extends AbstractResourceInstrumentationTestCase { final String page2 = GCParser.requestHtmlPage(cache.getGeocode(), null, "n"); final Geocache cache3 = GCParser.parseAndSaveCacheFromText(page2, null).getFirstCacheFromResult(LoadFlags.LOAD_CACHE_ONLY); assertThat(cache3).isNotNull(); - assert (cache3 != null); // eclipse bug + assert cache3 != null; // eclipse bug assertThat(cache3.hasUserModifiedCoords()).isFalse(); } diff --git a/tests/src/cgeo/geocaching/connector/gc/TrackablesTest.java b/tests/src/cgeo/geocaching/connector/gc/TrackablesTest.java index 89eada3..bf185b3 100644 --- a/tests/src/cgeo/geocaching/connector/gc/TrackablesTest.java +++ b/tests/src/cgeo/geocaching/connector/gc/TrackablesTest.java @@ -18,6 +18,7 @@ public class TrackablesTest extends AbstractResourceInstrumentationTestCase { public void testLogPageWithTrackables() { final List<TrackableLog> tbLogs = GCParser.parseTrackableLog(getFileContent(R.raw.log_with_2tb)); assertThat(tbLogs).isNotNull(); + assert tbLogs != null; assertThat(tbLogs).hasSize(2); final TrackableLog log = tbLogs.get(0); assertThat(log.name).isEqualTo("Steffen's Kaiserwagen"); diff --git a/tests/src/cgeo/geocaching/connector/oc/OkapiClientTest.java b/tests/src/cgeo/geocaching/connector/oc/OkapiClientTest.java index 20a51b8..aa66ac9 100644 --- a/tests/src/cgeo/geocaching/connector/oc/OkapiClientTest.java +++ b/tests/src/cgeo/geocaching/connector/oc/OkapiClientTest.java @@ -19,6 +19,7 @@ public class OkapiClientTest extends CGeoTestCase { assertThat(cache.isDetailed()).isTrue(); // cache should be stored to DB (to listID 0) when loaded above cache = DataStore.loadCache(geoCode, LoadFlags.LOAD_ALL_DB_ONLY); + assert cache != null; assertThat(cache).isNotNull(); assertThat(cache.getGeocode()).isEqualTo(geoCode); assertThat(cache.getName()).isEqualTo("Oshkosh Municipal Tank"); @@ -41,6 +42,7 @@ public class OkapiClientTest extends CGeoTestCase { assertThat(cache).as("Cache from OKAPI").isNotNull(); // cache should be stored to DB (to listID 0) when loaded above cache = DataStore.loadCache(geoCode, LoadFlags.LOAD_ALL_DB_ONLY); + assert cache != null; assertThat(cache).isNotNull(); assertThat(cache.getWaypoints()).hasSize(3); diff --git a/tests/src/cgeo/geocaching/files/GPXImporterTest.java b/tests/src/cgeo/geocaching/files/GPXImporterTest.java index f72cf4a..9426fe8 100644 --- a/tests/src/cgeo/geocaching/files/GPXImporterTest.java +++ b/tests/src/cgeo/geocaching/files/GPXImporterTest.java @@ -78,6 +78,8 @@ public class GPXImporterTest extends AbstractResourceInstrumentationTestCase { final SearchResult search = (SearchResult) importStepHandler.messages.get(3).obj; assertThat(new ArrayList<String>(search.getGeocodes())).isEqualTo(Collections.singletonList(geocode)); final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + assert cache != null; + assertThat(cache).isNotNull(); assertCacheProperties(cache); assertThat(cache.getWaypoints().isEmpty()).isTrue(); @@ -101,6 +103,8 @@ public class GPXImporterTest extends AbstractResourceInstrumentationTestCase { final SearchResult search = (SearchResult) importStepHandler.messages.get(3).obj; assertThat(new ArrayList<String>(search.getGeocodes())).isEqualTo(Collections.singletonList(geocode)); final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + assert cache != null; + assertThat(cache).isNotNull(); assertCacheProperties(cache); assertThat(cache.getWaypoints()).as("Number of imported waypoints").hasSize(4); @@ -128,6 +132,8 @@ public class GPXImporterTest extends AbstractResourceInstrumentationTestCase { final SearchResult search = (SearchResult) importStepHandler.messages.get(4).obj; assertThat(new ArrayList<String>(search.getGeocodes())).isEqualTo(Collections.singletonList("GC31J2H")); final Geocache cache = DataStore.loadCache("GC31J2H", LoadFlags.LOAD_CACHE_OR_DB); + assert cache != null; + assertThat(cache).isNotNull(); assertCacheProperties(cache); assertThat(cache.getWaypoints()).hasSize(2); } @@ -141,6 +147,8 @@ public class GPXImporterTest extends AbstractResourceInstrumentationTestCase { assertImportStepMessages(GPXImporter.IMPORT_STEP_START, GPXImporter.IMPORT_STEP_READ_FILE, GPXImporter.IMPORT_STEP_STORE_STATIC_MAPS, GPXImporter.IMPORT_STEP_FINISHED); final Geocache cache = DataStore.loadCache("AID1", LoadFlags.LOAD_CACHE_OR_DB); + assert cache != null; + assertThat(cache).isNotNull(); assertCacheProperties(cache); assertThat(cache.getName()).isEqualTo("First Aid Station #1"); } @@ -205,6 +213,8 @@ public class GPXImporterTest extends AbstractResourceInstrumentationTestCase { final SearchResult search = (SearchResult) importStepHandler.messages.get(3).obj; assertThat(new ArrayList<String>(search.getGeocodes())).isEqualTo(Collections.singletonList(geocode)); final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + assert cache != null; + assertThat(cache).isNotNull(); assertCacheProperties(cache); assertThat(cache.getWaypoints().isEmpty()).isTrue(); @@ -223,6 +233,8 @@ public class GPXImporterTest extends AbstractResourceInstrumentationTestCase { final SearchResult search = (SearchResult) importStepHandler.messages.get(4).obj; assertThat(new ArrayList<String>(search.getGeocodes())).isEqualTo(Collections.singletonList(geocode)); final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + assert cache != null; + assertThat(cache).isNotNull(); assertCacheProperties(cache); assertThat(cache.getWaypoints()).hasSize(1); // this is the original pocket query result without test waypoint } @@ -249,6 +261,8 @@ public class GPXImporterTest extends AbstractResourceInstrumentationTestCase { final SearchResult search = (SearchResult) importStepHandler.messages.get(4).obj; assertThat(new ArrayList<String>(search.getGeocodes())).isEqualTo(Collections.singletonList(geocode)); final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + assert cache != null; + assertThat(cache).isNotNull(); assertCacheProperties(cache); assertThat(cache.getWaypoints()).hasSize(1); // this is the original pocket query result without test waypoint } diff --git a/tests/src/cgeo/geocaching/files/GPXParserTest.java b/tests/src/cgeo/geocaching/files/GPXParserTest.java index 59b0d4f..2cc1f34 100644 --- a/tests/src/cgeo/geocaching/files/GPXParserTest.java +++ b/tests/src/cgeo/geocaching/files/GPXParserTest.java @@ -105,26 +105,25 @@ public class GPXParserTest extends AbstractResourceInstrumentationTestCase { assertGc31j2hWaypoints(cache); } + private static void checkWaypointType(final Collection<Geocache> caches, final String geocode, final int wpIndex, final WaypointType waypointType) { + for (final Geocache cache : caches) { + if (cache.getGeocode().equals(geocode)) { + final List<Waypoint> waypoints = cache.getWaypoints(); + assertThat(waypoints).isNotEmpty(); + final Waypoint waypoint = waypoints.get(wpIndex); + assertThat(waypoint).isNotNull(); + assertThat(waypoint.getWaypointType()).isEqualTo(waypointType); + return; + } + } + fail("could not find cache with geocode " + geocode); + } + public void testRenamedWaypointTypes() throws IOException, ParserException { - removeCacheCompletely("GC31J2H"); final List<Geocache> caches = readGPX10(R.raw.renamed_waypoints, R.raw.renamed_waypoints_wpts); assertThat(caches).hasSize(25); - // multi waypoint (now "physical stage") - Geocache cache = caches.get(12); - assertThat(cache.getGeocode()).isEqualTo("GC3NBDE"); - List<Waypoint> waypoints = cache.getWaypoints(); - assertThat(waypoints).isNotEmpty(); - Waypoint waypoint = waypoints.get(1); - assertThat(waypoint).isNotNull(); - assertThat(waypoint.getWaypointType()).isEqualTo(WaypointType.STAGE); - // mystery waypoint - now "virtual stage" - cache = caches.get(15); - assertThat(cache.getGeocode()).isEqualTo("GC16CBG"); - waypoints = cache.getWaypoints(); - assertThat(waypoints).isNotEmpty(); - waypoint = waypoints.get(1); - assertThat(waypoint).isNotNull(); - assertThat(waypoint.getWaypointType()).isEqualTo(WaypointType.PUZZLE); + checkWaypointType(caches, "GC3NBDE", 1, WaypointType.STAGE); // multi waypoint (now "physical stage") + checkWaypointType(caches, "GC16CBG", 1, WaypointType.PUZZLE); // mystery waypoint (now "virtual stage") } public void testGc31j2hWptsWithoutCache() throws IOException, ParserException { @@ -187,7 +186,7 @@ public class GPXParserTest extends AbstractResourceInstrumentationTestCase { private static long parseTime(final String time) { try { return LOG_DATE_FORMAT.parse(time).getTime(); - } catch (ParseException e) { + } catch (final ParseException e) { return 0; } } @@ -216,24 +215,24 @@ public class GPXParserTest extends AbstractResourceInstrumentationTestCase { assertEquals(8.545100, wp.getCoords().getLongitude(), 0.000001); } - private List<Geocache> readGPX10(int... resourceIds) throws IOException, ParserException { + private List<Geocache> readGPX10(final int... resourceIds) throws IOException, ParserException { final GPX10Parser parser = new GPX10Parser(getTemporaryListId()); return readVersionedGPX(parser, resourceIds); } - private List<Geocache> readGPX11(int... resourceIds) throws IOException, ParserException { + private List<Geocache> readGPX11(final int... resourceIds) throws IOException, ParserException { final GPX11Parser parser = new GPX11Parser(getTemporaryListId()); return readVersionedGPX(parser, resourceIds); } - private List<Geocache> readVersionedGPX(final GPXParser parser, int... resourceIds) throws IOException, ParserException { + private List<Geocache> readVersionedGPX(final GPXParser parser, final int... resourceIds) throws IOException, ParserException { final Set<String> result = new HashSet<String>(); - for (int resourceId : resourceIds) { + for (final int resourceId : resourceIds) { final InputStream instream = getResourceStream(resourceId); try { - Collection<Geocache> caches = parser.parse(instream, null); + final Collection<Geocache> caches = parser.parse(instream, null); assertThat(caches).isNotNull(); - for (Geocache cache : caches) { + for (final Geocache cache : caches) { result.add(cache.getGeocode()); } } finally { @@ -271,7 +270,7 @@ public class GPXParserTest extends AbstractResourceInstrumentationTestCase { final List<Geocache> caches = readGPX10(R.raw.geotoad); assertThat(caches).hasSize(2); final List<String> codes = new ArrayList<String>(); - for (Geocache cache : caches) { + for (final Geocache cache : caches) { codes.add(cache.getGeocode()); } assertThat(codes.contains("GC2KN6K")).isTrue(); @@ -287,6 +286,8 @@ public class GPXParserTest extends AbstractResourceInstrumentationTestCase { DataStore.removeAllFromCache(); // load only the minimum cache, it has several members missing final Geocache minimalCache = DataStore.loadCache(geocode, EnumSet.of(LoadFlag.DB_MINIMAL)); + assert minimalCache != null; + assertThat(minimalCache).isNotNull(); // now check that we load lazy members on demand assertThat(minimalCache.getAttributes()).isNotEmpty(); @@ -351,12 +352,11 @@ public class GPXParserTest extends AbstractResourceInstrumentationTestCase { assertThat(cache.getHint()).isEqualTo("Wasserleitung"); } - private Geocache getFirstCache(int gpxResourceId) throws IOException, ParserException { + private Geocache getFirstCache(final int gpxResourceId) throws IOException, ParserException { final List<Geocache> caches = readGPX10(gpxResourceId); assertThat(caches).isNotNull(); assertThat(caches).hasSize(1); - final Geocache cache = caches.get(0); - return cache; + return caches.get(0); } public void testGsakFavPoints() throws IOException, ParserException { @@ -376,14 +376,14 @@ public class GPXParserTest extends AbstractResourceInstrumentationTestCase { public void testGPXMysteryType() throws IOException, ParserException { final List<Geocache> caches = readGPX10(R.raw.tc2012); - Geocache mystery = getCache(caches, "U017"); + final Geocache mystery = getCache(caches, "U017"); assertThat(mystery).isNotNull(); - assert (mystery != null); + assert mystery != null; assertThat(mystery.getType()).isEqualTo(CacheType.MYSTERY); } - private static Geocache getCache(final List<Geocache> caches, String geocode) { - for (Geocache geocache : caches) { + private static Geocache getCache(final List<Geocache> caches, final String geocode) { + for (final Geocache geocache : caches) { if (geocache.getName().equals(geocode)) { return geocache; } @@ -394,7 +394,7 @@ public class GPXParserTest extends AbstractResourceInstrumentationTestCase { public void testLabCaches() throws IOException, ParserException { final List<Geocache> caches = readGPX10(R.raw.giga_lab_caches); assertThat(caches).hasSize(10); - Geocache lab = getCache(caches, "01_Munich Olympic Walk Of Stars_Updated-Project MUNICH2014 - Mia san Giga! Olympiapark"); + final Geocache lab = getCache(caches, "01_Munich Olympic Walk Of Stars_Updated-Project MUNICH2014 - Mia san Giga! Olympiapark"); assertThat(lab).isNotNull(); // parse labs as virtual for the time being diff --git a/tests/src/cgeo/geocaching/location/GeocoderTest.java b/tests/src/cgeo/geocaching/location/GeocoderTest.java new file mode 100644 index 0000000..f53c074 --- /dev/null +++ b/tests/src/cgeo/geocaching/location/GeocoderTest.java @@ -0,0 +1,48 @@ +package cgeo.geocaching.location; + +import static org.assertj.core.api.Assertions.assertThat; + +import cgeo.CGeoTestCase; +import cgeo.geocaching.CgeoApplication; + +import org.apache.commons.lang3.StringUtils; +import org.assertj.core.data.Offset; + +import rx.Observable; + +import android.annotation.TargetApi; +import android.location.Address; +import android.location.Geocoder; +import android.os.Build; + +public class GeocoderTest extends CGeoTestCase { + + private static final String TEST_ADDRESS = "46 rue Barrault, Paris, France"; + private static final double TEST_LATITUDE = 48.82677; + private static final double TEST_LONGITUDE = 2.34644; + private static final Offset<Double> TEST_OFFSET = Offset.offset(0.00050); + + @TargetApi(Build.VERSION_CODES.GINGERBREAD) + public static void testAndroidGeocoder() { + // Some emulators don't have access to Google Android geocoder + if (Geocoder.isPresent()) { + testGeocoder(new AndroidGeocoder(CgeoApplication.getInstance()).getFromLocationName(TEST_ADDRESS), "Android"); + } + } + + public static void testGCGeocoder() { + testGeocoder(GCGeocoder.getFromLocationName(TEST_ADDRESS), "GC"); + } + + public static void testMapQuestGeocoder() { + testGeocoder(MapQuestGeocoder.getFromLocationName(TEST_ADDRESS), "MapQuest"); + } + + public static void testGeocoder(final Observable<Address> addressObservable, final String geocoder) { + final Address address = addressObservable.toBlocking().first(); + assertThat(address.getLatitude()).as("latitude for " + geocoder + " geocoder").isCloseTo(TEST_LATITUDE, TEST_OFFSET); + assertThat(address.getLongitude()).as("longitude for " + geocoder + " geocoder").isCloseTo(TEST_LONGITUDE, TEST_OFFSET); + assertThat(StringUtils.lowerCase(address.getAddressLine(0))).as("street address for " + geocoder + " geocoder").startsWith("46 rue barrault"); + } + +} diff --git a/tests/src/cgeo/geocaching/sensors/SensorsTest.java b/tests/src/cgeo/geocaching/sensors/SensorsTest.java index b54cd13..6cedf50 100644 --- a/tests/src/cgeo/geocaching/sensors/SensorsTest.java +++ b/tests/src/cgeo/geocaching/sensors/SensorsTest.java @@ -5,10 +5,16 @@ import static org.assertj.core.api.Assertions.assertThat; import cgeo.geocaching.MainActivity; import cgeo.geocaching.utils.AngleUtils; +import rx.Observable; + import android.test.ActivityInstrumentationTestCase2; +import java.util.concurrent.TimeUnit; + public class SensorsTest extends ActivityInstrumentationTestCase2<MainActivity> { + private Sensors sensors; + public SensorsTest() { super(MainActivity.class); } @@ -16,6 +22,7 @@ public class SensorsTest extends ActivityInstrumentationTestCase2<MainActivity> @Override protected void setUp() throws Exception { super.setUp(); + sensors = Sensors.getInstance(); } public static void testGetDirectionNow() { @@ -23,4 +30,21 @@ public class SensorsTest extends ActivityInstrumentationTestCase2<MainActivity> assertThat(angle == 1.0f || angle == 91.0f || angle == 181.0f || angle == 271.0f).isTrue(); } + private static <T> void testDataAvailability(final Observable<T> observable) { + try { + observable.timeout(200, TimeUnit.MILLISECONDS).first().toBlocking().single(); + } catch (final Exception ignored) { + fail("timeout while waiting for sensor data"); + } + } + + public void testDirectionObservable() { + testDataAvailability(sensors.directionObservable()); + } + + public void testGeodataObservable() { + testDataAvailability(sensors.geoDataObservable(false)); + testDataAvailability(sensors.geoDataObservable(true)); + } + } diff --git a/tests/src/cgeo/geocaching/test/mock/GC1ZXX2.java b/tests/src/cgeo/geocaching/test/mock/GC1ZXX2.java index 8d9d7af..9580be8 100644 --- a/tests/src/cgeo/geocaching/test/mock/GC1ZXX2.java +++ b/tests/src/cgeo/geocaching/test/mock/GC1ZXX2.java @@ -40,6 +40,7 @@ public class GC1ZXX2 extends MockedCache { return "Rich Uncle Pennybags"; } + @NonNull @Override public CacheSize getSize() { return CacheSize.OTHER; diff --git a/tests/src/cgeo/geocaching/test/mock/GC2CJPF.java b/tests/src/cgeo/geocaching/test/mock/GC2CJPF.java index 8a8cf95..5c65f37 100644 --- a/tests/src/cgeo/geocaching/test/mock/GC2CJPF.java +++ b/tests/src/cgeo/geocaching/test/mock/GC2CJPF.java @@ -47,6 +47,7 @@ public class GC2CJPF extends MockedCache { return getOwnerDisplayName(); } + @NonNull @Override public CacheSize getSize() { return CacheSize.SMALL; diff --git a/tests/src/cgeo/geocaching/test/mock/GC2JVEH.java b/tests/src/cgeo/geocaching/test/mock/GC2JVEH.java index dff6bce..2525514 100644 --- a/tests/src/cgeo/geocaching/test/mock/GC2JVEH.java +++ b/tests/src/cgeo/geocaching/test/mock/GC2JVEH.java @@ -64,6 +64,7 @@ public class GC2JVEH extends MockedCache { return "indianerjones"; } + @NonNull @Override public CacheSize getSize() { return CacheSize.SMALL; diff --git a/tests/src/cgeo/geocaching/test/mock/GC3XX5J.html b/tests/src/cgeo/geocaching/test/mock/GC3XX5J.html index 44420e5..2d75cf1 100644 --- a/tests/src/cgeo/geocaching/test/mock/GC3XX5J.html +++ b/tests/src/cgeo/geocaching/test/mock/GC3XX5J.html @@ -4,23 +4,19 @@ <html lang="en" class="no-js"> <head id="ctl00_Head1"><meta charset="utf-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /><title> GC3XX5J Zaraščen Tir (Traditional Cache) in Slovenia created by David & Ajda -</title><meta name="DC.title" content="Geocaching - The Official Global GPS Cache Hunt Site" /><meta name="author" content="Groundspeak, Inc." /><meta name="DC.creator" content="Groundspeak, Inc." /><meta name="Copyright" content="Copyright (c) 2000-2013 Groundspeak, Inc. All Rights Reserved." /><!-- Copyright (c) 2000-2013 Groundspeak, Inc. All Rights Reserved. --><meta name="description" content="Geocaching is a treasure hunting game where you use a GPS to hide and seek containers with other participants in the activity. Geocaching.com is the listing service for geocaches around the world." /><meta name="DC.subject" content="Geocaching is a treasure hunting game where you use a GPS to hide and seek containers with other participants in the activity. Geocaching.com is the listing service for geocaches around the world." /><meta http-equiv="imagetoolbar" content="no" /><meta name="distribution" content="global" /><meta name="MSSmartTagsPreventParsing" content="true" /><meta name="rating" content="general" /><meta name="revisit-after" content="1 days" /><meta name="robots" content="all" /><link rel="icon" href="/favicon.ico" /><link rel="shortcut icon" href="/favicon.ico" /><link rel="apple-touch-icon" href="/apple-touch-icon.png" /><link rel="stylesheet" type="text/css" media="all" href="../css/blueprint/src/reset.css" /><link rel="stylesheet" type="text/css" media="all" href="../css/blueprint/src/typography.css" /><link rel="stylesheet" type="text/css" media="screen,projection" href="../css/blueprint/src/grid.css" /> - <!--[if lt IE 8]> - <link rel="stylesheet" type="text/css" media="all" href="../css/blueprint/ie.css" /> - <![endif]--> - <link id="uxCssMaster" rel="stylesheet" type="text/css" media="screen,projection" href="../css/tlnMasterScreen.css?r=1" /><link id="uxCssMain" rel="stylesheet" type="text/css" media="all" href="../css/tlnMain.css?r=1" /><link rel="Stylesheet" type="text/css" media="all" href="../css/jqueryui1810/jquery-ui-1.8.10.custom.css" /><link rel="stylesheet" type="text/css" media="all" href="/js/jquery_plugins/jquery.jgrowl.css" /><link rel="stylesheet" type="text/css" media="print" href="../css/tlnMasterPrint.css" /> +</title><meta name="DC.title" content="Geocaching - The Official Global GPS Cache Hunt Site" /><meta name="author" content="Geocaching" /><meta name="DC.creator" content="Geocaching" /><meta name="Copyright" content="Copyright (c) 2000-2015 Groundspeak, Inc. All Rights Reserved." /><!-- Copyright (c) 2000-2015 Groundspeak, Inc. All Rights Reserved. --><meta name="description" content="Geocaching is a treasure hunting game where you use a GPS to hide and seek containers with other participants in the activity. Geocaching.com is the listing service for geocaches around the world." /><meta name="DC.subject" content="Geocaching is a treasure hunting game where you use a GPS to hide and seek containers with other participants in the activity. Geocaching.com is the listing service for geocaches around the world." /><meta http-equiv="imagetoolbar" content="no" /><meta name="distribution" content="global" /><meta name="MSSmartTagsPreventParsing" content="true" /><meta name="rating" content="general" /><meta name="revisit-after" content="1 days" /><meta name="robots" content="all" /><meta name="p:domain_verify" content="107f8f596a30ff1ea307df82db696a5e" /><link rel="icon" href="/favicon.ico" /><link rel="shortcut icon" href="/favicon.ico" /><link rel="apple-touch-icon" href="/apple-touch-icon.png" /><link href="/content/coreCSS?v=FStdP7IBVzP6kb13wvFf_tS6RqtMyRAX16yt50q38G01" rel="stylesheet"/> +<link href="/css/jqueryui1104/jqUI?v=o-CPX73h6gKxCTGUlhe8oKrpdDBkkuBMgkCBfCBI5_A1" rel="stylesheet"/> +<link rel="stylesheet" type="text/css" media="print" href="../css/tlnMasterPrint.css" /><script src="/bundle/modernizer?v=wa97G2fdGaWBnIjKyioUssAWxy8tnYxkV0d-_5ClId01"></script> +<script src="/bundle/coreJS?v=uKjd3XRiFC1C29gkUc0JuDRCF6PuUu7R8PpzGjgXDEc1"></script> + + <script type="text/javascript"> - var _gaq = _gaq || []; - </script> - <script type="text/javascript" src="/js/modernizr-1.7.min.js"></script> - <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script> - - <script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js" type="text/javascript"></script> - <script type="text/javascript" src="/js/jquery.truncate.min.js"></script> - - <script type='text/javascript'> + var googletag = googletag || {}; googletag.cmd = googletag.cmd || []; + + var _gaq = _gaq || []; + (function () { var gads = document.createElement('script'); gads.async = true; @@ -30,21 +26,39 @@ var node = document.getElementsByTagName('script')[0]; node.parentNode.insertBefore(gads, node); })(); + + (function (i, s, o, g, r, a, m) { + i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () { + (i[r].q = i[r].q || []).push(arguments); + }, i[r].l = 1 * new Date(); + a = s.createElement(o), + m = s.getElementsByTagName(o)[0]; + a.async = 1; a.src = g; + m.parentNode.insertBefore(a, m); + })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga'); + + (function () { + + var ga = document.createElement('script'); + ga.src = ('https:' == document.location.protocol ? + 'https://ssl' : 'http://www') + + '.google-analytics.com/ga.js'; + ga.setAttribute('async', 'true'); + document.documentElement.firstChild.appendChild(ga); + + })(); + </script> - + <link href="/css/fancybox/jquery.fancybox.css" rel="stylesheet" type="text/css" /> <link href="/js/jquery_plugins/icalendar/jquery.icalendar.css" rel="stylesheet" type="text/css" /> <link href="/js/jquery_plugins/qtip/jquery.qtip.css" rel="stylesheet" type="text/css" /> - <link rel="stylesheet" type="text/css" media="all" href="/js/leaflet/0.5.1/leaflet.css" /> - <!--[if IE]> - <link rel="stylesheet" type="text/css" media="all" href="/js/leaflet/0.5.1/leaflet.ie.css" /> - <![endif]--> + <link rel="stylesheet" type="text/css" media="all" href="/static/js/leaflet/0.7.2/leaflet.css" /> <link rel="stylesheet" type="text/css" media="screen" href="/css/seek/beta.css" /> - <script type="text/javascript" src="/js/leaflet/0.5.1/leaflet.js"></script> - <script type="text/javascript" src="/js/geometa.js"></script> + <script type="text/javascript" src="/static/js/leaflet/0.7.2/leaflet.js"></script> <script type="text/javascript"> var userToken = null, urlParams = {}, @@ -63,15 +77,17 @@ } })(); </script> -<meta name="og:site_name" content="Geocaching.com" property="og:site_name" /><meta name="og:type" content="article" property="og:type" /><meta name="fb:app_id" content="251051881589204" property="fb:app_id" /><meta name="og:url" content="http://www.geocaching.com/seek/cache_details.aspx?wp=GC3XX5J&title=zarascen-tir&Submit6=Go" property="og:url" /><meta name="og:description" content="Use a smartphone or GPS device to navigate to the provided coordinates. Look for a small hidden container. When you find it, write your name and date in the logbook. If you take something from the container, leave something in exchange. The terrain is 2 and difficulty is 1.5 (out of 5)." property="og:description" /><meta name="og:image" content="http://www.geocaching.com/images/facebook/wpttypes/2.png" property="og:image" /><meta name="og:title" content="Zaraščen Tir" property="og:title" /><script>function utmx_section(){}function utmx(){}(function(){var k='3682814-19',d=document,l=d.location,c=d.cookie; if(l.search.indexOf('utm_expid='+k)>0)return; function f(n){if(c){var i=c.indexOf(n+'=');if(i>-1){var j=c.indexOf(';',i);return escape(c.substring(i+n.length+1,j<0?c.length:j))}}}var x=f('__utmx'),xx=f('__utmxx'),h=l.hash;d.write('<sc'+'ript src=" '+'http'+(l.protocol=='https:'?'s://ssl':'://www')+'.google-analytics.com/ga_exp.js?'+'utmxkey='+k+'&utmx='+(x?x:'')+'&utmxx='+(xx?xx:'')+'&utmxtime='+new Date().valueOf()+(h?'&utmxhash='+escape(h.substr(1)):'')+'" type="text/javascript" charset="utf-8"><\/sc'+'ript>')})();</script><script>utmx('url','A/B');</script><meta name="description" content="Zaraščen Tir (GC3XX5J) was created by David & Ajda on 10/01/2012. It's a Small size geocache, with difficulty of 1.5, terrain of 2. It's located in Slovenia.Kadar zbolimo nam pomaga...When we get sick, they are helpful... SLO:tir je danes zapuščen, včasih pa so ga uporabljala različna podjetja, da so po njem dostavljali material in odvažali končne izdelke." /><link rel="canonical" href="http://www.geocaching.com/seek/cache_details.aspx?guid=51e40dec-6272-4dad-934b-e175daaac265" /><link rel="alternate" href="../datastore/rss_galleryimages.ashx?guid=51e40dec-6272-4dad-934b-e175daaac265" type="application/rss+xml" title="[Gallery Images]" id="GalleryImages" /></head> +<script>function utmx_section(){}function utmx(){}(function(){var k='3682814-19',d=document,l=d.location,c=d.cookie; if(l.search.indexOf('utm_expid='+k)>0)return; function f(n){if(c){var i=c.indexOf(n+'=');if(i>-1){var j=c.indexOf(';',i);return escape(c.substring(i+n.length+1,j<0?c.length:j))}}}var x=f('__utmx'),xx=f('__utmxx'),h=l.hash;d.write('<sc'+'ript src=" '+'http'+(l.protocol=='https:'?'s://ssl':'://www')+'.google-analytics.com/ga_exp.js?'+'utmxkey='+k+'&utmx='+(x?x:'')+'&utmxx='+(xx?xx:'')+'&utmxtime='+new Date().valueOf()+(h?'&utmxhash='+escape(h.substr(1)):'')+'" type="text/javascript" charset="utf-8"><\/sc'+'ript>')})();</script><script>utmx('url','A/B');</script><meta name="og:site_name" content="Geocaching.com" property="og:site_name" /><meta name="og:type" content="article" property="og:type" /><meta name="fb:app_id" content="251051881589204" property="fb:app_id" /><meta name="og:url" content="http://www.geocaching.com/seek/cache_details.aspx?wp=GC3XX5J&title=zarascen-tir" property="og:url" /><meta name="og:description" content="Use a smartphone or GPS device to navigate to the provided coordinates. Look for a small hidden container. When you find it, write your name and date in the logbook. If you take something from the container, leave something in exchange. The terrain is 2 and difficulty is 1.5 (out of 5)." property="og:description" /><meta name="og:image" content="http://www.geocaching.com/images/facebook/wpttypes/2.png" property="og:image" /><meta name="og:title" content="Zaraščen Tir" property="og:title" /><meta name="description" content="Zaraščen Tir (GC3XX5J) was created by David & Ajda on 10/01/2012. It's a Small size geocache, with difficulty of 1.5, terrain of 2. It's located in Slovenia.Kadar zbolimo nam pomaga...When we get sick, they are helpful... SLO:tir je danes zapuščen, včasih pa so ga uporabljala različna podjetja, da so po njem dostavljali material in odvažali končne izdelke." /><link rel="canonical" href="http://www.geocaching.com/seek/cache_details.aspx?guid=51e40dec-6272-4dad-934b-e175daaac265" /><link rel="alternate" href="../datastore/rss_galleryimages.ashx?guid=51e40dec-6272-4dad-934b-e175daaac265" type="application/rss+xml" title="[Gallery Images]" id="GalleryImages" /></head> + <body background="https://lh6.googleusercontent.com/-PoDn9PmtYmg/UGnOZLEQboI/AAAAAAAAAHM/hBXxerWnSdA/s254/lek-verovskova.jpg" class="CacheDetailsPage"> - <form name="aspnetForm" method="post" action="/geocache/GC3XX5J_zarascen-tir?Submit6=Go" id="aspnetForm"> + <form name="aspnetForm" method="post" action="/geocache/GC3XX5J_zarascen-tir" id="aspnetForm"> <div> <input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" /> <input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" /> -<input type="hidden" name="__VIEWSTATEFIELDCOUNT" id="__VIEWSTATEFIELDCOUNT" value="2" /> -<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTE1Mzk0NTE5MTEPFgIeBEMuSUQoKVlTeXN0ZW0uSW50NjQsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OQczMjIwNjcyFgJmD2QWBmYPZBYKAgYPFgIeBFRleHQFYjxtZXRhIG5hbWU9IkNvcHlyaWdodCIgY29udGVudD0iQ29weXJpZ2h0IChjKSAyMDAwLTIwMTMgR3JvdW5kc3BlYWssIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC4iIC8+ZAIHDxYCHwEFRzwhLS0gQ29weXJpZ2h0IChjKSAyMDAwLTIwMTMgR3JvdW5kc3BlYWssIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC4gLS0+ZAIZDxYCHgRocmVmBR1+L2Nzcy90bG5NYXN0ZXJTY3JlZW4uY3NzP3I9MWQCGg8WAh8CBRV+L2Nzcy90bG5NYWluLmNzcz9yPTFkAh8PFgIeB1Zpc2libGVoZAIBDxYCHgZhY3Rpb24FKS9nZW9jYWNoZS9HQzNYWDVKX3phcmFzY2VuLXRpcj9TdWJtaXQ2PUdvFhYCCw8WAh8BZGQCDQ9kFgICBQ8WAh8DZxYIAgEPDxYCHghJbWFnZVVybAVOaHR0cDovL2ltZy5nZW9jYWNoaW5nLmNvbS91c2VyL2F2YXRhci9lOGE5N2EwNS1hMDUxLTRkYTktYmM5My02YTgzMGI4NjZhZmYuanBnZGQCAw8WAh8BBWxIZWxsbywgPGEgaHJlZj0iL215L2RlZmF1bHQuYXNweCIgdGl0bGU9IlZpZXcgUHJvZmlsZSBmb3IgSm9TYU1hSmEiIGNsYXNzPSJTaWduZWRJblByb2ZpbGVMaW5rIj5Kb1NhTWFKYTwvYT5kAgUPDxYCHgtOYXZpZ2F0ZVVybAWsAWh0dHBzOi8vd3d3Lmdlb2NhY2hpbmcuY29tL2xvZ2luL2RlZmF1bHQuYXNweD9SRVNFVD1ZJnJlZGlyPWh0dHAlM2ElMmYlMmZ3d3cuZ2VvY2FjaGluZy5jb20lMmZzZWVrJTJmY2FjaGVfZGV0YWlscy5hc3B4JTNmd3AlM2RHQzNYWDVKJTI2dGl0bGUlM2R6YXJhc2Nlbi10aXIlMjZTdWJtaXQ2JTNkR29kZAILDxYCHwEFEDQ4NCBDYWNoZXMgRm91bmRkAhcPFgIfA2cWAgINDw8WAh8GBUB+L3RyYWNrL3NlYXJjaC5hc3B4P289MSZ1aWQ9NGQ0MTY0NjEtZDFhNy00Y2M1LThlZTctMzM2YmI5MTBmZWI4ZGQCHQ8PFgIfA2dkZAIpDxYCHwNnZAJXD2QWBAIDDxYCHwEFB0VuZ2xpc2hkAgUPFgIeC18hSXRlbUNvdW50AhQWKGYPZBYCAgEPDxYIHg9Db21tYW5kQXJndW1lbnQFBWVuLVVTHgtDb21tYW5kTmFtZQUNU2V0VGVtcExvY2FsZR8BBQdFbmdsaXNoHhBDYXVzZXNWYWxpZGF0aW9uaGRkAgEPZBYCAgEPDxYIHwgFBWRlLURFHwkFDVNldFRlbXBMb2NhbGUfAQUHRGV1dHNjaB8KaGRkAgIPZBYCAgEPDxYIHwgFBWZyLUZSHwkFDVNldFRlbXBMb2NhbGUfAQUJRnJhbsOnYWlzHwpoZGQCAw9kFgICAQ8PFggfCAUFcHQtUFQfCQUNU2V0VGVtcExvY2FsZR8BBQpQb3J0dWd1w6pzHwpoZGQCBA9kFgICAQ8PFggfCAUFY3MtQ1ofCQUNU2V0VGVtcExvY2FsZR8BBQnEjGXFoXRpbmEfCmhkZAIFD2QWAgIBDw8WCB8IBQVkYS1ESx8JBQ1TZXRUZW1wTG9jYWxlHwEFBURhbnNrHwpoZGQCBg9kFgICAQ8PFggfCAUFc3YtU0UfCQUNU2V0VGVtcExvY2FsZR8BBQdTdmVuc2thHwpoZGQCBw9kFgICAQ8PFggfCAUFZXMtRVMfCQUNU2V0VGVtcExvY2FsZR8BBQhFc3Bhw7FvbB8KaGRkAggPZBYCAgEPDxYIHwgFBWV0LUVFHwkFDVNldFRlbXBMb2NhbGUfAQUFRWVzdGkfCmhkZAIJD2QWAgIBDw8WCB8IBQVpdC1JVB8JBQ1TZXRUZW1wTG9jYWxlHwEFCEl0YWxpYW5vHwpoZGQCCg9kFgICAQ8PFggfCAUFZWwtR1IfCQUNU2V0VGVtcExvY2FsZR8BBRDOlc67zrvOt869zrnOus6sHwpoZGQCCw9kFgICAQ8PFggfCAUFbHYtTFYfCQUNU2V0VGVtcExvY2FsZR8BBQlMYXR2aWXFoXUfCmhkZAIMD2QWAgIBDw8WCB8IBQVubC1OTB8JBQ1TZXRUZW1wTG9jYWxlHwEFCk5lZGVybGFuZHMfCmhkZAIND2QWAgIBDw8WCB8IBQVjYS1FUx8JBQ1TZXRUZW1wTG9jYWxlHwEFB0NhdGFsw6AfCmhkZAIOD2QWAgIBDw8WCB8IBQVwbC1QTB8JBQ1TZXRUZW1wTG9jYWxlHwEFBlBvbHNraR8KaGRkAg8PZBYCAgEPDxYIHwgFBW5iLU5PHwkFDVNldFRlbXBMb2NhbGUfAQUOTm9yc2ssIEJva23DpWwfCmhkZAIQD2QWAgIBDw8WCB8IBQVrby1LUh8JBQ1TZXRUZW1wTG9jYWxlHwEFCe2VnOq1reyWtB8KaGRkAhEPZBYCAgEPDxYIHwgFBWh1LUhVHwkFDVNldFRlbXBMb2NhbGUfAQUGTWFneWFyHwpoZGQCEg9kFgICAQ8PFggfCAUFcm8tUk8fCQUNU2V0VGVtcExvY2FsZR8BBQhSb23Dom7Egx8KaGRkAhMPZBYCAgEPDxYIHwgFBWphLUpQHwkFDVNldFRlbXBMb2NhbGUfAQUJ5pel5pys6KqeHwpoZGQCWw9kFgICAw8WAh8DaGQCXQ8WAh4FY2xhc3MFDHNwYW4tMjQgbGFzdBYCAgEPZBYmAgEPZBYCZg9kFgICAQ8PFgIfAQUHR0MzWFg1SmRkAgIPFgIfAQWmATxhIGhyZWY9Ii9hYm91dC9jYWNoZV90eXBlcy5hc3B4IiB0YXJnZXQ9Il9ibGFuayIgdGl0bGU9IkFib3V0IENhY2hlIFR5cGVzIj48aW1nIHNyYz0iL2ltYWdlcy9XcHRUeXBlcy8yLmdpZiIgYWx0PSJUcmFkaXRpb25hbCBDYWNoZSIgdGl0bGU9IlRyYWRpdGlvbmFsIENhY2hlIiAvPjwvYT5kAgYPZBYEAgEPFgIfA2dkAgcPFgIfA2hkAgoPZBYGAgEPFgIfAQUBMWQCBQ8WAh8DaGQCBw8PFgIfBgVEL3NlZWsvY2FjaGVfZmF2b3JpdGVkLmFzcHg/Z3VpZD01MWU0MGRlYy02MjcyLTRkYWQtOTM0Yi1lMTc1ZGFhYWMyNjVkZAIMDw8WAh8DaGRkAg8PFgIfA2hkAhAPFgIfA2hkAhEPZBYMAgMPZBYCAgEPFgIfAQUbVVRNOiAzM1QgRSA0NjEzNDAgTiA1MTAzMTA5ZAINDw8WAh8GBTNjZHBmLmFzcHg/Z3VpZD01MWU0MGRlYy02MjcyLTRkYWQtOTM0Yi1lMTc1ZGFhYWMyNjVkZAIPDw8WAh8GBThjZHBmLmFzcHg/Z3VpZD01MWU0MGRlYy02MjcyLTRkYWQtOTM0Yi1lMTc1ZGFhYWMyNjUmbGM9NWRkAhEPDxYCHwYFOWNkcGYuYXNweD9ndWlkPTUxZTQwZGVjLTYyNzItNGRhZC05MzRiLWUxNzVkYWFhYzI2NSZsYz0xMGRkAhMPDxYEHwYFemh0dHA6Ly9tYXBzLmdvb2dsZS5jb20vbWFwcz9mPWQmaGw9ZW4mc2FkZHI9NTIuMjE2MjUsOS43MTQ0ODMgKEhvbWUgTG9jYXRpb24pJmRhZGRyPTQ2LjA4MDQ2NywxNC41KFphcmElYzUlYTElYzQlOGRlbitUaXIpHgZUYXJnZXQFBl9ibGFua2RkAhsPZBYIZg8PFgQeCUZvcmVDb2xvcgweBF8hU0ICBGRkAgEPDxYEHw0MHw4CBGRkAgIPDxYCHwNnFgIeB29uY2xpY2sFO3MyZ3BzKCc1MWU0MGRlYy02MjcyLTRkYWQtOTM0Yi1lMTc1ZGFhYWMyNjUnKTtyZXR1cm4gZmFsc2U7ZAIDDw8WAh8DZxYCHw8FIHMycGhvbmUoJ0dDM1hYNUonKTtyZXR1cm4gZmFsc2U7ZAITDxYCHwNnZAIXD2QWCGYPFgIfA2hkAgEPDxYCHwNoZGQCAg8PFgIfA2hkZAIDDxYCHwNoZAIYD2QWAgIDDw8WAh8BBQdEZWNyeXB0ZGQCGQ8WAh8BBUJCcXpueGF2IHhuenJhIGJvIGd2ZWggLyBFcnpiaXIgZ3VyIGZnYmFyIGp2cHUgeXZyZiBvcmZ2cXIgZ3VyIGVudnlkAhsPZBYEAgEPFgQfAgUfL3NlZWsvbG9nLmFzcHg/SUQ9MzIyMDY3MiZsY249MR4JaW5uZXJodG1sBQ5Mb2cgeW91ciB2aXNpdGQCAg8WAh8DaGQCHA8WAh8DZ2QCHQ8WAh8DaGQCIA9kFgICAw8WAh8QBRNBZHZlcnRpc2luZyB3aXRoIFVzZAIkD2QWBAIFDw8WAh8DZ2RkAgkPDxYCHwYFPH4vdHJhY2svc2VhcmNoLmFzcHg/d2lkPTUxZTQwZGVjLTYyNzItNGRhZC05MzRiLWUxNzVkYWFhYzI2NWRkAiYPDxYCHwNnZBYCZg8WAh8HAgEWAgIBD2QWAmYPFQIA3QE8YSBocmVmPSJodHRwOi8vd3d3Lmdlb2NhY2hpbmcuY29tL2Jvb2ttYXJrcy92aWV3LmFzcHg/Z3VpZD0xNDU1MTcwOC0wYzNjLTRmOTUtOTM0Mi0xYmIzMTE5ZTZlZmUiPmNnZW8gbW9ja3M8L2E+PGJyIC8+IGJ5IDxhIGhyZWY9Imh0dHA6Ly93d3cuZ2VvY2FjaGluZy5jb20vcHJvZmlsZS8/Z3VpZD00ZDQxNjQ2MS1kMWE3LTRjYzUt" /> -<input type="hidden" name="__VIEWSTATE1" id="__VIEWSTATE1" value="OGVlNy0zMzZiYjkxMGZlYjgiPkpvU2FNYUphPC9hPmQCJw9kFhICAQ8PFgIfA2hkZAIDD2QWAgIBDw8WAh8GBUUvaGlkZS93cHRsaXN0LmFzcHg/UmVmV3B0SUQ9NTFlNDBkZWMtNjI3Mi00ZGFkLTkzNGItZTE3NWRhYWFjMjY1JkRTPTFkZAIHDw8WBh4GUkRTLklECyl2R3JvdW5kc3BlYWsuV2ViLkdQWC5XcHREYXRhU291cmNlcywgVHVjc29uLkNvbW1vbi5MZWdhY3ksIFZlcnNpb249My4wLjQ5ODEuMTMyOTMsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAEeB1JXUFQuSUQoKwQHMzIyMDY3Mh8DaGRkAgkPDxYCHwNnZBYCAgEPDxYEHwYFKC9tYXAvZGVmYXVsdC5hc3B4P2xhdD00Ni4wODA0NjcmbG5nPTE0LjUfA2dkZAIPDxYCHwEFkQEuLi5vdGhlciBjYWNoZXMgPGEgaHJlZj0iL3NlZWsvbmVhcmVzdC5hc3B4P3U9RGF2aWQrJTI2K0FqZGEiPmhpZGRlbjwvYT4gb3IgPGEgaHJlZj0iL3NlZWsvbmVhcmVzdC5hc3B4P3VsPURhdmlkKyUyNitBamRhIj5mb3VuZDwvYT4gYnkgdGhpcyB1c2VyZAIRD2QWBgIBDxYCHwEFgQIuLi5uZWFyYnkgPGEgaHJlZj0iL3NlZWsvbmVhcmVzdC5hc3B4P3R4PTMyYmM5MzMzLTVlNTItNDk1Ny1iMGY2LTVhMmM4ZmM3YjI1NyZsYXQ9NDYuMDgwNDY3JmxuZz0xNC41MDAwMDAiPmNhY2hlcyBvZiB0aGlzIHR5cGU8L2E+LCA8YSBocmVmPSIvc2Vlay9uZWFyZXN0LmFzcHg/dHg9MzJiYzkzMzMtNWU1Mi00OTU3LWIwZjYtNWEyYzhmYzdiMjU3JmxhdD00Ni4wODA0NjcmbG5nPTE0LjUwMDAwMCZmPTEiPnRoYXQgSSBoYXZlbid0IGZvdW5kPC9hPmQCAw8WAh8BBagBLi4uYWxsIG5lYXJieSA8YSBocmVmPSIvc2Vlay9uZWFyZXN0LmFzcHg/bGF0PTQ2LjA4MDQ2NyZsbmc9MTQuNTAwMDAwIj5jYWNoZXM8L2E+LCA8YSBocmVmPSIvc2Vlay9uZWFyZXN0LmFzcHg/bGF0PTQ2LjA4MDQ2NyZsbmc9MTQuNTAwMDAwJmY9MSI+dGhhdCBJIGhhdmVuJ3QgZm91bmQ8L2E+ZAIFDxYCHwEFfy4uLmFsbCBuZWFyYnkgPGEgaHJlZj0iaHR0cDovL3d3dy53YXltYXJraW5nLmNvbS9kaXJlY3RvcnkuYXNweD9mPTEmbGF0PTQ2LjA4MDQ2NyZsb249MTQuNTAwMDAwIj53YXltYXJrcyBvbiBXYXltYXJraW5nLmNvbTwvYT5kAhMPFgIfA2hkAhcPZBYCAgEPDxYEHwEFoAc8bGk+PGEgaHJlZj0iaHR0cDovL3d3dy5nZW9jYWNoaW5nLmNvbS9tYXAvZGVmYXVsdC5hc3B4P2xhdD00Ni4wODA0NyZsbmc9MTQuNSIgdGFyZ2V0PSJfYmxhbmsiPkdlb2NhY2hpbmcuY29tIE1hcDwvYT48L2xpPjxsaT48YSBocmVmPSJodHRwOi8vbWFwcy5nb29nbGUuY29tL21hcHM/cT1OKzQ2JWMyJWIwKzA0LjgyOCtFKzAxNCVjMiViMCszMC4wMDArKEdDM1hYNUopKyIgdGFyZ2V0PSJfYmxhbmsiPkdvb2dsZSBNYXBzPC9hPjwvbGk+PGxpPjxhIGhyZWY9Imh0dHA6Ly93d3cubWFwcXVlc3QuY29tL21hcHMvbWFwLmFkcD9zZWFyY2h0eXBlPWFkZHJlc3MmZm9ybXR5cGU9bGF0bG9uZyZsYXRsb25ndHlwZT1kZWNpbWFsJmxhdGl0dWRlPTQ2LjA4MDQ3JmxvbmdpdHVkZT0xNC41Jnpvb209MTAiIHRhcmdldD0iX2JsYW5rIj5NYXBRdWVzdDwvYT48L2xpPjxsaT48YSBocmVmPSJodHRwOi8vbWFwcy55YWhvby5jb20vI2xhdD00Ni4wODA0NyZsb249MTQuNSZ6b29tPTE2JnE9NDYuMDgwNDcsMTQuNSZjb25mPTEmc3RhcnQ9MSZtdnQ9bSZ0cmY9MCIgdGFyZ2V0PSJfYmxhbmsiPllhaG9vIE1hcHM8L2E+PC9saT48bGk+PGEgaHJlZj0iaHR0cDovL3d3dy5iaW5nLmNvbS9tYXBzL2RlZmF1bHQuYXNweD92PTImbHZsPTE0JnNwPXBvaW50LjQ2LjA4MDQ3XzE0LjVfR0MzWFg1SiIgdGFyZ2V0PSJfYmxhbmsiPkJpbmcgTWFwczwvYT48L2xpPjxsaT48YSBocmVmPSJodHRwOi8vd3d3Lm9wZW5jeWNsZW1hcC5vcmcvP3pvb209MTImbGF0PTQ2LjA4MDQ3Jmxvbj0xNC41IiB0YXJnZXQ9Il9ibGFuayI+T3BlbkN5Y2xlTWFwPC9hPjwvbGk+PGxpPjxhIGhyZWY9Imh0dHA6Ly93d3cub3BlbnN0cmVldG1hcC5vcmcvP21sYXQ9NDYuMDgwNDcmbWxvbj0xNC41Jnpvb209MTIiIHRhcmdldD0iX2JsYW5rIj5PcGVuU3RyZWV0TWFwPC9hPjwvbGk+HwNnZGQCGw9kFgYCAQ8WAh8BBRExMTkgTG9nZ2VkIFZpc2l0c2QCBw8PFgIfBgVDfi9zZWVrL2NhY2hlX2xvZ2Jvb2suYXNweD9ndWlkPTUxZTQwZGVjLTYyNzItNGRhZC05MzRiLWUxNzVkYWFhYzI2NWRkAgkPDxYEHwYFPX4vc2Vlay9nYWxsZXJ5LmFzcHg/Z3VpZD01MWU0MGRlYy02MjcyLTRkYWQtOTM0Yi1lMTc1ZGFhYWMyNjUfAQUiVmlldyB0aGUgSW1hZ2UgR2FsbGVyeSBvZiA3IGltYWdlc2RkAl8PFgIfA2gWAgIBDw9kFgIeBXN0eWxlBQx3aWR0aDoxMjBweDtkAmEPZBYEAgMPFgIfAQUHRW5nbGlzaGQCBQ8WAh8HAhQWKGYPZBYCAgEPDxYIHwgFBWVuLVVTHwkFDVNldFRlbXBMb2NhbGUfAQUHRW5nbGlzaB8KaGRkAgEPZBYCAgEPDxYIHwgFBWRlLURFHwkFDVNldFRlbXBMb2NhbGUfAQUHRGV1dHNjaB8KaGRkAgIPZBYCAgEPDxYIHwgFBWZyLUZSHwkFDVNldFRlbXBMb2NhbGUfAQUJRnJhbsOnYWlzHwpoZGQCAw9kFgICAQ8PFggfCAUFcHQtUFQfCQUNU2V0VGVtcExvY2FsZR8BBQpQb3J0dWd1w6pzHwpoZGQCBA9kFgICAQ8PFggfCAUFY3MtQ1ofCQUNU2V0VGVtcExvY2FsZR8BBQnEjGXFoXRpbmEfCmhkZAIFD2QWAgIBDw8WCB8IBQVkYS1ESx8JBQ1TZXRUZW1wTG9jYWxlHwEFBURhbnNrHwpoZGQCBg9kFgICAQ8PFggfCAUFc3YtU0UfCQUNU2V0VGVtcExvY2FsZR8BBQdTdmVuc2thHwpoZGQCBw9kFgICAQ8PFggfCAUFZXMtRVMfCQUNU2V0VGVtcExvY2FsZR8BBQhFc3Bhw7FvbB8KaGRkAggPZBYCAgEPDxYIHwgFBWV0LUVFHwkFDVNldFRlbXBMb2NhbGUfAQUFRWVzdGkfCmhkZAIJD2QWAgIBDw8WCB8IBQVpdC1JVB8JBQ1TZXRUZW1wTG9jYWxlHwEFCEl0YWxpYW5vHwpoZGQCCg9kFgICAQ8PFggfCAUFZWwtR1IfCQUNU2V0VGVtcExvY2FsZR8BBRDOlc67zrvOt869zrnOus6sHwpoZGQCCw9kFgICAQ8PFggfCAUFbHYtTFYfCQUNU2V0VGVtcExvY2FsZR8BBQlMYXR2aWXFoXUfCmhkZAIMD2QWAgIBDw8WCB8IBQVubC1OTB8JBQ1TZXRUZW1wTG9jYWxlHwEFCk5lZGVybGFuZHMfCmhkZAIND2QWAgIBDw8WCB8IBQVjYS1FUx8JBQ1TZXRUZW1wTG9jYWxlHwEFB0NhdGFsw6AfCmhkZAIOD2QWAgIBDw8WCB8IBQVwbC1QTB8JBQ1TZXRUZW1wTG9jYWxlHwEFBlBvbHNraR8KaGRkAg8PZBYCAgEPDxYIHwgFBW5iLU5PHwkFDVNldFRlbXBMb2NhbGUfAQUOTm9yc2ssIEJva23DpWwfCmhkZAIQD2QWAgIBDw8WCB8IBQVrby1LUh8JBQ1TZXRUZW1wTG9jYWxlHwEFCe2VnOq1reyWtB8KaGRkAhEPZBYCAgEPDxYIHwgFBWh1LUhVHwkFDVNldFRlbXBMb2NhbGUfAQUGTWFneWFyHwpoZGQCEg9kFgICAQ8PFggfCAUFcm8tUk8fCQUNU2V0VGVtcExvY2FsZR8BBQhSb23Dom7Egx8KaGRkAhMPZBYCAgEPDxYIHwgFBWphLUpQHwkFDVNldFRlbXBMb2NhbGUfAQUJ5pel5pys6KqeHwpoZGQClQEPFgIfAQUQJmNvcHk7IDIwMDAtMjAxM2QCAw8WAh8BBStTZXJ2ZXI6IFdFQjE3OyBCdWlsZDogV2ViLkhvdEZpeF8yMDEzMDgyMS4xZGS/Gx6rxzdJpCUf5WchaJRbjfwY8Q==" /> +<input type="hidden" name="__VIEWSTATEFIELDCOUNT" id="__VIEWSTATEFIELDCOUNT" value="3" /> +<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="6sus3jI5A40iM8UO89ECa7Jpxi6Rve2KNWpEBwTUarIdJqKSaJA42oHo1rnl8U2oSZlzZve0PlZzhrBxCzCmAD0RgCwIo0onYG1jFFX79DhmyrQS2Ci/QFOiq+5hZiEB31upPsshi+R7o76TXRgakpryB1l/5xwEJy0fi8Pu2lmGjhuA2yHtG13/ZdAK9tcQrE9JkSn9JnT+YryUqos0I0sQNzESl6uIKX4cQL35G7bD4A/vRydf8yrwDj/c7nHNeDWfCQOz+vVapMLZXMqyhQFH4XP9aillixtOnmSdZns4PO5nPGd2dAjNf1SRp0xeh1gedqoZvaKrgcVlr19DH92L/gQH0y9YKrIdEmm1nWKo5waUSO+dXev4GJa2jwVGtPFXmbevNoMepv9+AZU1ViJNvelfbcHwFCumE2vu1D1ixZZxXrS7St29FyQzIgtqMEB3Az8m/RPCMDMakUGQMnCRXagmfHFgA5HrdknGq0tjEULMNGeH/Bc5/4cWPvlH4BIvCAeG+tqN5pMQflkr0+7nyy8edqVpMJf7Ej6V953rigEdsbBFOB3A9kPZVRZAB3muJd43WlDu95mtVbIXtAKih3ZodzyTr6eEBolatNqST4t6WheR91reWKdM8epZb22WWOQYwHwX+uADQ84LiivFLyRjo3/9Z22C9ZhZUy3azbPwweHU0aLSza+qNwPadXdDrUZMvrA6XKz4vgXgI2xtD559izq7kkryZGyqRoNqzCmxEYzbqUU7tQ8fAREasSrwwcBxf0d2RHQkmh7bnYM/1S03zgqU7VrmaVpBFQMt/LpapX6Gny5u3A+Lg4SwWXStlywwdOAbql0twBpQKtRpFF2UB6LK6ZRVb87M02AbkRehnf+T5KnvE14Fhkjwyq7/8TPj4rBxj5jpAmW77WzDrnLZQPluFXd7Vy4sTKHVKDaYQreHIB4rwt7e0p/9HuH9gG2Uc6GLqr6ZohCdvn4TpJNPGJ/V4VNvUg+1ZT6nbZfaTcvGMlpLYb9IXNiuk6cZ/mnnGXYN9MLIti6PzeDE8Hzm2ELTTcATSkOd/TG5vnuo2jFKH0zy4cMGu1WK7vjMoNgUwn2+rSZxRZNBoC1bSGdtlE/DVpRtF5l6KWon3rnXgi259nrYfjAJSudZHiFy7oJoUjd6ksiIUZclkNw+BnBCa84ThrFVsUEt2Pk7O4tJtz/TMyauaZF1hcaQ8HETiE1IYMbt6W2rS5NILgtSAHNFkzTMhgdIqAOwA+uMU0OwjNms8SlMOI6wBE0/WDM3bAucfMqgfAdMeDmWfXJM72KZpSRxFcljWINjAwbrrEgTcXzDJVRmG5NOyStgBIAr1kdWVGLJqg8lRBoGYeEmf+2muniHBFcZc9L67FUfEeltQNgqP4WGi4pu+G0PhyA7HHKNRx1AT5rm12VtwUNFDSvRDiF9DBSg9XoEaeD4TLQY0j+uH/IrIwWyCUDdaekadJ8yL1LVEeCZvuTiW4of0PUpGHaF6/cT6z773nIyJOTP3SdljFr/I+1ez74uF9w9hQOejbBp7oSHAJ1gwQ6qGg8wvbzLAg9t0LzYLQwRvljmPFEvPio1rFl55zKvpGgxrvZAxmIIqik6yckszVp2XTsKi9bSJ7rBZSU5UQfujI2GMh3Sz9ZsDY1BrSULTjcZAVMtLC26p9LADi1ypmkG8fz4ah4sfjILmVoAxk8FzIBHLxzSxzlt2PNcu8ZFzSMq4uzbH8tBznZQezzRwRU98Mk+yfWI0imQAs64tz0m3/cf+GCRQe2nqKqRPJJcg9q+hGE4WCsfUTqkeNYt5aiI2/Th+3IMMnMlxUPI4hY/VCY83be2NcHj1Zpw3J8+1g6bhCZ9foL/w0JrdUsBj8j4a37wJnud/K0Zj8hi5mvv2c9Azu0zFUwm0rao1kA7nYA9U4+ikJHLl8Oj8QEiyDcta+tFQrBfl2h334T0eyqnq7FIv+M8JdWfTMMdFaNopUg0Rqhhlafw//Pt/Qd+XoxhAPxdwiWqHkL46Ze1C0vqa7CAv3Mf5MZf7GlIPr0Bf5oIW7djRZxRN5OT1vr6AsZM9tOXJcOuSvkID4LRMZ47nHe9zvkEoL+9gfDw4zznEVjce9AFs8TsRGYTSP3JfSxFGoTT/ncat0srvYc9go1Mbf/oEGSaZSlEKyKuJHM6FAWQ+x3H1Fxu/SJrWP/vjgO8xSODwi/Z851/dK3P2jvylGca84r91GtalE23dIiETxVtrBvY+XOsQ18TWrLoSal1NL57d3Blklb8V5XLOi4P9fY08Pn+QCyetVG6enrqArff9GRb1WzfzdLmY7Fv8qE5n6u5hAPqLdXr4+UWiAfL8KRU4YwygWqfU5AmVsbzgb+PhCmdE0xexpGaUvjGgTQdwvHqQkUx1GHLSBD8+frKQptjVP1gQf7StzZrmUq/37O58Sl1oHuUq5vD3Tj/I7UUEPBBx+XgEadar2prplFPariR1OL3BE1I7yrCTmSAuLRFFQInTKqf8NT3coXB1j+IWM8sl4mVnxrSyM50/lMo+eU5mKMYl+gWPWYHtNWfXoKhMlgnfhfauIwTTqgdqPYnBF3e5HPekRCKELr6InZwEHk/o5JdX6uy4XkUwH6OLT0c6XiK8g4ssqi1oc44tSfB/RRqcskfR8FlAlGEDtS+96WIz5rc512JRzu7j47u4esB20b4XCY6ZOJkijd9MuvmZo7epTjerWxZ4VLZg7qtOOu3Zsf7LlzseWjqEFYqrBr3EP4PISm+m8MAsNsbG+0XATR2/OLapHJBeXoZNUbHidmZ0kELNgA8FEdAg2G96CurQyZ8pjT+PfH5bXO98TvawM8pwyZPsNOVDrtMPhTq588IV/a8Dw3BU0/4mTZAAubXHbzXe0d1pt5lnQjNbPaw3AlnGoGJUSyGbVaMUsORYfVfIGYHFJ3QhwQH1drj8CD7qCQsZAIf0u4WMUkCix99wusHbyzsz7tGmAE3oFfls6jdfeaXTvtR5SdMkpgy1NIy24KT1baSAQG8Tail9LTcR20Om0qevJ2vg/WIXknUaVluETsRH8QxGujaI6xmk6KsDFRE/JBhm/THTCjwxHSyIZV25rP/4yh0bV+Ixlmbleqp+yhz7bu+OzXTdc4SOvFZoTMR9Vd0GTMtNZVZpJ6MoWqPHiEpdDH3B39ZLA6WUiqEJpe4Ake23QGUoWnC2Dw00TgNXnCa2qbmUTMz/VXGQaUqy8zy4bQ9WJd2eMOexbyHQHhPUUUEOy/9VoFARlO/R36udQ85lp5wvpBxuSy/ecaVT39qR90x7fXjZysWiQbZKpYKzAhx9RU7QoU2HMX8IYrEhFFiJ6yElFhGRzSbUZVGxZ99aUqYysss6Zsa9xGEtvBRgXySg9InTYjnPemkaHtovwxElKrwM/d945c7Lc2tPVCUjIPaZNbA3jVnultRIw0wn4qXqshlgZsoQKiW+Y/aBa/TI1Ewt+YyT+PEDuXuipBGlzrIV1IteWu1A4oIRBJHeEQGtqWxvFyicX0ST+fENLdGkPBcnnIniS/WSyepeiS+j75M9AJBf+/NW38tSHs0eKxl+vT6mcBF7iy8JLTgi7DC/zvoz/D+durLW5J8UAAycP8pGM2F3BhA6j6LOYVJnj9TrCqZsxcquax9OrsBKgzqdmn4MlF2zhOYAZqCDyN3VSBCLF0qY2y01/2klKJ5YXPQ+B7eBBgJlHSuiV9xYEJqhGMw8ANJBbWQPj2PnzUtszdE/xyxxwzChMxJsz6cZnJYurxkxZPY7kR9dtBIPlZiyL+Ga/fX/BM+b/+XJSDi/VFTBspTRXdNOODrZmnqi9b578EboKIX84F3aPeoTO2lXul/BR2s7uSUQQn8/y53sGmj+zYDS/hXQIIihjj6yWFmFovLMEms7WlEoKUgdbfy8RRoSWpB/JNGz3YP3hdKbva9GrQ6rlKD5lmQs2WuJzN1UXKC2raN1kF2X33QH8Rzd4Y1KPog82vQ2cVTYwX0q+Qq+syuXn3EdRIR/vh+XjGpz7jsNl3ASbXmj2YIdTZc88TsYLqjcBtAsWBH5CMeMlpKUDbjl19BOmpKw5tm+K+qfZ/BxVFGcPSMdVsJB2p+4sWz+wAOi1EQ8VvoZOWtlATwndmr5UWQailPOTKHjuP8KxMKF0fA1G9OUzhgQ4c69bDRcmTwSkCVm0+QSBJNjbFNwCWSp1z/R+XeyfXi4eI1j48jqQkFDfMJ7Es40fdZVeA6Wv9rV+HOodXbHqNmYFXZnbB8CoODtHhu5pPKSww+nU3P5vW+VSjo9K4lOTsbFm2Lx3qjgfmYvDLNniGFjJ64J+qGGRYmta9S//zBfM34U4hPzucCGpcCoUdnNy1G/+9i/FEv87DWORXAXAmt7/ZXrPCHXgDKUNXtPTb4qv/rYenjdrp42QsSHQHxJofOkQ9/8Z8tA3E3NgmM7Q15hyCzBdWXSqa90bnlYzfCWxNFD4tI2N/kCs5j4+0TfCQkjYzVhba3MNA83ySFQ8dscRNYhqgkhzZmjhslyg7gbyuIQfNUZTL8EzzBtsQAK7d3jLOuNoZF7J4vecHJCNiPbdhVvXFWTlAgKnZSUTGH6JZIrN6fvFrMRsUI1I15jhTKIsAAODWBICZNEKHxKK+S/DepdH7+OU3cPyrD8j3x8a1vVhXl0PCGwJyNpdlG9ZakteWkhSTP2GxcnLbJx8RQIl5oMdpEAu9LvLlohQaESCiHxNyaNGZCz9ZoRRlC6AhgwGvNv8wOSwPzSvKPEHrgI+AUT2EjD4f2Ol/do2/ob5S0vmIzS3xNn4RtMeVHWtgr+k9UNwcoQw+dDVPO0RcGTWMEDhsM/IHGLlmP7lD9PtaRrV4gF+TOp5dhlNwtCHH1LdnDNg+DFj852fYpJrYszZL9QrjtM1tldAAvTAfmNDjvd118bHJJHggr4CFDyNrxFxnoSo4YDE+cO+t+qybgAMLarnKkrVaINEO32j84LPuIGgY3OXpjbQGqPKD+B6RQW2Rdd5R9EbaYocwIrp/rXWfjeYtOqXbfVkf5kDf9runckVX/6EmePJCkRGniksIKonUYwQsRTTtUrovc5VmMRc42+EbYqoUpEP7xtOypKcTp1Rhwd7gF" /> +<input type="hidden" name="__VIEWSTATE1" id="__VIEWSTATE1" value="4sXs8+snATkatuqtXsUX/JjsrIZWxY8qf+UiYm/ZHLc7OVAPiJBoOGfX6wMVXGm4QtiuhmaFG45FMWJhWhGnN1ZGCNxY0tq/mnD/74Jl+YbRkuqNTytsTCJjZyaMRBZjvn2d1zTgiLNk0Bz5hRstSRUSxZ9pnwy74qy3WEfSwXAKnPc4zjzmMXdgPJsiRF71xTLVdLNiHP2cWy+Spgh1/gqFuEGh8dEom7QKOYNt8ziUUkKcWRyVa6hW0gAXAde2MiPrsrMCnE+BB087bhOETEXiouz7FiPKFUcyYIZKKTnSsE0VVm0HTwBAoTEvm8n8tZCOYypwPatNUw1fmOkv1Isje/MB9Fyn8FDB4bKfuAe9nCCil3UTgLmG/wV1iQpR+zrxTiJ9jWPJsmFxcyUxpGLX0PoDG+REdYfdmv6fDKR6erg3cyEOafWYrlCVDBnmeoe4VTC/zGS+ZHUWePS0lMLhbVZQY5UiCH1xXQ6aAuyFaFSpLO4lL8wJIBI+LaBOBItRmQAdEEr49r8wijoOzjpMyy++v0fIdPW+hSxAWS17vEoGgRGRAdCtqMc1tHtOikBRKire1YzJ+jmkzdahM+wyK3IZQOPMdpxKPxVM+2Idm/0Ny00Xxuz1MDUYcO+xqxwQhIOkuM+HwhtQg+IsAD6+DikWEc1Qssx1FaBuhiVtdhCsS3xJ1fp6faBNYrIM5TBbDJ9OhTPn7x31/K8k4ZI/stT1NT0/gC/2jScmHqrb69d07ORX3uEd9ggdsMs09/8xEmCqWJUsaq4j7UfsBDSGxJVc/BYE3/1bEf2s3k9RGrwfdQYCC9exr25Rpvy6WuC562FnEhp5J/bu5BV0HAuwaO2B4r9+gwEeW4pufZQCQZEMjxP60Io1CwWUrIcpX8kI49P2FL0KHkcZGpEcEBYp+stnuGLFs14UxZVwdDAiFuLkdH8P3H/TDD0ieW8crKYrPHHlt8pPRTC4uOx3RlXnnJ59mQTazHnB5n6m7ridqwjq/+KpyxettRtG2qLLWeDQ6xV/s7PlwKYakMeEc2x6uJFqUSitNcKOB6wNbIspnL6k6nwbDdIW7gCKtbaEwNnFpt8dOtHc1uIb4rqqWC8953aVPKOmRwTj12mIs1631z9jfxVoTzATyVXmxBngCsgDAq6+lrG2mHQfZ/UFbqkV5CScVs/DJC6w21X2PtiK9KjoxItqWzTI+h/GDJvEA8CyOl7fZ1puE2YKvao2z+rPNM0qYgDkaC+354KV92CsI+CYw+5o2JNW7ySq2ahYlnQkSH7ahoI1ZnBlRnmdK4i9LdTNpchYRDqmtB+1ZUqTNLzrui+13onFprd1vrd5JtO8aLe2TFiHXAbxzrv9VmApNThrhRJ88148ZJ+LnRK42XdKgrAp14l0QaBz1SPXArnmsk/b/0+jttrLPQizt5VueNf9CiOb+U2/dg6dwAdDX8jZqkZUeNigYFFX20tbWWonS9pAHPeDQ4kIZR4t1TEm3Fqj3S1KCZhw662Ok/YgZado5QDCzpTcveTDCW/8C5xgrIqJbK58WKRzejo7mIb0Glg6gPpmxIk1u5phzhZ+8a+ruCsX/rvtoartZZzaJ8vwGs5G48hpYkNedY5zc7MZxRC6+BJuYkxfpOZSXoLEzJd2+BUtUiANynWCXOhEk1rUKUEZ32qCmuPjuob3PP7xvtm52uobwXhm01Xinj8rl3Rna2IaTe793r3L+k0vKh/DWuUugvAbxKUHiMLJ6vIsNKPgVyQL+l+MC9biFW6ms/erG9DwLFGQvgPY1+3vLM4fmg4WdSx6w/EujLxY/n5OHXqU8HVKmvLFnuh0EJpSr2mJlDID4HUPhVKU30qZDqqLohedCcnKqGtVl48kcFqvkqrWM7wIBqDgSmeajApTax7dBs41GO7GItTOhMpG+LwO64zjSrsYaT8L88h5v/14qJLatTtPbEXwCgu8MkiKfxScGsToj6JJsDnQjT1hpXg9IONeJX9K0dnr/Vs1ncQAPtIaTUQwgG5ib/w1UO/JB6rCams0ONQbbjOGW3w4B6KXaPPEgEq2uwAF0aO+ukMWSNubYWZ9z7mVH3u0+8pz0u9Kz9/i2WH3dvmfPVYF1PA0J8PMSHW8w/jYMHo5Yxr18y9CwCwcvh7MehQyqa5JD4BFUcpslbhZ0rEwMs/WqDxpOFlVK3v5dnouYsOEIRgsRi+/eJAtdcvCBkUj5hmwt2+fyjStoV6u2TQNM/nL3CtBJy6ue+8gRpqMG5pwYiLWSim7DToGfI2KK8ZhnUU3D/DufFhIYtJd7EUwnEpDR+X3seEYo3i6fiI/L55kXdF6+QiIvR187hc0TWbxnNtKxAbBprICXLeen5No7Kmot7N7CuVeInbSOsl24iaDN0xKI6n5EgZvGL0SAqLet8vISCFh3sGV5pjMlmOooCPdt2s/LqyxsJQI/c+GJDxpVP1SehWC4uw8hBJ9efUkS8veBDwBgqnhAMFcivseTPSOuKgc/UQ9RjAN49s0thHh4ePI25ogovHsKP12BwiMwBLR0qazaHLEwbngT8oJZ82xsVuGPX5C/xTO5LChYbdHryBj2J0sQSlWL4gSR1ipys8Ub76InpI4xBPFCH5O4c6ntjzVI51uX2XHmovbY0rZtr+ge1myGyFjs9OcnuyuKpVHzoUkb5EMH05v/xAB1PTJpscXWRXQ1V0pIlgRQxUqwa17X5Y7KvDXCTOGZZxjh3YyAcihnvbBmE/0WmVskMVplG7+xx3qD5wYc8iXNEUmGp2CH3f1sY2c2puTAG8EHq8WkCA6Ql2dyzei6tz/3C34OKjPwvol104+sBWLkppe3BD8Tc39cSKW7Dhq+2ELdP3xM2J1NgoNPbCFTYG9tMBiY/E2g2+8DnKXBTEpLLqt0cWH8NB1NGY0pNitD/USgYLABi+cD/BsrVxcZk/26K3AJs9pMXfMNMUHvCjD94NJR0vTmEm7ZuvLq/iQs0C10ZHPTDNoSb7nULZ/pPAE3ilODOwI2iDTEClxD+rFLc9nge7SOA+EQZofKGgzsUoXwHOq73vl0t/Gr72e9NkS6m5/V4QFI2/cWQ6YNFgkBum5dnlQ127y/mPG0lPiyk61hp+XBqwcRosSDPkEuNneUMDoAb6Evg3+5vTQMPJ8K7GoNOfR5aUsEHhH3rFQAVuLn7PD9au9qMaQ1chZn7QenTlu2i8pkbhWYzPZpucAEjeca4ou4rT1xaIMWTu0kBl/n20hwHbQKTH6W1hgK5rncAr5Xa7shq1PBV6O3rDkCQwuVLDv1VXPfmzqA2mve5yIymCdPzPSbB09wNidqzqyV8nw2IDCpbn4O2jbSjDUJki0lrW9gvC/GnZZ1CAPJxiozJpcR5FJ5dWvkM4uOgdG6/KMX8jO8WMOh6GqEe8KchPxJ/KV4Ah2JDV6VCUWlAgU3oyKJEHG30pLWWbeKASTPDKPaVbUvD1KGt2cntqukey0Ajrvv+EGlpKGrmSFKXB6R1hm4ajF5FVCFyAio+YrC+0QzfNkIMl75j/O08IE8RoxEjxH9/TZR6C8xlMcA2V2btuHYflL7z9jT7mCnj1me9Mxz0naLr3v1SGpaXjzcHZWSXgvX4/jeOQ4wc0jVH1JHJbUvFjWAV2s1YJ8r6vWVI1SNm/5zoq8MO1fneamnmrm53F0EzzU+3BHYmDp/TzNcYf1VEL+Ba3xykvFjHPljWAwR0DS2FbTzV8Qa72U+urt8GkqRp0dt6hJSX1UhzXePe2ezxmRQxqJjZsjfdqG/UmEJdeEQ01e77q1qCUqSxqsbzacO5MKkbehLG5ZBymI5TEE0VBzwsb0ilERi2GkyZOQz4S4gOUy4JO0/+WZ/HxmTdw9P1/OMGeKTPSzhZiR6xgF4GYXOlWRdkb/U0eiIUzQAL6HcpV5RDWoaJ2LRECOg/0kgn4vuiHmZh/2XE0yh+U0eP4EuVCP6TCIfkHwe0gAcasWJaOeiUl9s1Her7c460fdXV3pkm2glWGEq/kMavX6EUrtoozh342mab6wWNzXOnLBV0shMAVP0PEJ50KnQBblXZ6aH1/bnj8wtrsSW77UAB+2OXh5S2yyKFMwpBs4Bab7/tODlXNY1lri1chrELF6AWVErzLiIOXLdDqmhBIkjZseBsylPKwH5h8naKGG1vWWpmwIxBMNAUbYXYtNF6aE4yOj17sCVKisIU9S0Lf/k+ngpmwyfrAZVgIOVMg0olLwoy8In9wbVvqBUapVpayVssvVGn99F/C9JdQn/wvJOcKxCGXIoJx7CU2HFn+Z69M6QVA7nccSEALc/shAllw+r3HwEsWKGAmejWJIlYWfJBa50tyjq1IFqfk1Vt5awjeUmIkQ5JsocNtmgGXN/FNbL2REVpkYENfJjU2/usabhID11WNs0qgzdfFXH8CMDPxoBhD60T9aPq9jzY+MciRvvWmYP4bLXo9fOZ1PJtwAgDKnDsf1Tql4HuqEXVolADdk7Z0CfuX37nT0+vB04j2m7HZBgETQnIOxdS4b4MiNWhOKGDA6vVyK4XBxpdzqjwz3ORmZX8s9LYBNtIzsZ8TRndJaA5JBigmQWA0UZhJ5G0scBMgHO89dj322y3O92K7qA2ae+OsTM5Infk7GTOhL+RwUMaM26sGCF9KEN2R3hp4+zW0L4G0UxnxP7wD47b6DN/P/3s10ONkHb+etlLZrqDkXvwakTE7t1jd7WxLEa20ruPFJGpjp2W6s4wRcjsIxGSsQaolkuE1/CD3qHQD3yXwrqjkqsllOfxrySG0l4q/eZD8imn8lH1g+Abu44nFH/euCsYJZIvMWNlb44vfkgK3/eQr4ZDab4b3lN9o0OoGrtPmXD2b7kkl15C9QSmV18BFmjflrqydkDzKcX8FGNskHfeljNL6DjanArFwtSK4rMNfDID9d9dmMacGQM0WhcxBSGz4oDMxutyvgbOyp7ul1ODSp8s+vuNbViazExXrFidBOVBKwf3mKxO+oUMfclNIBSrloofmCH7kodiPE37mI9gVVY35fyXIh01tRGOKRRmFeXuxQtO2v7WRS8DXRfENu/sjK2l9qO7vA/3WfJ2kH1myQYvNfCaml0ZS4TRD0cEICRea3ktbfLdBv" /> +<input type="hidden" name="__VIEWSTATE2" id="__VIEWSTATE2" value="yt+n+rcWzRafWiKdON7frk7OOhyx8s4jAgRWQZELjnkDSeATVQHIS8ch8WfflIisTTnQsrYwNDAdLfRHUibkk2kWYtTS9cXPSdmsUSZIxwGva6CvHxX3HQpDbp+IiDF48LYbDbNNgQEtrMQflTOAD1RkJHFTIYRPnP+XPQoqzI6KbGdw7mOKTNEQVR7e/a0lEzv+DMpaAPAx9JticVtDKnAIkNS6d9s5tJGBCvAy3n/fRX2RjKKohYY800ZXPd8w84KdwmQ0ZOxGXtgDBMTYWS7xLdPPZJ8M9518sGxclnLeke84UgShNQmPEzNnbZ6ERufW0jrYy8+i+EBnLX42puMkIn7O6m+KOilQHoHzWWLB+wmdG4fo7Ey0JqDbvCWFoMZUlYDWWuFG3VpeJRTqDBoQug9xKCEAlKcYG3m7/qdzf49ccujkN3pFmMagSSxp+0mewJZx9rIlialrx6zl35NX7Exh2U2SpfH3vNoqm4cO0I7Ct+1Qh7eFpIRvRiquwUf2p6rM8WEUcwCUaVxeTxvyxpcKUshdExVnb71bIXKl5q5PnKvDVjEkiCA9/qIQgtEvyefl60SJPqVy1sjv60K3GJISyptC9KL/DP2SQFgQcjq47PbALvC6vMbyIp9fLfR/vkVtPy+cjXirdPBv2XqmuBU=" /> </div> <script type="text/javascript"> @@ -91,182 +107,189 @@ function __doPostBack(eventTarget, eventArgument) { </script> -<script src="/WebResource.axd?d=Dh2VENdI9XyWNN0f7DnYfR8WWRCRIzdVqal2y0yjiQ5nC_eHhLchYgnQDHIk0d3RCcSUMVZ36ciRD0qmhXKmeu3S_RE1&t=634981136287450696" type="text/javascript"></script> +<script src="/WebResource.axd?d=pynGkmcFUV13He1Qd6_TZETFLbdR8wwRMdub1eDU5ILCHE7OhotblLUuaNwx3HQ7rYen0g2&t=635418606520000000" type="text/javascript"></script> -<script src="/ScriptResource.axd?d=I9_m2Hb1Tv_B0qTMDG8bMbnkNSHUkv5oUaG9-V5NZ8qQ2VFlu60I8y8gfr3vPmZjbiPnu43MOQdFVDeYF-nDAEKBLmyxD3DCTGmes9NNbbvaDEHyEuuRWgccIkK3ik5TI48YGDxjHjqdn-gTK4Fkgd17LGw1&t=ffffffff940d030f" type="text/javascript"></script> -<script src="/ScriptResource.axd?d=8vNbe34dAujgZMPnfnacfjeoweX1vHgyns8KlAV4vpGpsZC9Cf3pro__lv8ekBa0NiCgXGMMolzOUNH__lrnEI_qjlNBIAuuLeemtAXV_i6E0QIMZa8nGSYmWGF5nQOJK3rmZzvTxsr2Mh4Ebdba_1ywGLUSH_U_XIe-jzecfRQwwvjZ0&t=ffffffff940d030f" type="text/javascript"></script> -<script src="/ScriptResource.axd?d=VZKNgu1isPaAINPNmSDkniHRmEiR365Y3EPnMj7AXSlsW-OjQIYc7VPoltqjVULZ1g8K9CNDrBJuNvUVOZhFqH3NS7yTvcU3NnCeS_FcC9dLDDQ8Q9UH7PW6s-B2gF04VAooHH3Ji69sDY7LPmffv0PShiGc1206s_RLAIzuI952BGa9reXAmYClCuFJH4FjsvPZoM7oG82eR9HPyOS28x0KmiQ1" type="text/javascript"></script> -<script src="/ScriptResource.axd?d=H0ET7B4U8EJwmcAbxjSGxSsqhUYAPJWkcrAuejTBaZ7N1SX_jc3D8QuNKUPa0lO4X3QHb4S3pibvaKNYs1ChjXro9ahCfiZGcYIQu6Ypce_q7YOm_PkXbcyD93f6aDlH4D9eFkkuLiE9Ax4WQgrrygMqipqtLqR7mio-gp6w-ttXgLxtrsFRt-pUjltSCvHgWUwBtVr9wTSTuzeTLswFqWhBmRPpN7g52pLMsR4cwsSOiAKUeb6dRJ_DZWWUA7Gz0XyQM93WLdA3NKoaC8qUufQM3ZOa5RAudzueB87VFLfeuhZXxK1-AsHyrJkHMiOGwGZjN55yCPtP1BEwdRcWpCJwU7Y_efv4APmLdYZJuvt4AxhKqZdv9FBT2n8FSDrgUdJXxmOVf_5r2_lt_khWegXBwRoygGjD3nKfQf8Q-zpyIsJZZDzQ8Td_2c9eDB4Zy1LRk5dNsNZy6hAaWo_Uq50DOnrNf0LY0qwB1ZAd9b-kZLlEUQjaCXSSBeCccOLbgDalKaxQ15-fEh-R_Lqgt1720fRQyzlevhgNz5vknnm2hF_vHLKlKAoJWuuSIFEiIgEz-JeplpCgAtzjjo0mJ2Zdl9v_fAQxwE4XgBXmUTaiSmoT6TxPHJPr-3BmMQSZs2qBNbYrFu53gs5JbsKPA0pdzyHLlfP2bYUDBdjB1oCqq5qW0" type="text/javascript"></script> +<script src="/ScriptResource.axd?d=uHIkleVeDJf4xS50Krz-yEJRbXY2x1dOBEdM7W-QkNpgaumdwaefPzMErSeG_W29-lHX6vl5G7uDafHaYWCx8Z9aLlo8tZwVtV42ISp6LhT6LbxuVUWMo5GyApWAyPOqkSkf1vCyntgT-PmPv-C6FWxsbWo1&t=7e632e9f" type="text/javascript"></script> +<script src="/ScriptResource.axd?d=Jw6tUGWnA15YEa3ai3FadDbNvwkajNGIHz7aGm4w_MLRMuZ5hwlk3bfOsTs3E4cZZ4ktnTFE_MzciTx4exD15JXabrPKGazf6xj6fW1A8vXJoc3OCqf4cg_BDlVl8fQEsQiziDh4kHnJRWZEQotwuLoezlg1&t=7e632e9f" type="text/javascript"></script> +<script src="/ScriptResource.axd?d=6LJZMDufn-hi7iwK2lQP8296QR5YO6IOz4uYaPxelcgS6vmWRn4q9j_6I-JYuGubJ0cTLEvSBNgu19_SXplwBuEvSNxaW-D6g8G3O1I3JX0Je23bSzmF04IEfX4NDqdZvUxknuyplPautJT0TFfqTn6Ju141" type="text/javascript"></script> +<script src="/ScriptResource.axd?d=b2036wvFK_9z4kFZbjt71EW__rE1R7AeA5jN412riXxChR7BMr_AaeKBqtqXJZcM3bbZogbNuYRhiypSoWUSu8oSgQ_HBWkuc37XOvdpKX29tjLUL940vIBcjY53n6yGPIWjY--CRGXbpIZn4li0mlPN_tT7QTaKcgvjBB0qVfz5UIRVbpnQCfpJZiXVYxafH23wPV8Eyz2f58Tu7MdQtW-tWZZ4pEaFzlnEnMQ4IrsddayqKCvrpKKSrfjKkkGyj4b_ZppECkcXtqQAqB2FwDKbF1h__dQsE1qw63jgGBWHJomZ9ivzjFSTGLiIclxouwbYNTpQCyVTHjO8LgQOg0Lp2sFXVBHyxh8Y9focA6TNBEY4vrVdMfNsJfeK9s4b5_PXX0dOxkoTKooL2rhzpe6bOK4v_InKuHn3MqwpKMyXDsbEyHanhYaICFEHZVw6lXQU8T88731VVsI2coLrf7stUQgGI2Y_SWBsU_ZNKVgiyGrXY0VzmIeqzwutnkJr0l9KIXW2nKocpMkT-L3ag9SiAEzZXb_QmNjvCcfTxo5hTXdEkw_e5TPoQzV4yl-nDuRhVnA-xHjoSikxFt6Qy2wa4B_paaSAYOjxXedPZevCJ0JbIZTKqBvTaGmXWP7EHziVBcqxa9eEbdgrVAyvLzYizh04vzHSjbjkgWMooRnzDOsO0" type="text/javascript"></script> <script src="../seek/js/cachedetails.js" type="text/javascript"></script> <script src="../js/latlng.js" type="text/javascript"></script> - <script type="text/javascript"> +<div> + + <input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="C05D5804" /> +</div> + <script type="text/javascript"> //<![CDATA[ Sys.WebForms.PageRequestManager._initialize('ctl00$uxMainScriptManager', 'aspnetForm', [], [], [], 90, 'ctl00'); //]]> </script> - <div id="Top" class="SkipLinks"> - <a id="ctl00_hlSkipLinksNavigation" accesskey="n" title="Skip to Navigation" href="#Navigation">Skip to Navigation</a> - <a id="ctl00_hlSkipLinksContent" accesskey="c" title="Skip to Content" href="#Content">Skip to Content</a> - </div> - <!--[if lte IE 7]> - <div class="WarningMessage PhaseOut"> - <p>Groundspeak is phasing out support for older browsers. Visit the <a href="http://support.groundspeak.com/index.php?pg=kb.page&id=215" title="Browser Support Information">Help Center</a> for more information.</p> - </div> - <![endif]--> - - - <div class="PrintOnly"> - <p> + <div id="Top" class="SkipLinks"> + <a id="ctl00_hlSkipLinksNavigation" accesskey="n" title="Skip to Navigation" href="#Navigation">Skip to Navigation</a> + <a id="ctl00_hlSkipLinksContent" accesskey="c" title="Skip to Content" href="#Content">Skip to Content</a> + </div> + <!--[if lte IE 8]> + <div class="WarningMessage PhaseOut"> + <p>Groundspeak is phasing out support for older browsers. Visit the <a href="http://support.groundspeak.com/index.php?pg=kb.page&id=215" title="Browser Support Information">Help Center</a> for more information.</p> + </div> + <![endif]--> + + + <div class="PrintOnly"> + <p> <img src="/images/logo_print_bw.png" alt="Geocaching.com" /> </p> - <hr /> - </div> - <header id="ctl00_siteHeader"> - <div class="container"> - <h1 class="Logo span-16"> - <a href="../" id="ctl00_HDHomeLink" title="Geocaching" accesskey="h"> - <img src="/images/tlnMasters/geocaching-logo.png" alt="Geocaching" height="43" width="301" /> - </a> - </h1> - <div class="ProfileWidget span-8 last"> - - <div id="ctl00_divSignedIn"> - <p class="Avatar NoBottomSpacing"> - <a id="ctl00_hlHeaderAvatar" accesskey="p" title="Your Profile" href="../my/"><img title="Your Profile" src="http://img.geocaching.com/user/avatar/e8a97a05-a051-4da9-bc93-6a830b866aff.jpg" alt="" style="border-width:0px;" /></a> + <hr /> + </div> + <header id="ctl00_siteHeader"> + <div class="container"> + <h1 class="Logo"> + <a href="../" id="ctl00_HDHomeLink" title="Geocaching" accesskey="h"> + <img src="/images/tlnMasters/geocaching-logo.png" alt="Geocaching" height="43" width="301" /> + </a> + </h1> + <div class="ProfileWidget"> + + <div id="ctl00_divSignedIn"> + <p class="Avatar NoBottomSpacing"> + <a id="ctl00_hlHeaderAvatar" accesskey="p" title="Your Profile" href="../my/"><img title="Your Profile" src="https://d1qqxh9zzqprtj.cloudfront.net/avatar/2025b8b1-7d49-4c1f-b85d-650fe7f7b5c2.jpg" alt="" style="border-width:0px;" /></a> </p> - <p class="SignedInText"> - <strong> - Hello, <a href="/my/default.aspx" title="View Profile for JoSaMaJa" class="SignedInProfileLink">JoSaMaJa</a></strong> (<a id="ctl00_hlSignOut" accesskey="s" title="Sign Out" href="https://www.geocaching.com/login/default.aspx?RESET=Y&redir=http%3a%2f%2fwww.geocaching.com%2fseek%2fcache_details.aspx%3fwp%3dGC3XX5J%26title%3dzarascen-tir%26Submit6%3dGo">Sign Out</a>)<br /> + <p class="SignedInText"> + <strong> + Hello, <a href="/my/default.aspx" title="View Profile for [redacted]" class="SignedInProfileLink">[redacted]</a></strong> (<a id="ctl00_hlSignOut" accesskey="s" title="Sign Out" href="https://www.geocaching.com/login/default.aspx?RESET=Y&redir=http%3a%2f%2fwww.geocaching.com%2fseek%2fcache_details.aspx%3fwp%3dGC3XX5J%26title%3dzarascen-tir">Sign Out</a>)<br /> <span id="ctl00_litPMLevel">Premium Member</span> - <strong style="display: block"> - 484 Caches Found</strong> - - </p> + <strong class="find-count"> + 486 Caches Found</strong> + + <a title="Edit Account Settings" class="icon-settings" href="/account/settings">Account Settings</a> + </p> + </div> </div> - </div> - </div> - </header> - <nav id="Navigation"> - <div class="container"> - <ul class="Menu"> - <li> - <a id="ctl00_hlNavLearn" accesskey="1" title="Learn" href="../guide/">Learn ▼</a> - <ul class="SubMenu"> - <li> - <a id="ctl00_hlSubNavGeocaching101" accesskey="i" title="Geocaching 101" href="../guide/">Geocaching 101</a></li> - <li> - <a id="ctl00_hlSubNavGeocaching2Minutes" title="Geocaching in 2 Minutes" href="../videos/default.aspx#cat=PL939C3CBDC2F2F385&vid=1YTqitVK-Ts">Geocaching in 2 Minutes</a></li> - </ul> - </li> - - <li id="ctl00_liNavProfile"> - <a id="ctl00_hlNavProfile" accesskey="2" title="Your Profile" href="../my/">Your Profile ▼</a> - <ul class="SubMenu"> - <li> - <a id="ctl00_hlSubNavQuickView" accesskey="p" title="Quick View" href="../my/">Quick View</a></li> - <li> - <a id="ctl00_hlSubNavLists" accesskey="q" title="Lists" href="../my/lists.aspx">Lists</a></li> - <li class="ExtraText"> - <a id="ctl00_hlSubNavGeocaches" accesskey="m" title="Geocaches" class="NoRightPadding" href="../my/geocaches.aspx">Geocaches</a> - (<a id="ctl00_hlSubNavGeocachesYours" accesskey="y" title="Your Geocaches" class="NoSidePadding" href="../my/owned.aspx">Yours</a>)</li> - <li class="ExtraText"> - <a id="ctl00_hlSubNavProfileTrackables" title="Trackables" class="NoRightPadding" href="../my/travelbugs.aspx">Trackables</a> - (<a id="ctl00_hlSubNavTrackablesYours" accesskey="8" title="Your Trackables" class="NoSidePadding" href="../track/search.aspx?o=1&uid=4d416461-d1a7-4cc5-8ee7-336bb910feb8">Yours</a>)</li> - <li> - <a id="ctl00_hlSubNavPocketQueries" accesskey="9" title="Pocket Queries" href="../pocket/">Pocket Queries</a></li> - <li> - <a id="ctl00_hlSubNavFieldNotes" accesskey="0" title="Field Notes" href="../my/fieldnotes.aspx">Field Notes</a></li> - <li> - <a id="ctl00_hlSubNavAccount" accesskey="a" title="Account Details" href="../account/">Account Details</a></li> - </ul> - </li> - <li> - <a id="ctl00_hlNavPlay" accesskey="3" title="Play" href="../seek/">Play ▼</a> - <ul class="SubMenu"> - <li> - <a id="ctl00_hlSubNavHide" accesskey="d" title="Hide & Seek a Cache" href="../seek/">Hide & Seek a Cache</a></li> - <li> - <a id="ctl00_hlSubNavLogCache" title="Log a Cache" href="../my/recentlyviewedcaches.aspx">Log a Cache</a></li> - <li> + <a href="http://shop.geocaching.com/default/premium-membership?utm_source=geocaching&utm_medium=links&utm_content=gift&utm_campaign=Geocaching%20Links" class="promo-gift" target="_blank"> + <img src="/account/Content/images/promo/gift-banner.png" height="56" width="164" alt="Give the gift of Geocaching Premium" /> + </a> + </div> + </header> + <nav id="Navigation"> + <div class="container"> + <ul class="Menu"> + <li> + <a id="ctl00_hlNavLearn" accesskey="1" title="Learn" class="Dropdown" href="../guide/">Learn</a> + <ul class="SubMenu"> + <li> + <a id="ctl00_hlSubNavGeocaching101" accesskey="i" title="Geocaching 101" href="../guide/">Geocaching 101</a></li> + <li> + <a id="ctl00_hlSubNavGeocaching2Minutes" title="Videos" href="../videos/default.aspx#cat=PL939C3CBDC2F2F385&vid=1YTqitVK-Ts">Videos</a></li> + </ul> + </li> + + <li id="ctl00_liNavProfile"> + <a id="ctl00_hlNavProfile" accesskey="2" title="Your Profile" class="Dropdown" href="../my/">Your Profile</a> + <ul class="SubMenu"> + <li> + <a id="ctl00_hlSubNavQuickView" accesskey="p" title="Quick View" href="../my/">Quick View</a></li> + <li> + <a id="ctl00_hlSubNavLists" accesskey="q" title="Lists" href="../my/lists.aspx">Lists</a></li> + <li class="ExtraText"> + <a id="ctl00_hlSubNavGeocaches" accesskey="m" title="Geocaches" class="NoRightPadding" href="../my/geocaches.aspx">Geocaches</a> + (<a id="ctl00_hlSubNavGeocachesYours" accesskey="y" title="Your Geocaches" class="NoSidePadding" href="../my/owned.aspx">Yours</a>)</li> + <li class="ExtraText"> + <a id="ctl00_hlSubNavProfileTrackables" title="Trackables" class="NoRightPadding" href="../my/travelbugs.aspx">Trackables</a> + (<a id="ctl00_hlSubNavTrackablesYours" accesskey="8" title="Your Trackables" class="NoSidePadding" href="../track/search.aspx?o=1&uid=8eb527ff-2b0b-4391-96c3-98bd28eca905">Yours</a>)</li> + <li> + <a id="ctl00_hlSubNavPocketQueries" accesskey="9" title="Pocket Queries" href="../pocket/">Pocket Queries</a></li> + <li> + <a id="ctl00_hlSubNavFieldNotes" accesskey="0" title="Field Notes" href="../my/fieldnotes.aspx">Field Notes</a></li> + </ul> + </li> + <li> + <a id="ctl00_hlNavPlay" accesskey="3" title="Play" class="Dropdown" href="../seek/">Play</a> + <ul class="SubMenu"> + <li> + <a id="ctl00_hlSubNavHide" accesskey="d" title="Hide & Seek a Cache" href="../seek/">Hide & Seek a Cache</a></li> + <li id="ctl00_liSubNavLogCache"> + <a id="ctl00_hlSubNavLogCache" title="Log a Cache" href="../my/recentlyviewedcaches.aspx">Log a Cache</a></li> + <li> <a id="ctl00_hlSubNavMap" accesskey="/" title="View Geocache Map" href="../map/">View Geocache Map</a></li> - <li> - <a id="ctl00_hlSubNavTrackables" accesskey="e" title="Find Trackables" href="../track/">Find Trackables</a></li> - <li> - <a id="ctl00_hlSubNavGeoTours" title="GeoTours" href="../adventures/geotours">GeoTours</a> - </li> - <li> - <a id="ctl00_hlSubNavHelpCenter" title="Help Center" rel="external" href="http://support.groundspeak.com/index.php">Help Center</a></li> - </ul> - </li> - <li> - <a id="ctl00_hlNavCommunity" accesskey="6" title="Community" href="../forums/">Community ▼</a> - <ul class="SubMenu"> - - <li> - <a id="ctl00_hlSubNavTellaFriend" accesskey="-" title="Tell a Friend" href="../account/SendReferral.aspx">Tell a Friend</a> - </li> - - <li> + <li> + <a id="ctl00_hlSubNavTrackables" accesskey="e" title="Find Trackables" href="../track/">Find Trackables</a></li> + <li> + <a id="ctl00_hlSubNavGeoTours" title="GeoTours" href="../adventures/geotours">GeoTours</a> + </li> + <li> + <a id="ctl00_hlSubNavHelpCenter" title="Help Center" rel="external" href="http://support.groundspeak.com/index.php">Help Center</a></li> + </ul> + </li> + <li> + <a id="ctl00_hlNavCommunity" accesskey="6" title="Community" class="Dropdown" href="../forums/">Community</a> + <ul class="SubMenu"> + + <li> + <a id="ctl00_hlSubNavTellaFriend" accesskey="-" title="Tell a Friend" href="../account/referafriend">Tell a Friend</a> + </li> + + <li> <a id="ctl00_hlSubNavVolunteers" accesskey="+" title="Volunteers" href="../volunteers/">Volunteers</a></li> <li> - <a id="ctl00_hlSubNavLocal" accesskey="z" title="Local Organizations" href="../organizations/">Local Organizations</a></li> - <li> - <a id="ctl00_hlSubNavDiscussionForums" accesskey="f" title="Discussion Forums" href="../forums/">Discussion Forums</a></li> - <li> - <a id="ctl00_hlSubNavBlog" accesskey="b" title="Blog" rel="external" href="http://blog.geocaching.com/">Blog</a></li> - <li> - <a id="ctl00_hlSubNavEvents" accesskey="v" title="Events" href="../calendar/">Events</a></li> - </ul> - </li> - <li> - <a id="ctl00_hlNavShop" accesskey="4" title="Shop" href="http://shop.geocaching.com/?utm_source=Geocaching&utm_medium=Links&utm_content=Header&utm_campaign=Geocaching+Links">Shop ▼</a> - <ul class="SubMenu"> - <li> - <a id="ctl00_hlSubNavShop" accesskey="j" title="Shop Geocaching" rel="external" href="http://shop.geocaching.com/?utm_source=Geocaching&utm_medium=Links&utm_content=Header&utm_campaign=Geocaching+Links">Shop Geocaching</a></li> - <li> - <a id="ctl00_hlSubNavIntlRetailers" title="International Retailers" rel="external" href="http://shop.geocaching.com/default/international-retailers/">International Retailers</a></li> - <li> - <a id="ctl00_hlSubNavGPSReviews" accesskey="w" title="GPS Reviews" href="/reviews/gps">GPS Reviews</a></li> - <li> - <a id="ctl00_hlSubNavGPSGuide" accesskey="k" title="Guide to Buying a GPS Device" href="../about/buying.aspx">Guide to Buying a GPS Device</a></li> - </ul> - </li> - <li> - <a id="ctl00_hlNavPartnering" accesskey="5" title="Partnering" href="../travel/">Partnering ▼</a> - <ul class="SubMenu"> - <li> - <a id="ctl00_hlSubNavTravel" title="Travel and GeoTourism" href="../travel/">Travel and GeoTourism</a></li> - <li> - <a id="ctl00_hlSubNavBrandedPromotions" title="Branded Promotions" href="../brandedpromotions/">Branded Promotions</a></li> - <li> - <a id="ctl00_hlSubNavEducation" title="Geocaching and Education" href="../education/">Geocaching and Education</a></li> - <li> - <a id="ctl00_hlSubNavAdvertisingWithUs" title="Advertising with Us" href="../about/advertising.aspx">Advertising with Us</a></li> - <li> - <a id="ctl00_hlSubNavAPIProgram" title="API Program" href="../live/apidevelopers/">API Program</a></li> - </ul> - </li> - <li> - <a id="ctl00_hlNavVideos" accesskey="7" title="Videos" href="../videos/">Videos</a></li> - <li> - <a id="ctl00_hlNavFollowUs" title="Follow Us" href="http://www.facebook.com/geocaching">Follow Us ▼</a> - <ul class="SubMenu NavSocialMedia"> - <li> - <a id="ctl00_hlSubNavFacebook" title="Facebook" class="SubNavFacebook" href="http://www.facebook.com/geocaching">Facebook</a></li> - <li> - <a id="ctl00_hlSubNavYouTube" title="YouTube" class="SubNavYouTube" href="http://www.youtube.com/user/GoGeocaching">YouTube</a></li> - <li> - <a id="ctl00_hlSubNavInstagram" title="Instagram" class="SubNavInstagram" href="http://instagram.com/gogeocaching">Instagram</a></li> - <li> - <a id="ctl00_hlSubNavTwitter" title="Twitter" class="SubNavTwitter" href="http://twitter.com/GoGeocaching">Twitter</a></li> + <a id="ctl00_hlSubNavLocal" accesskey="z" title="Local Organizations" href="../organizations/">Local Organizations</a></li> + <li> + <a id="ctl00_hlSubNavDiscussionForums" accesskey="f" title="Discussion Forums" href="../forums/">Discussion Forums</a></li> + <li> + <a id="ctl00_hlSubNavBlog" accesskey="b" title="Blog" rel="external" href="http://blog.geocaching.com/">Blog</a></li> + <li> + <a id="ctl00_hlSubNavEvents" accesskey="v" title="Events" href="../calendar/">Events</a></li> </ul> - </li> - </ul> - <div class="LanguageSelector"> - + </li> + <li> + <a id="ctl00_hlNavShop" accesskey="4" title="Shop" class="Dropdown" href="http://shop.geocaching.com/?utm_source=Geocaching&utm_medium=Links&utm_content=Header&utm_campaign=Geocaching+Links">Shop</a> + <ul class="SubMenu"> + <li> + <a id="ctl00_hlSubNavShop" accesskey="j" title="Shop Geocaching" rel="external" href="http://shop.geocaching.com/?utm_source=Geocaching&utm_medium=Links&utm_content=Header&utm_campaign=Geocaching+Links">Shop Geocaching</a></li> + <li> + <a id="ctl00_hlSubNavIntlRetailers" title="International Retailers" rel="external" href="http://shop.geocaching.com/default/international-retailers/">International Retailers</a></li> + <li> + <a id="ctl00_hlSubNavGPSGuide" accesskey="k" title="Guide to Buying a GPS Device" href="../about/buying.aspx">Guide to Buying a GPS Device</a></li> + <li> + <a id="ctl00_hlBecomePremium" title="Become a Premium Member" href="https://payments.geocaching.com/?upgrade=true">Become a Premium Member</a> + </li> + </ul> + </li> + <li> + <a id="ctl00_hlNavPartnering" accesskey="5" title="Partnering" class="Dropdown" href="../travel/">Partnering</a> + <ul class="SubMenu"> + <li> + <a id="ctl00_hlSubNavTravel" title="Travel and GeoTourism" href="../travel/">Travel and GeoTourism</a></li> + <li> + <a id="ctl00_hlSubNavBrandedPromotions" title="Branded Promotions" href="../brandedpromotions/">Branded Promotions</a></li> + <li> + <a id="ctl00_hlSubNavEducation" title="Geocaching and Education" href="../education/">Geocaching and Education</a></li> + <li> + <a id="ctl00_hlSubNavAdvertisingWithUs" title="Advertising with Us" href="../about/advertising.aspx">Advertising with Us</a></li> + <li> + <a id="ctl00_hlSubNavAPIProgram" title="API Program" href="../live/apidevelopers/">API Program</a></li> + </ul> + </li> + <li> + <a id="ctl00_hlNavVideos" accesskey="7" title="Videos" href="../videos/">Videos</a></li> + <li> + <a id="ctl00_hlNavFollowUs" title="Follow Us" class="Dropdown" href="http://www.facebook.com/geocaching">Follow Us</a> + <ul class="SubMenu NavSocialMedia"> + <li> + <a id="ctl00_hlSubNavFacebook" title="Facebook" class="SubNavFacebook" href="http://www.facebook.com/geocaching">Facebook</a></li> + <li> + <a id="ctl00_hlSubNavYouTube" title="YouTube" class="SubNavYouTube" href="http://www.youtube.com/user/GoGeocaching">YouTube</a></li> + <li> + <a id="ctl00_hlSubNavInstagram" title="Instagram" class="SubNavInstagram" href="http://instagram.com/geocaching">Instagram</a></li> + <li> + <a id="ctl00_hlSubNavTwitter" title="Twitter" class="SubNavTwitter" href="http://twitter.com/GoGeocaching">Twitter</a></li> + </ul> + </li> + </ul> + <div class="LanguageSelector"> + <div class="LocaleText"> @@ -277,50 +300,54 @@ Sys.WebForms.PageRequestManager._initialize('ctl00$uxMainScriptManager', 'aspnet <div class="selected-language"> - <a href="#">English▼</a> + <a href="#">English ▼</a> </div> <ul class="language-list"> - <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl00_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl00$uxLocaleItem','')">English</a></li> + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl00_uxLocaleItem" class="selected" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl00$uxLocaleItem','')">English</a></li> - <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl01_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl01$uxLocaleItem','')">Deutsch</a></li> + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl01_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl01$uxLocaleItem','')">Català</a></li> - <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl02_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl02$uxLocaleItem','')">Français</a></li> + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl02_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl02$uxLocaleItem','')">Čeština</a></li> - <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl03_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl03$uxLocaleItem','')">Português</a></li> + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl03_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl03$uxLocaleItem','')">Dansk</a></li> - <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl04_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl04$uxLocaleItem','')">Čeština</a></li> + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl04_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl04$uxLocaleItem','')">Deutsch</a></li> - <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl05_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl05$uxLocaleItem','')">Dansk</a></li> + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl05_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl05$uxLocaleItem','')">Ελληνικά</a></li> - <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl06_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl06$uxLocaleItem','')">Svenska</a></li> + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl06_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl06$uxLocaleItem','')">Eesti</a></li> <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl07_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl07$uxLocaleItem','')">Español</a></li> - <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl08_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl08$uxLocaleItem','')">Eesti</a></li> + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl08_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl08$uxLocaleItem','')">Français</a></li> <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl09_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl09$uxLocaleItem','')">Italiano</a></li> - <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl10_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl10$uxLocaleItem','')">Ελληνικά</a></li> + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl10_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl10$uxLocaleItem','')">日本語</a></li> - <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl11_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl11$uxLocaleItem','')">Latviešu</a></li> + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl11_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl11$uxLocaleItem','')">한국어</a></li> - <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl12_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl12$uxLocaleItem','')">Nederlands</a></li> + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl12_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl12$uxLocaleItem','')">Latviešu</a></li> - <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl13_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl13$uxLocaleItem','')">Català</a></li> + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl13_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl13$uxLocaleItem','')">Magyar</a></li> - <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl14_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl14$uxLocaleItem','')">Polski</a></li> + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl14_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl14$uxLocaleItem','')">Nederlands</a></li> <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl15_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl15$uxLocaleItem','')">Norsk, Bokmål</a></li> - <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl16_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl16$uxLocaleItem','')">한국어</a></li> + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl16_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl16$uxLocaleItem','')">Polski</a></li> - <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl17_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl17$uxLocaleItem','')">Magyar</a></li> + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl17_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl17$uxLocaleItem','')">Português</a></li> <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl18_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl18$uxLocaleItem','')">Română</a></li> - <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl19_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl19$uxLocaleItem','')">日本語</a></li> + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl19_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl19$uxLocaleItem','')">Русский</a></li> + + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl20_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl20$uxLocaleItem','')">Suomi</a></li> + + <li><a id="ctl00_uxLocaleListTop_uxLocaleList_ctl21_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleListTop$uxLocaleList$ctl21$uxLocaleItem','')">Svenska</a></li> </ul> @@ -347,20 +374,20 @@ Sys.WebForms.PageRequestManager._initialize('ctl00$uxMainScriptManager', 'aspnet }); }); </script> + </div> </div> - </div> - </nav> - <section id="Content"> - - <div class="container"> - <div id="ctl00_divBreadcrumbs" class="BreadcrumbWidget span-24 last"> - <p> - <span id="ctl00_Breadcrumbs"><span><a title="Geocaching - The Official Global GPS Cache Hunt Site" href="/">Geocaching</a></span><span> > </span><span><a title="Hide and Seek A Geocache" href="/seek/default.aspx">Hide and Seek A Geocache</a></span><span> > </span><span>Geocache Details</span></span> - </p> - - </div> - <div id="ctl00_divContentMain" class="span-24 last"> - + </nav> + <section id="Content"> + + <div class="container"> + <div id="ctl00_divBreadcrumbs" class="BreadcrumbWidget span-24 last"> + <p> + <span id="ctl00_Breadcrumbs"><span><a title="Geocaching - The Official Global GPS Cache Hunt Site" href="/">Geocaching</a></span><span> > </span><span><a title="Hide and Seek A Geocache" href="/seek/default.aspx">Hide and Seek A Geocache</a></span><span> > </span><span>Geocache Details</span></span> + </p> + + </div> + <div id="ctl00_divContentMain" class="span-24 last"> + <div id="ctl00_ContentBody_CoordInfoLinkControl1_uxCoordInfoLinkPanel" class="CoordInfoLinkWidget"> @@ -407,11 +434,12 @@ Sys.WebForms.PageRequestManager._initialize('ctl00$uxMainScriptManager', 'aspnet </script> + <div class="span-17"> <div class="span-17 last BottomSpacing" id="cacheDetails"> <p class="cacheImage"> - <a href="/about/cache_types.aspx" target="_blank" title="About Cache Types"><img src="/images/WptTypes/2.gif" alt="Traditional Cache" title="Traditional Cache" /></a> + <a href="/about/cache_types.aspx" target="_blank" title="About Cache Types"><img src="/images/WptTypes/2.gif" alt="Traditional Geocache" title="Traditional Geocache" /></a> </p> <h2 class="NoBottomSpacing"> @@ -494,7 +522,7 @@ Sys.WebForms.PageRequestManager._initialize('ctl00$uxMainScriptManager', 'aspnet </p> - + <p class="OldWarning NoBottomSpacing"><strong>Cache Issues:</strong></p><ul class="OldWarning"><li>This cache is temporarily unavailable. Read the logs below to read the status for this cache.</li></ul> <div id="ctl00_ContentBody_CacheInformationTable" class="CacheInformationTable"> <div class="LocationData FloatContainer"> @@ -513,7 +541,7 @@ Sys.WebForms.PageRequestManager._initialize('ctl00$uxMainScriptManager', 'aspnet </div> <div class="span-7 last AlignRight"> <span id="ctl00_ContentBody_Location">In Slovenia</span><br /> - <span id="lblDistFromHome"><img src="/images/icons/compass/SE.gif" alt="SE" style="vertical-align:text-bottom" /> SE 765.5 km from your home location</span> + <span id="lblDistFromHome"><img src="/images/icons/compass/E.gif" alt="E" style="vertical-align:text-bottom" /> E 962.4 km from your home location</span> </div> </div> <div class="DownloadLinks"> @@ -525,7 +553,7 @@ Sys.WebForms.PageRequestManager._initialize('ctl00$uxMainScriptManager', 'aspnet <a id="ctl00_ContentBody_lnkPrintFriendly" href="../seek/cdpf.aspx?guid=51e40dec-6272-4dad-934b-e175daaac265" target="_blank">No Logs</a> <a id="ctl00_ContentBody_lnkPrintFriendly5Logs" href="../seek/cdpf.aspx?guid=51e40dec-6272-4dad-934b-e175daaac265&lc=5" target="_blank">5 Logs</a> <a id="ctl00_ContentBody_lnkPrintFriendly10Logs" href="../seek/cdpf.aspx?guid=51e40dec-6272-4dad-934b-e175daaac265&lc=10" target="_blank">10 Logs</a> - <a id="ctl00_ContentBody_lnkPrintDirectionsSimple" class="DrivingDirections" href="http://maps.google.com/maps?f=d&hl=en&saddr=52.21625,9.714483 (Home Location)&daddr=46.080467,14.5(Zara%c5%a1%c4%8den+Tir)" target="_blank">Driving Directions</a> + <a id="ctl00_ContentBody_lnkPrintDirectionsSimple" class="DrivingDirections" href="http://maps.google.com/maps?f=d&hl=en&saddr=48.822509,2.342255 (Home Location)&daddr=46.080467,14.5(Zara%c5%a1%c4%8den+Tir)" target="_blank">Driving Directions</a> </dd> </dl> <dl id="ctl00_ContentBody_uxPrintPDFSection" style="display: none;"> @@ -618,12 +646,13 @@ PRINESI SVOJE PISALO / BRING YOUR OWN PEN<br /></span> <a href="/seek/log.aspx?ID=3220672&lcn=1" id="ctl00_ContentBody_GeoNav_logButton" class="Button LogVisit">Log your visit</a> <ul> - <li><a href="/seek/gallery.aspx?guid=51e40dec-6272-4dad-934b-e175daaac265">View Gallery</a> (7)</li> + <li><a href="/seek/gallery.aspx?guid=51e40dec-6272-4dad-934b-e175daaac265">View Gallery</a> (9)</li> <li><a href="/my/watchlist.aspx?w=3220672">Watch</a> (0)</li> <li><a href="/bookmarks/mark.aspx?guid=51e40dec-6272-4dad-934b-e175daaac265&WptTypeID=2">Bookmark</a></li> <li><a href="/bookmarks/ignore.aspx?guid=51e40dec-6272-4dad-934b-e175daaac265&WptTypeID=2">Ignore</a></li> </ul> + </div> @@ -640,7 +669,7 @@ PRINESI SVOJE PISALO / BRING YOUR OWN PEN<br /></span> Attributes </h3> <div class="WidgetBody"> - <img src="/images/attributes/stroller-no.gif" alt="not stroller accessible" title="not stroller accessible" width="30" height="30" /> <img src="/images/attributes/kids-no.gif" alt="no kids" title="no kids" width="30" height="30" /> <img src="/images/attributes/bicycles-yes.gif" alt="bikes allowed" title="bikes allowed" width="30" height="30" /> <img src="/images/attributes/night-yes.gif" alt="recommended at night" title="recommended at night" width="30" height="30" /> <img src="/images/attributes/available-yes.gif" alt="available 24-7" title="available 24-7" width="30" height="30" /> <img src="/images/attributes/stealth-yes.gif" alt="stealth required" title="stealth required" width="30" height="30" /> <img src="/images/attributes/public-yes.gif" alt="public transit available" title="public transit available" width="30" height="30" /> <img src="/images/attributes/parking-yes.gif" alt="parking available" title="parking available" width="30" height="30" /> <img src="/images/attributes/onehour-yes.gif" alt="takes less than 1 hour" title="takes less than 1 hour" width="30" height="30" /> <img src="/images/attributes/hike_short-yes.gif" alt="hike shorter than 1km" title="hike shorter than 1km" width="30" height="30" /> <img src="/images/attributes/parkngrab-yes.gif" alt="park and grab" title="park and grab" width="30" height="30" /> <img src="/images/attributes/dogs-yes.gif" alt="dogs allowed" title="dogs allowed" width="30" height="30" /> <p class="NoBottomSpacing"><small><a href="/about/icons.aspx" title="What are Attributes?">What are Attributes?</a></small></p> + <img src="/images/attributes/kids-no.gif" alt="no kids" title="no kids" width="30" height="30" /> <img src="/images/attributes/stroller-no.gif" alt="not stroller accessible" title="not stroller accessible" width="30" height="30" /> <img src="/images/attributes/stealth-yes.gif" alt="stealth required" title="stealth required" width="30" height="30" /> <img src="/images/attributes/bicycles-yes.gif" alt="bikes allowed" title="bikes allowed" width="30" height="30" /> <img src="/images/attributes/night-yes.gif" alt="recommended at night" title="recommended at night" width="30" height="30" /> <img src="/images/attributes/available-yes.gif" alt="available 24-7" title="available 24-7" width="30" height="30" /> <img src="/images/attributes/hike_short-yes.gif" alt="hike shorter than 1km" title="hike shorter than 1km" width="30" height="30" /> <img src="/images/attributes/public-yes.gif" alt="public transit available" title="public transit available" width="30" height="30" /> <img src="/images/attributes/parking-yes.gif" alt="parking available" title="parking available" width="30" height="30" /> <img src="/images/attributes/onehour-yes.gif" alt="takes less than 1 hour" title="takes less than 1 hour" width="30" height="30" /> <img src="/images/attributes/parkngrab-yes.gif" alt="park and grab" title="park and grab" width="30" height="30" /> <img src="/images/attributes/dogs-yes.gif" alt="dogs allowed" title="dogs allowed" width="30" height="30" /> <p class="NoBottomSpacing"><small><a href="/about/icons.aspx" title="What are Attributes?">What are Attributes?</a></small></p> </div> </div> @@ -651,14 +680,14 @@ PRINESI SVOJE PISALO / BRING YOUR OWN PEN<br /></span> <script type='text/javascript'> googletag.cmd.push(function() {{ -googletag.defineSlot('/1011121/cache_details_pg_120x240', [120, 240], 'div_1cd2f642-a5e3-42dc-a38f-7033ff6b8c42').addService(googletag.pubads()); +googletag.defineSlot('/1011121/cache_details_pg_120x240', [120, 240], 'div_89c7e4ec-8ce8-4694-bea0-1662333ebc69').addService(googletag.pubads()); googletag.pubads().enableSingleRequest(); googletag.enableServices(); }}); </script> -<div id='div_1cd2f642-a5e3-42dc-a38f-7033ff6b8c42'> +<div id='div_89c7e4ec-8ce8-4694-bea0-1662333ebc69'> <script type='text/javascript'> -googletag.cmd.push(function() { googletag.display('div_1cd2f642-a5e3-42dc-a38f-7033ff6b8c42'); }); +googletag.cmd.push(function() { googletag.display('div_89c7e4ec-8ce8-4694-bea0-1662333ebc69'); }); </script> </div> @@ -699,32 +728,28 @@ googletag.cmd.push(function() { googletag.display('div_1cd2f642-a5e3-42dc-a38f-7 </div> - <div class="CacheDetailNavigationWidget"> - - <h3 class="WidgetHeader">My Bookmark Lists</h3> + <h3 class="WidgetHeader"> + Bookmark Lists + </h3> <div class="WidgetBody"> - <ul class="BookmarkList"> <li class=''> - <a href="http://www.geocaching.com/bookmarks/view.aspx?guid=14551708-0c3c-4f95-9342-1bb3119e6efe">cgeo mocks</a><br /> by <a href="http://www.geocaching.com/profile/?guid=4d416461-d1a7-4cc5-8ee7-336bb910feb8">JoSaMaJa</a> + <a href="http://www.geocaching.com/bookmarks/view.aspx?guid=8d803709-853c-4f06-974a-861fadf83e16">FTF LIST</a><br />by <a href="http://www.geocaching.com/profile/?guid=76f3cd2a-8ff4-45a8-afd1-4ce98d8474bd">bojank</a> </li> </ul> <p class="NoBottomSpacing"> - + <a href="/bookmarks/default.aspx?guid=51e40dec-6272-4dad-934b-e175daaac265&WptTypeID=2" title="View all bookmark lists...">View all bookmark lists...</a> </p> - </div> - - </div> - + </div> @@ -801,16 +826,16 @@ googletag.cmd.push(function() { googletag.display('div_1cd2f642-a5e3-42dc-a38f-7 <div class="InformationWidget Clear"> <h3> - 119 Logged Visits + 174 Logged Visits </h3> <div class="EncryptDecrypt"> <a href="#" class="decrypt-link"> Decrypt </a> </div> - <span id="ctl00_ContentBody_lblFindCounts"><p class="LogTotals"><img src="/images/logtypes/2.png" alt="Found it" title="Found it" /> 112 <img src="/images/logtypes/3.png" alt="Didn't find it" title="Didn't find it" /> 1 <img src="/images/logtypes/4.png" alt="Write note" title="Write note" /> 1 <img src="/images/logtypes/24.png" alt="Publish Listing" title="Publish Listing" /> 2 <img src="/images/logtypes/25.png" alt="Retract Listing" title="Retract Listing" /> 1 <img src="/images/logtypes/45.png" alt="Needs Maintenance" title="Needs Maintenance" /> 1 <img src="/images/logtypes/46.png" alt="Owner Maintenance" title="Owner Maintenance" /> 1 </p></span> + <span id="ctl00_ContentBody_lblFindCounts"><p class="LogTotals"><img src="/images/logtypes/2.png" alt="Found it" title="Found it" /> 155 <img src="/images/logtypes/3.png" alt="Didn't find it" title="Didn't find it" /> 8 <img src="/images/logtypes/4.png" alt="Write note" title="Write note" /> 2 <img src="/images/logtypes/7.png" alt="Needs Archived" title="Needs Archived" /> 1 <img src="/images/logtypes/22.png" alt="Temporarily Disable Listing" title="Temporarily Disable Listing" /> 1 <img src="/images/logtypes/24.png" alt="Publish Listing" title="Publish Listing" /> 2 <img src="/images/logtypes/25.png" alt="Retract Listing" title="Retract Listing" /> 1 <img src="/images/logtypes/45.png" alt="Needs Maintenance" title="Needs Maintenance" /> 2 <img src="/images/logtypes/46.png" alt="Owner Maintenance" title="Owner Maintenance" /> 2 </p></span> <p class="HalfLeft"> - <a id="ctl00_ContentBody_uxLogbookLink" href="../seek/cache_logbook.aspx?guid=51e40dec-6272-4dad-934b-e175daaac265">View Logbook</a> | <a id="ctl00_ContentBody_uxGalleryImagesLink" DisplayFormatPlural="View the Image Gallery of {0:#,###} images" DisplayFormatSingular="View the Image Gallery" href="../seek/gallery.aspx?guid=51e40dec-6272-4dad-934b-e175daaac265">View the Image Gallery of 7 images</a> + <a id="ctl00_ContentBody_uxLogbookLink" href="../seek/cache_logbook.aspx?guid=51e40dec-6272-4dad-934b-e175daaac265">View Logbook</a> | <a id="ctl00_ContentBody_uxGalleryImagesLink" DisplayFormatPlural="View the Image Gallery of {0:#,###} images" DisplayFormatSingular="View the Image Gallery" href="../seek/gallery.aspx?guid=51e40dec-6272-4dad-934b-e175daaac265">View the Image Gallery of 9 images</a> </p> <p class="NoBottomSpacing AlignRight"> <span class="Warning">**Warning!</span> <a href="/about/glossary.aspx#spoiler" title="Spoilers">Spoilers</a> may be included in the descriptions or links. @@ -839,7 +864,7 @@ googletag.cmd.push(function() { googletag.display('div_1cd2f642-a5e3-42dc-a38f-7 </div> <p> <small> - Current Time: <time datetime="2013-08-21T21:12:17Z">08/21/2013 21:12:17 Pacific Daylight Time (04:12 GMT)</time><br/>Last Updated: <time class="timeago" datetime="2013-08-18T20:01:06Z">2013-08-18T20:01:06Z</time> on 08/18/2013 13:01:06 Pacific Daylight Time (20:01 GMT) <br/>Rendered From:Unknown<br />Coordinates are in the WGS84 datum + Current Time: <time datetime="2015-01-01T03:35:55Z">01/01/2015 03:35:55 (UTC-08:00) Pacific Time (US & Canada) (11:35 GMT)</time><br/>Last Updated: <time class="timeago" datetime="2014-12-13T19:58:03Z">2014-12-13T19:58:03Z</time> on 12/13/2014 11:58:03 (UTC-08:00) Pacific Time (US & Canada) (19:58 GMT) <br/>Rendered From:Unknown<br />Coordinates are in the WGS84 datum </small> </p> <div id="topScroll" class="TopScroll" style="display: none;"> @@ -944,7 +969,7 @@ googletag.cmd.push(function() { googletag.display('div_1cd2f642-a5e3-42dc-a38f-7 <dl class="ccu-parseverify" style="display: none;"> <dt>Change To:</dt> <dd> - <span class="ccu-parseverify-coords">N 32°38.880′, W 097°23.755′</span> + <span class="ccu-parseverify-coords">N 32°38.880′, W 097°23.755′</span> </dd> <dt> </dt> <dd> @@ -955,14 +980,14 @@ googletag.cmd.push(function() { googletag.display('div_1cd2f642-a5e3-42dc-a38f-7 </div> </script> - </div> - - </div> - </section> - <footer> - <div class="container"> - <div class="span-24 last FooterTop"> + </div> + </div> + </section> + <footer> + <div class="container"> + <div class="span-24 last FooterTop"> + <div class="LocaleText"> @@ -973,50 +998,54 @@ googletag.cmd.push(function() { googletag.display('div_1cd2f642-a5e3-42dc-a38f-7 <div class="selected-language"> - <a href="#">English▼</a> + <a href="#">English ▼</a> </div> <ul class="language-list"> - <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl00_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl00$uxLocaleItem','')">English</a></li> + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl00_uxLocaleItem" class="selected" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl00$uxLocaleItem','')">English</a></li> - <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl01_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl01$uxLocaleItem','')">Deutsch</a></li> + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl01_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl01$uxLocaleItem','')">Català</a></li> - <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl02_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl02$uxLocaleItem','')">Français</a></li> + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl02_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl02$uxLocaleItem','')">Čeština</a></li> - <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl03_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl03$uxLocaleItem','')">Português</a></li> + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl03_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl03$uxLocaleItem','')">Dansk</a></li> - <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl04_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl04$uxLocaleItem','')">Čeština</a></li> + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl04_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl04$uxLocaleItem','')">Deutsch</a></li> - <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl05_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl05$uxLocaleItem','')">Dansk</a></li> + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl05_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl05$uxLocaleItem','')">Ελληνικά</a></li> - <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl06_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl06$uxLocaleItem','')">Svenska</a></li> + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl06_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl06$uxLocaleItem','')">Eesti</a></li> <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl07_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl07$uxLocaleItem','')">Español</a></li> - <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl08_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl08$uxLocaleItem','')">Eesti</a></li> + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl08_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl08$uxLocaleItem','')">Français</a></li> <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl09_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl09$uxLocaleItem','')">Italiano</a></li> - <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl10_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl10$uxLocaleItem','')">Ελληνικά</a></li> + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl10_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl10$uxLocaleItem','')">日本語</a></li> - <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl11_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl11$uxLocaleItem','')">Latviešu</a></li> + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl11_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl11$uxLocaleItem','')">한국어</a></li> - <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl12_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl12$uxLocaleItem','')">Nederlands</a></li> + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl12_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl12$uxLocaleItem','')">Latviešu</a></li> - <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl13_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl13$uxLocaleItem','')">Català</a></li> + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl13_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl13$uxLocaleItem','')">Magyar</a></li> - <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl14_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl14$uxLocaleItem','')">Polski</a></li> + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl14_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl14$uxLocaleItem','')">Nederlands</a></li> <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl15_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl15$uxLocaleItem','')">Norsk, Bokmål</a></li> - <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl16_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl16$uxLocaleItem','')">한국어</a></li> + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl16_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl16$uxLocaleItem','')">Polski</a></li> - <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl17_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl17$uxLocaleItem','')">Magyar</a></li> + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl17_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl17$uxLocaleItem','')">Português</a></li> <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl18_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl18$uxLocaleItem','')">Română</a></li> - <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl19_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl19$uxLocaleItem','')">日本語</a></li> + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl19_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl19$uxLocaleItem','')">Русский</a></li> + + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl20_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl20$uxLocaleItem','')">Suomi</a></li> + + <li><a id="ctl00_uxLocaleList_uxLocaleList_ctl21_uxLocaleItem" href="javascript:__doPostBack('ctl00$uxLocaleList$uxLocaleList$ctl21$uxLocaleItem','')">Svenska</a></li> </ul> @@ -1043,123 +1072,127 @@ googletag.cmd.push(function() { googletag.display('div_1cd2f642-a5e3-42dc-a38f-7 }); }); </script> + </div> </div> - </div> - <div class="container column-container"> - <div class="column"> - <p class="FooterLinksHeader"> - <strong> + <div class="container column-container"> + <div class="column"> + <p class="FooterLinksHeader"> + <strong> About</strong> </p> - <ul class="FooterLinks"> - <li> - <a id="ctl00_hlFooterGlossary" title="Glossary of Terms" href="../about/glossary.aspx">Glossary of Terms</a></li> - <li> - <a id="ctl00_hlFooterBrochures" title="Brochures" href="../tools/#Guide">Brochures</a></li> - <li> - <a id="ctl00_hlFooterAbout" title="About Groundspeak" href="../about/groundspeak.aspx">About Groundspeak</a></li> - <li> - <a id="ctl00_hlFooterHistory" title="History" href="../about/history.aspx">History</a></li> - </ul> - </div> - <div class="column"> - <p class="FooterLinksHeader"> - <strong> + <ul class="FooterLinks"> + <li> + <a id="ctl00_hlFooterGlossary" title="Glossary of Terms" href="../about/glossary.aspx">Glossary of Terms</a></li> + <li> + <a id="ctl00_hlFooterBrochures" title="Brochures" href="../tools/#Guide">Brochures</a></li> + <li> + <a id="ctl00_hlFooterAbout" title="About Groundspeak" href="../about/groundspeak.aspx">About Groundspeak</a></li> + <li> + <a id="ctl00_hlFooterHistory" title="History" href="../about/history.aspx">History</a></li> + </ul> + </div> + <div class="column"> + <p class="FooterLinksHeader"> + <strong> Press</strong> </p> - <ul class="FooterLinks"> - <li> - <a id="ctl00_hlFooterNews" title="News Articles" href="../press/">News Articles</a></li> - <li> - <a id="ctl00_hlFooterMediaFAQs" title="Media FAQs" rel="document" href="../articles/Brochures/footer/FAQ_Media.pdf">Media FAQs</a></li> - <li> - <a id="ctl00_hlFooterMediaInquiries" title="Media Inquiries" rel="external" href="http://support.groundspeak.com/index.php?pg=request&xCategory=11">Media Inquiries</a></li> - <li> - <a id="ctl00_hlFooterLogo" accesskey="l" title="Logo Usage Guidelines" href="../about/logousage.aspx">Logo Usage Guidelines</a></li> - </ul> - </div> - <div class="column"> - <p class="FooterLinksHeader"> - <strong> + <ul class="FooterLinks"> + <li> + <a id="ctl00_hlFooterNews" title="News Articles" href="../press/">News Articles</a></li> + <li> + <a id="ctl00_hlFooterMediaFAQs" title="Media FAQs" rel="document" href="../press/faq.aspx">Media FAQs</a></li> + <li> + <a id="ctl00_hlFooterMediaInquiries" title="Media Inquiries" rel="external" href="http://support.groundspeak.com/index.php?pg=request&xCategory=11">Media Inquiries</a></li> + <li> + <a id="ctl00_hlFooterLogo" accesskey="l" title="Logo Usage Guidelines" href="../about/logousage.aspx">Logo Usage Guidelines</a></li> + </ul> + </div> + <div class="column"> + <p class="FooterLinksHeader"> + <strong> Questions & Suggestions</strong> </p> - <ul class="FooterLinks"> - <li> - <a id="ctl00_hlFooterHelpCenterLink" title="Help Center" rel="external" href="http://support.groundspeak.com/index.php">Help Center</a></li> - <li> - <a id="ctl00_hlFooterDiscussionForums" accesskey="f" title="Discussion Forums" href="../forums/">Discussion Forums</a></li> - <li> - <a id="ctl00_hlFooterParksPoliceLink" title="Land Management and Law Enforcement" href="../parksandpolice/">Land Management and Law Enforcement</a></li> - <li> - <a id="ctl00_hlFooterContactUs" title="Contact Us" href="../contact/">Contact Us</a></li> - </ul> - </div> - <div class="column"> - <p class="FooterLinksHeader"> - <strong> + <ul class="FooterLinks"> + <li> + <a id="ctl00_hlFooterHelpCenterLink" title="Help Center" rel="external" href="http://support.groundspeak.com/index.php">Help Center</a></li> + <li> + <a id="ctl00_hlFooterDiscussionForums" accesskey="f" title="Discussion Forums" href="../forums/">Discussion Forums</a></li> + <li> + <a id="ctl00_hlFooterParksPoliceLink" title="Land Management and Law Enforcement" href="../parksandpolice/">Land Management and Law Enforcement</a></li> + <li> + <a id="ctl00_hlFooterContactUs" title="Contact Us" href="http://support.groundspeak.com/index.php?pg=request">Contact Us</a></li> + </ul> + </div> + <div class="column"> + <p class="FooterLinksHeader"> + <strong> Resources</strong> </p> - <ul class="FooterLinks"> - <li> - <a id="ctl00_hlFooterTools" accesskey="o" title="Tools and Downloads" href="../tools/">Tools and Downloads</a></li> - <li> - <a id="ctl00_hlFooterAPIProgram" title="API Program" href="../live/">API Program</a></li> - <li> - <a id="ctl00_hlFooterBenchmarks" title="Find a Benchmark" href="../mark/">Find a Benchmark</a></li> - </ul> - </div> - <div class="column"> - <p class="FooterLinksHeader"> - <strong> + <ul class="FooterLinks"> + <li> + <a id="ctl00_hlFooterTools" accesskey="o" title="Tools and Downloads" href="../tools/">Tools and Downloads</a></li> + <li> + <a id="ctl00_hlFooterAPIProgram" title="API Program" href="../live/">API Program</a></li> + <li> + <a id="ctl00_hlFooterBenchmarks" title="Find a Benchmark" href="../mark/">Find a Benchmark</a></li> + <li> + <a id="ctl00_hlFooterJobs" title="Jobs" href="http://www.groundspeak.com/jobs.aspx">Jobs</a> + </li> + </ul> + </div> + <div class="column"> + <p class="FooterLinksHeader"> + <strong> Follow Us</strong> - </p> - <ul class="FooterLinks FollowUsLinks"> - <li> - <a id="ctl00_hlFacebook" title="Facebook" href="http://www.facebook.com/geocaching"></a></li> - <li> - <a id="ctl00_hlYouTube" title="YouTube" href="http://www.youtube.com/user/GoGeocaching"></a></li> - <li> - <a id="ctl00_hlInstagram" title="Instagram" href="http://instagram.com/gogeocaching/"></a></li> - <li> - <a id="ctl00_hlTwitter" title="Twitter" href="http://twitter.com/GoGeocaching"></a></li> - </ul> + </p> + <ul class="FooterLinks FollowUsLinks"> + <li> + <a id="ctl00_hlFacebook" title="Facebook" href="http://www.facebook.com/geocaching"></a></li> + <li> + <a id="ctl00_hlYouTube" title="YouTube" href="http://www.youtube.com/user/GoGeocaching"></a></li> + <li> + <a id="ctl00_hlInstagram" title="Instagram" href="http://instagram.com/geocaching/"></a></li> + <li> + <a id="ctl00_hlTwitter" title="Twitter" href="http://twitter.com/GoGeocaching"></a></li> + </ul> + </div> </div> - </div> - <div class="FooterBottom"> - <div class="container"> - <p> - Copyright - © 2000-2013 - <a href="http://www.groundspeak.com/" title="Groundspeak, Inc." accesskey="g">Groundspeak, Inc.</a> - All Rights Reserved. - <a id="ctl00_hlFooterTerms" accesskey="u" title="Groundspeak Terms of Use" href="../about/termsofuse.aspx">Groundspeak Terms of Use</a> - | + <div class="FooterBottom"> + <div class="container"> + <p> + Copyright + © 2000-2015 + <a href="http://www.groundspeak.com/" title="Groundspeak, Inc." accesskey="g">Groundspeak, Inc.</a> + All Rights Reserved. + <a id="ctl00_hlFooterTerms" accesskey="u" title="Groundspeak Terms of Use" href="../about/termsofuse.aspx">Groundspeak Terms of Use</a> + | <a id="ctl00_hlFooterPrivacy" accesskey="x" title="Privacy Policy" href="../about/privacypolicy.aspx">Privacy Policy</a> - </p> + </p> + </div> </div> + </footer> + <div class="SkipLinks"> + <a id="ctl00_hlSkipLinksTop" accesskey="t" title="Return to the Top of the Page" href="#Top">Return to the Top of the Page</a> </div> - </footer> - <div class="SkipLinks"> - <a id="ctl00_hlSkipLinksTop" accesskey="t" title="Return to the Top of the Page" href="#Top">Return to the Top of the Page</a> - </div> <script type="text/javascript"> //<![CDATA[ $(function() { _gaq.push(['_trackEvent', 'Geocaching', 'CacheDetailsMemberType', 'Premium', null, true]); });var isLoggedIn = true; -var userDefinedCoords = {"status":"fail","data":{"isUserDefined":false,"oldLatLngDisplay":"N 46° 04.828' E 014° 30.000'"}}; mapLatLng = {"lat":46.08047,"lng":14.5,"type":2,"name":"Zaraščen Tir"}; var ccConversions = [{"t":"Decimal","k":"DD","d":"WGS84","v":"46.080467, 014.500000"},{"t":"DDD MM SS.SSS","k":"DMS","d":"WGS84","v":"N 46° 04' 49.681\" E 014° 30' 00.000\""},{"t":"UTM","k":"UTM","d":"WGS84","v":"33T E 461340 N 5103109"}]; -var dh=false;userInfo = {ID: 4793174}; -userToken = '4OB3GFHLRR3CGMZFUAPD6CWPE24ZHP6Q7KCBEDNYY5CUNWNQIA32GZPVPFMH6IGAEYKVNNSFLMQ7BBGFDKQBT3BZGZBYKVQ4H5BWKD6MVBKYBTM627XPOUSUP6U4JF7NKKEOYJ4A6LEG2T5B6MG5AMMFOS27KNE4U2BAU4PZHXTIASIYWFYVKCUOKL22KFA6QAHSYC5LPKUTADH2JZH6BXMTYDNGMZXDBT3OPXUHQASIIU4WRAEA'; +var dh=false;userInfo = {ID: 3514898}; +userToken = 'ID5R2NBJDBPQZQ6CMWZJOWRMNQSY5B4L3KRUELYBZSPXO67NW7RE2RD7BSKLMJIFBY4OBMTVVFX7VW4362L6YY5Y4K6AJSGEER65DT6LMQV5EYAVH2FTRYD66AZEZP3YYMEO2UGNX265RLOI2UN7TABIOZDZLNKU6WEK3YCNDPSY24JAXVNDEZTMNO7537J65LOZ4ELPHPS7ATL6URJFRBXWMFKOBCTUHQ7VCAV5AKNTNXQ26UDA'; includeAvatars = true; var lat=46.080467, lng=14.5, guid='51e40dec-6272-4dad-934b-e175daaac265'; -initalLogs = {"status":"success", "data": [{"LogID":340527924,"CacheID":3220672,"LogGuid":"f45d0816-d024-46c2-9908-94a492c725fe","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Found it:)","Created":"2013-08-16","Visited":"2013-08-16","UserName":"K I N","MembershipLevel":1,"AccountID":7771619,"AccountGuid":"4c169228-9960-4775-a6b4-bdcd352f98db","Email":"","AvatarImage":"","GeocacheFindCount":18,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":341444389,"CacheID":3220672,"LogGuid":"8ad83648-c096-422a-a4d5-93c1762c7a80","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"TFTC!","Created":"2013-08-18","Visited":"2013-08-15","UserName":"yaochacha","MembershipLevel":1,"AccountID":7859000,"AccountGuid":"30794f4b-c91a-455e-8f1a-1fa1afd0a912","Email":"","AvatarImage":"","GeocacheFindCount":9,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":341054839,"CacheID":3220672,"LogGuid":"dd2aa3b3-0364-4c67-908d-5eb882424b9e","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"TFTC!","Created":"2013-08-17","Visited":"2013-08-15","UserName":"Remih87","MembershipLevel":1,"AccountID":4638242,"AccountGuid":"868ac153-1a8f-44b9-bcad-b225a0567032","Email":"","AvatarImage":"5eca0e89-b8b4-4d50-aacc-84e7cfad79a2.jpg","GeocacheFindCount":136,"GeocacheHideCount":2,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":340199614,"CacheID":3220672,"LogGuid":"f9da4abb-b21b-4f68-bafd-7599efd9821a","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":":)<br />#111","Created":"2013-08-15","Visited":"2013-08-15","UserName":"Hobbit (SLO)","MembershipLevel":1,"AccountID":7226088,"AccountGuid":"3a258272-3e3b-4db1-a9a2-1e04e341c2a5","Email":"","AvatarImage":"","GeocacheFindCount":113,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":340193177,"CacheID":3220672,"LogGuid":"2ae33e97-3a3a-425d-b7d8-3b621ea922d4","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Found it with K I N! ;)","Created":"2013-08-15","Visited":"2013-08-15","UserName":"Lilawal","MembershipLevel":1,"AccountID":7791384,"AccountGuid":"1fa5b881-0dbe-4a49-af65-959f899f4597","Email":"","AvatarImage":"4406d56f-9f00-4299-832c-d19411b2712d.jpg","GeocacheFindCount":12,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":335241565,"CacheID":3220672,"LogGuid":"2c6b924b-becd-4275-8e3d-509178f5f87e","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"TFTC <img src=\"/images/icons/icon_smile.gif\" border=\"0\" align=\"middle\" />","Created":"2013-08-02","Visited":"2013-08-02","UserName":"MrsJones*","MembershipLevel":1,"AccountID":7724546,"AccountGuid":"f4ef80f3-400f-408a-998a-dc17b846baed","Email":"","AvatarImage":"726bbd62-20ca-4fff-b4c3-62e9703ec306.jpg","GeocacheFindCount":11,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":331032294,"CacheID":3220672,"LogGuid":"59058647-41d3-4490-894f-eee93dfca51c","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Hzc! ","Created":"2013-07-18","Visited":"2013-07-18","UserName":"Pejntboks","MembershipLevel":1,"AccountID":7301367,"AccountGuid":"a0be7068-23e0-4cd0-8c17-72e9e781e802","Email":"","AvatarImage":"e91201a4-7e41-4cdd-bbec-ec53011538e6.gif","GeocacheFindCount":28,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":329487987,"CacheID":3220672,"LogGuid":"c2c9b364-2a08-47d6-9d1a-f0b716e054db","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Owner Maintenance","LogTypeImage":"46.png","LogText":"a while ago someone replaced the logbook and i promise i'll go and check on it soon<br /><br />Happy hunting","Created":"2013-07-13","Visited":"2013-07-13","UserName":"David & Ajda","MembershipLevel":3,"AccountID":5991956,"AccountGuid":"7b1ed479-e378-4cfd-a5b4-3bd47bab619f","Email":"","AvatarImage":"0bafa82b-6510-4903-bb6a-68dd455eb971.jpg","GeocacheFindCount":202,"GeocacheHideCount":15,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Premium Member","GroupImageUrl":"/images/icons/prem_user.gif"},"Images":[]},{"LogID":328565685,"CacheID":3220672,"LogGuid":"029240c4-8daf-470e-871d-f46dfee2684c","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Hzz! <img src=\"/images/icons/icon_smile.gif\" border=\"0\" align=\"middle\" />","Created":"2013-07-09","Visited":"2013-07-09","UserName":"bremza","MembershipLevel":1,"AccountID":5283193,"AccountGuid":"7295459e-5262-43fa-8d4f-c8dd8ebf262e","Email":"","AvatarImage":"","GeocacheFindCount":162,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":328563478,"CacheID":3220672,"LogGuid":"9308486a-a2bf-48a6-820e-4d60f1ee21f0","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Hvala za zaklad! <img src=\"/images/icons/icon_smile.gif\" border=\"0\" align=\"middle\" />","Created":"2013-07-09","Visited":"2013-07-09","UserName":"hropka","MembershipLevel":1,"AccountID":5307782,"AccountGuid":"0f5ad667-3900-440f-b5f3-8f0e76fc84a8","Email":"","AvatarImage":"","GeocacheFindCount":161,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":326820900,"CacheID":3220672,"LogGuid":"48b93040-aa09-4dce-a414-6c31e68a82de","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"TFTC","Created":"2013-07-03","Visited":"2013-07-03","UserName":"Svizec&Lisja","MembershipLevel":3,"AccountID":5666226,"AccountGuid":"a5c8fc25-6030-4313-942b-236a7cb4a1a1","Email":"","AvatarImage":"","GeocacheFindCount":203,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Premium Member","GroupImageUrl":"/images/icons/prem_user.gif"},"Images":[]},{"LogID":325302896,"CacheID":3220672,"LogGuid":"2b8fc04d-2b49-414d-9f0c-ecae36a88cee","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Notri je veliko vode(za silo popravljeno) in malo mrtvih žužkov, drugače pa zelo lepa lokacija in skrivališče. HZZ! :D","Created":"2013-06-27","Visited":"2013-06-27","UserName":"nosonje","MembershipLevel":1,"AccountID":5283276,"AccountGuid":"4a4fd997-0531-437c-baee-bb11272e5a12","Email":"","AvatarImage":"96cd321d-ab38-4bc1-bd8f-350d519c3ee5.jpg","GeocacheFindCount":120,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":323651478,"CacheID":3220672,"LogGuid":"98f9c80f-ecbf-437d-972c-9d82a71b950d","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Našla! Notri je voda in neki žužki! :)","Created":"2013-06-20","Visited":"2013-06-20","UserName":"hanaaa :)","MembershipLevel":1,"AccountID":7499579,"AccountGuid":"9a187938-a916-4759-9f9c-7bc6f1978468","Email":"","AvatarImage":"","GeocacheFindCount":15,"GeocacheHideCount":1,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":320316919,"CacheID":3220672,"LogGuid":"b66fcfe6-9c54-479f-9323-334905eeb87e","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Hitra najdba. :)<br /><br />Škatlica je res vlažna, vendar kot je že nekdo od predhodnikov povedal, logbook je varen :)<br /><br />HZZ","Created":"2013-06-07","Visited":"2013-06-05","UserName":"aljoG","MembershipLevel":1,"AccountID":6057040,"AccountGuid":"4b400d11-aea9-4305-95d4-928d96574d28","Email":"","AvatarImage":"","GeocacheFindCount":255,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":319969838,"CacheID":3220672,"LogGuid":"ae8e3d92-0407-421b-b1cf-ee798ea9ff42","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"V posodici je bila voda, a logbook je dobro zavarovan.<br />HZZ!","Created":"2013-06-05","Visited":"2013-06-05","UserName":"mmeglic","MembershipLevel":1,"AccountID":3716271,"AccountGuid":"86f8f1c6-fc72-495b-a32e-a4066ace063e","Email":"","AvatarImage":"60be750c-9ec4-439e-92bc-f77db5704ee3.bmp","GeocacheFindCount":459,"GeocacheHideCount":15,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":319749488,"CacheID":3220672,"LogGuid":"01cbe1c2-9061-408b-ad89-8ecc9aea05c3","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Sprehod po zelezniski tirih nas je pripeljal do novega zakladka.","Created":"2013-06-04","Visited":"2013-06-04","UserName":"Pustolovka","MembershipLevel":1,"AccountID":6986260,"AccountGuid":"a77cd22e-6dff-4240-bd7c-934bacc93625","Email":"","AvatarImage":"229d7f1c-4fc8-4594-b945-7daf04c28e8e.png","GeocacheFindCount":187,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":318121707,"CacheID":3220672,"LogGuid":"995ef24e-45c2-4786-b256-a236094138d8","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Could not log, because the box is totally wet inside. I attached a picture of the box as proof. Took out TB and saved it from drowning in the water. The box needs maintenance!<br /><br />This entry was edited by Larsus cisalpinus on Wednesday, 29 May 2013 at 21:33:36 UTC.","Created":"2013-05-29","Visited":"2013-05-29","UserName":"Larsus cisalpinus","MembershipLevel":3,"AccountID":7318481,"AccountGuid":"d3ced821-5969-4bca-89c6-2d74466dc6c8","Email":"","AvatarImage":"7613ed0e-6a52-4f54-9d90-be84be4d73f6.png","GeocacheFindCount":223,"GeocacheHideCount":11,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Premium Member","GroupImageUrl":"/images/icons/prem_user.gif"},"Images":[{"ImageID":17960725,"ImageGuid":"1c3db698-e65d-4fa0-93d7-b72ccd9fda41","Name":"Zarascen tir 001","Descr":"","FileName":"1c3db698-e65d-4fa0-93d7-b72ccd9fda41.jpg","Created":"2013-05-29","LogID":318121707,"CacheID":3220672,"ImageUrl":null}]},{"LogID":317739701,"CacheID":3220672,"LogGuid":"8f07484c-ac82-4878-8716-bf182f584ad0","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Hitro najdeno. In: TB<br />HZZ","Created":"2013-05-27","Visited":"2013-05-27","UserName":"hermani","MembershipLevel":3,"AccountID":1594587,"AccountGuid":"399778e5-6821-4c27-abe7-ca2df6a4c585","Email":"","AvatarImage":"","GeocacheFindCount":533,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Premium Member","GroupImageUrl":"/images/icons/prem_user.gif"},"Images":[]},{"LogID":317728651,"CacheID":3220672,"LogGuid":"f3910ea4-5320-4d95-9d85-d4257828354f","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"V škatli je dosti vode, travel boy se utaplja, logbook pa je zaenkrat še suh.","Created":"2013-05-27","Visited":"2013-05-27","UserName":"geoscout_slo","MembershipLevel":3,"AccountID":6305970,"AccountGuid":"3919036d-5159-4227-9d81-0dd2188834b6","Email":"","AvatarImage":"4dc41fe7-8c11-4dad-9ef5-32f278195aaa.jpg","GeocacheFindCount":1116,"GeocacheHideCount":16,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Premium Member","GroupImageUrl":"/images/icons/prem_user.gif"},"Images":[]},{"LogID":317262995,"CacheID":3220672,"LogGuid":"06fadc9e-f489-4122-ba51-12727e8cd24f","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"During our trip by campingcar to Kroatië we found this cache<br /><br />TFTC Team Wans from the Netherlands","Created":"2013-05-26","Visited":"2013-05-26","UserName":"WANS","MembershipLevel":3,"AccountID":1982355,"AccountGuid":"69adeb9f-a5dd-4005-86d7-609250a3d4dc","Email":"","AvatarImage":"d90fbf86-39df-4376-8988-98c8188b23b4.jpg","GeocacheFindCount":2321,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Premium Member","GroupImageUrl":"/images/icons/prem_user.gif"},"Images":[]},{"LogID":316856553,"CacheID":3220672,"LogGuid":"5d2e314f-d6ce-4151-8387-688104eaa35c","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Didn't find it","LogTypeImage":"3.png","LogText":"Couldnt find it anywhere.... :(","Created":"2013-05-24","Visited":"2013-05-24","UserName":"idastante","MembershipLevel":1,"AccountID":5251559,"AccountGuid":"742b75c4-eb09-4dca-86f9-5e2b19fabbe5","Email":"","AvatarImage":"","GeocacheFindCount":75,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":316395131,"CacheID":3220672,"LogGuid":"6a847259-ba30-4817-a905-574efa416f16","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"škatlica je bila spet polna vode, logbook je suh.<br />HZZ!","Created":"2013-05-22","Visited":"2013-05-22","UserName":"Buhteljni","MembershipLevel":1,"AccountID":4407541,"AccountGuid":"dc0b3085-483c-48a9-a988-7846302fa721","Email":"","AvatarImage":"","GeocacheFindCount":745,"GeocacheHideCount":2,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":314411680,"CacheID":3220672,"LogGuid":"212d0c53-0a27-47ae-8c17-fdf51255992c","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Ni kazalo drugega kot, da si izmislim en dober izgovor o nuji sprehoda po progi, če bi bil varnostnik kaj siten. Kot 90 predhodnikov je tudi meni ratalo brez problema. HZZ","Created":"2013-05-15","Visited":"2013-05-15","UserName":"marko0037","MembershipLevel":1,"AccountID":4062170,"AccountGuid":"df3a3244-64a0-4a56-97a9-e561178298ce","Email":"","AvatarImage":"e7b37399-a2d8-4350-809c-b2549409bf06.jpg","GeocacheFindCount":948,"GeocacheHideCount":66,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":315271823,"CacheID":3220672,"LogGuid":"c95d4dd3-4cca-404a-82bf-7957780aa9bc","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"The cache was full of water, but the loogbook was dry. We had no pencil to mark our visit in the logbook. For the evidence we made a photo of it. TFTC!","Created":"2013-05-19","Visited":"2013-05-12","UserName":"crimaldi","MembershipLevel":1,"AccountID":955257,"AccountGuid":"056f0a1f-9d75-4eac-8321-1da98dd07271","Email":"","AvatarImage":"f0bc05c9-5f68-4937-b8b7-8fceef1763ff.jpg","GeocacheFindCount":484,"GeocacheHideCount":10,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[{"ImageID":17818960,"ImageGuid":"43297a3c-de4c-4b70-a228-cdfe5b767094","Name":"Cache","Descr":"","FileName":"43297a3c-de4c-4b70-a228-cdfe5b767094.jpg","Created":"2013-05-19","LogID":315271823,"CacheID":3220672,"ImageUrl":null}]},{"LogID":313719869,"CacheID":3220672,"LogGuid":"7dc39404-2407-44a0-8298-29e03dfcfd9a","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"HZZ","Created":"2013-05-12","Visited":"2013-05-12","UserName":"nixii","MembershipLevel":1,"AccountID":5786956,"AccountGuid":"d4964534-db4d-4d8a-8ec2-45e36ba6d3aa","Email":"","AvatarImage":"","GeocacheFindCount":77,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]}], "pageInfo": { "idx":1, "size": 25, "totalRows": 119, "rows": 119 } }; -var gaToken = 'UA-2020240-1';//]]> +initalLogs = {"status":"success", "data": [{"LogID":465904435,"CacheID":3220672,"LogGuid":"fde8575b-4609-4057-a9fa-21b8c2ea6273","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Temporarily Disable Listing","LogTypeImage":"22.png","LogText":"Please do the maintanance on your cache and tell us if it is still there. I disable this cache for a month. <br /><br />After no response and another N/A log I have to archive it. Thanks.","Created":"2014-12-13","Visited":"2014-12-13","UserName":"Geolens Reviewer","MembershipLevel":3,"AccountID":778521,"AccountGuid":"b75a0c96-985c-416a-b472-2b37da5cc1ef","Email":"","AvatarImage":"7ada6f14-cae0-49d0-8888-1888bd8ec206.jpg","GeocacheFindCount":2,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Reviewer","GroupImageUrl":"/images/icon_admin.gif"},"Images":[]},{"LogID":465796393,"CacheID":3220672,"LogGuid":"9e8d2663-bd48-4344-88c1-9e144d801196","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Needs Archived","LogTypeImage":"7.png","LogText":"Cache has obviously disappeared a long time ago and cache owners are no longer active, do not respond to maintenance and NA requests at some of their other caches. It's time to let this one go...","Created":"2014-12-13","Visited":"2014-12-13","UserName":"icabrian","MembershipLevel":3,"AccountID":4405840,"AccountGuid":"a1b369e8-1f63-48d2-a45c-d315e1d207b2","Email":"","AvatarImage":"0f993e55-37d6-47fd-b59b-a90b9c28d994.jpg","GeocacheFindCount":2445,"GeocacheHideCount":25,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Premium Member","GroupImageUrl":"/images/icons/prem_user.gif"},"Images":[]},{"LogID":461333293,"CacheID":3220672,"LogGuid":"6b10f81b-cd78-4467-a4b4-d4dc62d93237","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Didn't find it","LogTypeImage":"3.png","LogText":"Iskali sva ga več kot pol ure, brez uspeha. Bi pa lahko lastnik cacha preveril, če je sploh dostopen, glede na to, da so pred kratkim obrezovali drevesa in je na desni strani tirov ogromno težjih vej. LP","Created":"2014-11-15","Visited":"2014-11-15","UserName":"tabaluga1","MembershipLevel":1,"AccountID":10264789,"AccountGuid":"7ce3b363-5a9e-4875-b82f-fabc024fb92e","Email":"","AvatarImage":"","GeocacheFindCount":13,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":459732939,"CacheID":3220672,"LogGuid":"2a98a44b-ed9e-4ab2-a9de-e316ccab26d7","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Didn't find it","LogTypeImage":"3.png","LogText":"Kar nekaj časa iskal... ampak ga ni nikjer...","Created":"2014-11-08","Visited":"2014-11-08","UserName":"ŽKT","MembershipLevel":1,"AccountID":5797133,"AccountGuid":"d05bccf8-c730-4319-a97e-c7eff69a9bd5","Email":"","AvatarImage":"","GeocacheFindCount":740,"GeocacheHideCount":25,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":453019251,"CacheID":3220672,"LogGuid":"1876bacf-e6df-47b0-9357-e796cf0b3541","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Didn't find it","LogTypeImage":"3.png","LogText":"Iskali v temi..a vendar po pol ure obupali :(","Created":"2014-10-12","Visited":"2014-10-12","UserName":"KoalaSpela&JS","MembershipLevel":1,"AccountID":9349853,"AccountGuid":"7518eb15-a3d0-4619-aff0-2708e27f1056","Email":"","AvatarImage":"73901d9d-3a53-42d1-832b-712a8ba2bb8a.jpg","GeocacheFindCount":91,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":452817935,"CacheID":3220672,"LogGuid":"2db262aa-1002-4e94-9821-4934a64e8cfe","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Didn't find it","LogTypeImage":"3.png","LogText":"Nismo nasl. Pa smo pregledali vse polivinile in iskali vsaj pol ure. Ima kdo se kaksen namig?","Created":"2014-10-12","Visited":"2014-10-12","UserName":"jovoa","MembershipLevel":1,"AccountID":11022584,"AccountGuid":"9646cf3c-e2d2-4177-935a-8a3e2fcf9398","Email":"","AvatarImage":"","GeocacheFindCount":4,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":447814401,"CacheID":3220672,"LogGuid":"5f950efa-6e6e-4e77-b927-0b6bb324a48b","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Didn't find it","LogTypeImage":"3.png","LogText":"Napacen kesek sem ponesreci oznacila kot najdenega :( ko ga najdem ga bom pa dala kot najdenega :)","Created":"2014-09-23","Visited":"2014-09-24","UserName":"manca23","MembershipLevel":1,"AccountID":10465132,"AccountGuid":"ac9d054c-5d63-49ed-b4e0-fdabced68c9b","Email":"","AvatarImage":"f69e59d5-0709-4bcb-b65d-4efc63fb138d.jpg","GeocacheFindCount":79,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":447812068,"CacheID":3220672,"LogGuid":"1e136711-479d-493e-ae57-58aab66d8827","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Hitro :) le namig malce napacen :S tftc :)","Created":"2014-09-23","Visited":"2014-09-24","UserName":"manca23","MembershipLevel":1,"AccountID":10465132,"AccountGuid":"ac9d054c-5d63-49ed-b4e0-fdabced68c9b","Email":"","AvatarImage":"f69e59d5-0709-4bcb-b65d-4efc63fb138d.jpg","GeocacheFindCount":79,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":447740085,"CacheID":3220672,"LogGuid":"a1c195f1-1893-4977-b7b4-803e28ba2c19","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Tftc. Po petem poizkusu smo ga končno našli. Gps kaže dejansko 4m stran. Pomagala slikca in odvržen polivinil . ","Created":"2014-09-23","Visited":"2014-09-23","UserName":"morfej","MembershipLevel":1,"AccountID":9159065,"AccountGuid":"74daad71-4bc2-458e-a92c-e0830f46544a","Email":"","AvatarImage":"8febeb81-6dc4-4621-be01-e70e2674ed06.jpg","GeocacheFindCount":359,"GeocacheHideCount":4,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":447714784,"CacheID":3220672,"LogGuid":"77105f59-b874-4087-b490-fc84d9eedc8e","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"fak zafrknn do konca!!! :D ampak nice!!","Created":"2014-09-23","Visited":"2014-09-23","UserName":"blackwings1","MembershipLevel":1,"AccountID":10582292,"AccountGuid":"43046d5d-b9d5-4b51-95eb-2e3df1969594","Email":"","AvatarImage":"c323fa2d-e6ec-4628-9964-eb3f2a281fbe.jpeg","GeocacheFindCount":139,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":465449211,"CacheID":3220672,"LogGuid":"f3661d40-fb39-4f44-86aa-aae9c367ceef","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"That’s one more find for me! Thanks so much for hiding this geocache.","Created":"2014-12-09","Visited":"2014-09-20","UserName":"kjerkloe","MembershipLevel":1,"AccountID":10158843,"AccountGuid":"6b9ba8da-6090-4ff2-ae4f-2c85a0ede0fe","Email":"","AvatarImage":"","GeocacheFindCount":77,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":446668838,"CacheID":3220672,"LogGuid":"fc9161ae-9818-47f0-8d4e-f155b90884e9","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Ponoči je malo težje ga najti, pa vendarle sva ga opazili. HZZ! :) <br />Logbook je premočen in potreben zamenjave, saj prostora ni več-sva se podpisali na zadnjo stran.","Created":"2014-09-20","Visited":"2014-09-19","UserName":"zelenko&ana","MembershipLevel":3,"AccountID":9650533,"AccountGuid":"17fb56c6-5196-44e4-9867-9002c13b8a05","Email":"","AvatarImage":"cba51b72-6696-4f5a-9cdb-2382c18e2895.jpg","GeocacheFindCount":407,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Premium Member","GroupImageUrl":"/images/icons/prem_user.gif"},"Images":[]},{"LogID":445228772,"CacheID":3220672,"LogGuid":"d8bb2888-36d9-4a8e-abe1-a280c7881fbf","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Po kar dolgem iskanju smo ga našli. Frnikulo zamenjali z avtomobilčkom.<br />Hzz","Created":"2014-09-14","Visited":"2014-09-14","UserName":"ZipBlocx","MembershipLevel":1,"AccountID":9321945,"AccountGuid":"ec68a9b1-2a12-42c6-94a7-8fb15c60b74c","Email":"","AvatarImage":"","GeocacheFindCount":2,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":443418219,"CacheID":3220672,"LogGuid":"af60d92b-1739-4899-88f9-c809ef669c09","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Večerni potep s kolesom.<br /><br />HZZ","Created":"2014-09-07","Visited":"2014-09-07","UserName":"bbajs","MembershipLevel":3,"AccountID":10525211,"AccountGuid":"f0dad178-d784-4f88-8acc-bc9c572f2b38","Email":"","AvatarImage":"f1214c85-8e71-401a-aa27-6854985455ab.jpg","GeocacheFindCount":383,"GeocacheHideCount":6,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Premium Member","GroupImageUrl":"/images/icons/prem_user.gif"},"Images":[{"ImageID":24389373,"ImageGuid":"03662b37-6376-461b-99f7-531b41ee3354","Name":"","Descr":"","FileName":"03662b37-6376-461b-99f7-531b41ee3354.jpg","Created":"2014-09-07","LogID":443418219,"CacheID":3220672,"ImageUrl":null}]},{"LogID":441923337,"CacheID":3220672,"LogGuid":"f33fcfa4-dbd6-4fc9-9ff1-65f2459aabca","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Zanimiv zaklad, pokrov škatlice zlomljen:(<br />HZZ","Created":"2014-09-02","Visited":"2014-09-02","UserName":"rozica","MembershipLevel":3,"AccountID":10546480,"AccountGuid":"64333e98-d178-4dd9-b6e0-1b51cc4abff9","Email":"","AvatarImage":"0a336011-3981-4191-8304-d80c35152b22.jpg","GeocacheFindCount":274,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Premium Member","GroupImageUrl":"/images/icons/prem_user.gif"},"Images":[]},{"LogID":440060287,"CacheID":3220672,"LogGuid":"8ba61aec-340d-4ed3-a237-93f6ab3e6891","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Tukaj sva se s Tjanom že drugič danes srečala in šele tukaj ugotovila, da nisva tujca in da se lahko pogovarjava tudi v slovenščini. :) Očitno sva se prej narobe razumela. Združila sva moči, a to ni zadostovalo. Po klicu v sili, je bilo iskanje nekoliko bolj enostavno. <br />Opis cacha mi sicer deluje malo zmedeno. Vem, da je bil nek del teksta zaradi reklame odstranjen, a sedaj nam očitno kadar zbolimo pomaga zaraščen tir?<br />TFTC","Created":"2014-08-28","Visited":"2014-08-28","UserName":"pograd","MembershipLevel":1,"AccountID":5281767,"AccountGuid":"0ca3d5b4-8823-4a96-a5a6-36ba74b602be","Email":"","AvatarImage":"2a462ee7-9cb8-4d9b-9ff8-d26cd2f76617.jpg","GeocacheFindCount":745,"GeocacheHideCount":6,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":440027024,"CacheID":3220672,"LogGuid":"19788564-e0bb-49bc-96e8-6be1b523f9ae","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Under the brown! ","Created":"2014-08-28","Visited":"2014-08-28","UserName":"Tjan","MembershipLevel":3,"AccountID":10374351,"AccountGuid":"808f84d5-7d33-48e3-acfc-783e08c8050b","Email":"","AvatarImage":"1f4f8987-c992-4eca-bc2a-a123c67624bc.jpeg","GeocacheFindCount":156,"GeocacheHideCount":2,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Premium Member","GroupImageUrl":"/images/icons/prem_user.gif"},"Images":[]},{"LogID":439165288,"CacheID":3220672,"LogGuid":"8ad3f6b4-23ac-4726-8453-1b17a1ecdf7a","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Found together with sierius, thx for hiding.<br />TFTC<br />harrylime + Ingrid","Created":"2014-08-25","Visited":"2014-08-23","UserName":"harrylime","MembershipLevel":3,"AccountID":1195360,"AccountGuid":"630f3b79-023a-4908-b960-143eaca19f99","Email":"","AvatarImage":"53fbcfa7-8e53-49f6-bbe1-8c7d8c3b27a7.jpg","GeocacheFindCount":28490,"GeocacheHideCount":28,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Premium Member","GroupImageUrl":"/images/icons/prem_user.gif"},"Images":[]},{"LogID":438323373,"CacheID":3220672,"LogGuid":"50aadeb0-c2d2-4f25-9ec7-47ce11a932f5","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Found together with harrylime.<br />Thanks for hiding to all owners and greetings from Austria!<br />SIERIUS","Created":"2014-08-23","Visited":"2014-08-23","UserName":"SIERIUS","MembershipLevel":3,"AccountID":1609892,"AccountGuid":"36495b95-d980-49af-919d-a132fcd4726e","Email":"","AvatarImage":"c15f228a-4b20-4f8d-9a63-3c0ad9e9d3f4.jpg","GeocacheFindCount":23223,"GeocacheHideCount":28,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Premium Member","GroupImageUrl":"/images/icons/prem_user.gif"},"Images":[]},{"LogID":432414556,"CacheID":3220672,"LogGuid":"1b80b42f-761a-4e4b-a616-a2564cee3869","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Ne kup, polivinil ga je izdal. Standardni postopek, odlij vodo in vpisi. Zdaj sem škatlo obrnil na glavo.<br /><br />Not stones but PVC revealed this one.<br /><br />TFTC!","Created":"2014-08-07","Visited":"2014-08-07","UserName":"Bamzž","MembershipLevel":3,"AccountID":1631613,"AccountGuid":"cea0e22f-d997-498b-b1aa-d2a6e967bf28","Email":"","AvatarImage":"","GeocacheFindCount":1181,"GeocacheHideCount":8,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Premium Member","GroupImageUrl":"/images/icons/prem_user.gif"},"Images":[]},{"LogID":430573248,"CacheID":3220672,"LogGuid":"6aee7c80-90fa-4851-aa33-80f5f0edb7a1","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Iskal 30 m","Created":"2014-08-02","Visited":"2014-08-02","UserName":"Possessor","MembershipLevel":1,"AccountID":5457171,"AccountGuid":"04336896-c32d-464f-8be2-3c4e4f16f42d","Email":"","AvatarImage":"","GeocacheFindCount":27,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":401779621,"CacheID":3220672,"LogGuid":"21c0ce48-3d19-47b7-83dc-901fbe1e08b8","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Tudi notranji kontejner je moker :( Je pa super izvedba <br />Out: kompasek<br />In: frnikula<br /><br />HZZ, 5M (4/5)","Created":"2014-04-27","Visited":"2014-04-27","UserName":"5M-jev","MembershipLevel":1,"AccountID":9137542,"AccountGuid":"fadeae0f-e52f-45ac-af65-f073d84db2ad","Email":"","AvatarImage":"8ea519e3-163e-4d03-bfdb-47a400d66ee6.jpg","GeocacheFindCount":42,"GeocacheHideCount":0,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":400178423,"CacheID":3220672,"LogGuid":"53e6769c-6538-4d96-aa58-592c244d0ee7","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Kot moji predhodniki sem tudi jaz najprej odlil vodo, nato pa se lotil vpisa v dnevnik, ki je bil tudi precej vlažen. Priporočam menjavo posode.<br /><br />HZZ!","Created":"2014-04-22","Visited":"2014-04-22","UserName":"IztokCult","MembershipLevel":3,"AccountID":1229391,"AccountGuid":"8ef811cb-1e14-4147-ad9c-999daedc6c72","Email":"","AvatarImage":"","GeocacheFindCount":212,"GeocacheHideCount":3,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Premium Member","GroupImageUrl":"/images/icons/prem_user.gif"},"Images":[]},{"LogID":399535262,"CacheID":3220672,"LogGuid":"b0282337-123f-43e8-aebb-f555ea946b8f","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Po tem ko sem napolnil rezervoar v avtu sem se ustavil še pri tem zakladu. Kljub temu, da je daž začel močneje padati ravno, ko sem stopil iz avta, sem vzel še dežnik. Zaklad mi je uspelo hitro najti. V njem je bilo veliko vode, vendar pa je dnevnik ostal skoraj čisto suh, ker je votranja posodica dobra <img src=\"/images/icons/icon_smile.gif\" border=\"0\" align=\"middle\" /><br />HZZ","Created":"2014-04-21","Visited":"2014-04-21","UserName":"vasili1945","MembershipLevel":1,"AccountID":7808645,"AccountGuid":"69cfa81a-4743-4ac4-a27e-16bdad245ce4","Email":"","AvatarImage":"dfe29348-335e-4d78-bab7-4d8de589326a.jpg","GeocacheFindCount":1760,"GeocacheHideCount":52,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Member","GroupImageUrl":"/images/icons/reg_user.gif"},"Images":[]},{"LogID":397776628,"CacheID":3220672,"LogGuid":"11f16cf6-78c2-401a-a72e-da4ee045a35b","Latitude":null,"Longitude":null,"LatLonString":"","LogType":"Found it","LogTypeImage":"2.png","LogText":"Ko sem že skoraj obupal in nameraval oditi, sem nekaj metrov stran od koordinat, ki jih je kazal GPS le zagledal sumljiv kup kamenja. Škatlica ima zlomljen pokrovček, vendar je logbook zaradi dvojne zaščite varno na suhem :)<br />HZZ","Created":"2014-04-17","Visited":"2014-04-17","UserName":"blazko_slo","MembershipLevel":3,"AccountID":8936107,"AccountGuid":"b578134f-bbc8-4ece-8962-03a59bd22ea7","Email":"","AvatarImage":"","GeocacheFindCount":500,"GeocacheHideCount":4,"ChallengesCompleted":0,"IsEncoded":false,"creator":{"GroupTitle":"Premium Member","GroupImageUrl":"/images/icons/prem_user.gif"},"Images":[{"ImageID":21918292,"ImageGuid":"5b0b5541-ee81-44f3-8c3d-71687f7eca0e","Name":"GC3XX5J","Descr":"","FileName":"5b0b5541-ee81-44f3-8c3d-71687f7eca0e.jpg","Created":"2014-04-17","LogID":397776628,"CacheID":3220672,"ImageUrl":null}]}], "pageInfo": { "idx":1, "size": 25, "totalRows": 174, "rows": 174 } }; +var gaToken = 'UA-2020240-1';ga('create', 'UA-2020240-35', 'geocaching.com'); ga('send', 'pageview');//]]> </script> </form> - <script type="text/javascript"> + <script src="/js/jquery_plugins/tinynav.min.js"></script> + <script src="/js/matchMedia.js"></script> + <script> var browserType = { IE: !!(window.attachEvent && !window.opera), Opera: !!window.opera, @@ -1168,16 +1201,45 @@ var gaToken = 'UA-2020240-1';//]]> MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/) }; + // Modernizr only identifies browsers that support touch events; Windows Phone and webOS handle touch differently + var isMobile = (/(webOS|hpwOS|IEMobile)/).test(navigator.userAgent); + $(function () { - // Make the menu system play nice with all browsers: - $('ul.Menu li').hover(function () { - $(this).addClass('hover'); - $('ul:first', this).css('visibility', 'visible'); - }, function () { - $(this).removeClass('hover'); - $('ul:first', this).css('visibility', 'hidden'); - }); - if (!isiOS()) { + var $topMenuLinks = $('.Menu > li'); + + if (isMobile || Modernizr.touch) { + // make dropdowns work on mobile + $topMenuLinks.children('a').each(function () { + $(this).on('touchstart click', function (e) { + e.stopPropagation(); + + var $this = $(this), + $parent = $(this).parent(); + + $topMenuLinks.removeClass('hover'); + + if (!$parent.hasClass('hover') && $this.hasClass('Dropdown')) { + e.preventDefault(); + $parent.addClass('hover'); + } + + $(document).on('click', function (e) { + if (!$(e.target).parents().hasClass(".Menu")) { + $topMenuLinks.removeClass("hover"); + } + }); + }); + }); + + // Convert language dropdown to native select + $("ul.language-list .selected").parent().addClass("selected"); + $(".selected-language a, ul.language-list").hide(); + $(".language-list").tinyNav({ + active: 'selected' + }); + } + + if (window.matchMedia("(min-width: 1000px)").matches) { // Constructing a Twitter-esque Login: $(".SignInLink").click(function (e) { e.preventDefault(); @@ -1199,14 +1261,6 @@ var gaToken = 'UA-2020240-1';//]]> $("#SignInWidget label").removeClass("hideMe"); } - $('.SignedInProfileLink').truncate({ - width: 120, - after: '&hellip;', - center: false, - addclass: false, - addtitle: false - }); - // Hide the warning message if the user closed it already if ($.cookie('hide_warning') != null) { $(".WarningMessage").hide(); @@ -1219,8 +1273,8 @@ var gaToken = 'UA-2020240-1';//]]> function isiOS() { return ( - (navigator.userAgent.match(/(iPhone)|(iPod)|(iPad)/i)) - ); + (navigator.userAgent.match(/(iPhone)|(iPod)|(iPad)/i)) + ); } }); </script> @@ -1410,9 +1464,8 @@ var gaToken = 'UA-2020240-1';//]]> var editLink = $('a[href*="report.aspx"]').attr('href'); - $("#cspMessage").prepend('<P>Please take a moment to check the listing and ensure it is ready to enable. Clicking "Submit for Review" will enable your cache page.</P>'); - $("#cspMessage").prepend('<P>Once it is enabled, you will receive a confirmation email that it was successfully submitted. It is peak season for cache placement. Your volunteer reviewer will strive to begin the review process within the next 7 days.</P>'); - $("#cspMessage").prepend('<P>Your cache page has not been reviewed yet. It will not appear in the review queue until you enable it.</P>'); + $("#cspMessage").prepend('<P><strong>Already submitted this geocache?</strong> Please check below for the Reviewer’s notes and make any necessary changes before you resubmit. You can also respond by clicking "Log Your Visit" and posting a Reviewer Note.</P>'); + $("#cspMessage").prepend('<P>Your geocache has not been published. Once your geocache is in place and all of the details have been entered, click "Submit for Review". A volunteer reviewer will begin the review process within about 7 days.</P>'); $("#cspGoBack").click(function (e) { e.preventDefault(); @@ -1433,15 +1486,7 @@ var gaToken = 'UA-2020240-1';//]]> return false; }); - $("#cspConfirm").change(function() { - if ($("#cspConfirm").is(":checked")) { - $("#cspSubmit").removeAttr('disabled'); - $("#cspGoBack").attr('disabled', true); - } else { - $("#cspSubmit").attr('disabled', true); - $("#cspGoBack").removeAttr('disabled'); - } - }); + } @@ -1529,7 +1574,7 @@ var gaToken = 'UA-2020240-1';//]]> $(this).countable({ maxLength: 500 }); - } + } }); $("#pcn_help").qtip({ @@ -1542,6 +1587,7 @@ var gaToken = 'UA-2020240-1';//]]> classes: 'ui-tooltip-dark ui-tooltip-rounded pcn-tooltip' }, }); + $("a.decrypt-link").html(decryptLogs ? locString.encrypt : locString.decrypt); if ($("#cache_logs_container").length > 0) { @@ -1633,14 +1679,16 @@ var gaToken = 'UA-2020240-1';//]]> return false; }); + $("#ctl00_ContentBody_uxNotesAboutPrinting").fancybox({ + overlayShow: false + }); + setStaticMaps(); - //$("#staticMap").lazyload(); } }); function setStaticMaps() { - var llBounds = new L.LatLngBounds(); - + var map = new L.Map('map_preview_canvas', { center: new L.LatLng(mapLatLng.lat, mapLatLng.lng), zoom: 10, @@ -1652,8 +1700,8 @@ var gaToken = 'UA-2020240-1';//]]> }) .addControl(new L.Control.Attribution({ prefix: '<a href="/about/maps.aspx#leaflet" target="_blank">About our maps</a>' })); - var mapLarge = new L.Map('map_canvas', { - center: new L.LatLng(mapLatLng.lat, mapLatLng.lng), + var mapLarge = L.map('map_canvas', { + center: [mapLatLng.lat, mapLatLng.lng], zoom: 14, doubleClickZoom: true, dragging: true, @@ -1663,23 +1711,22 @@ var gaToken = 'UA-2020240-1';//]]> attributionControl: false }) .addControl(new L.Control.Attribution({ prefix: '<a href="/about/maps.aspx#leaflet" target="_blank">About our maps</a>' })) - .addControl(new L.Control.Scale()); - - llBounds.extend(new L.LatLng(mapLatLng.lat, mapLatLng.lng)); + .addControl(new L.Control.Scale()); var tileOptions = { - tileUrl: "http://otile{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.jpg", - name: "mpqosm", - alt: "MapQuest", - //attribution: "Tiles Courtesy of <a href='http://www.mapquest.com/' target='_blank'>MapQuest</a> <img src='http://developer.mapquest.com/content/osm/mq_logo.png'>, Map and map data © 2012 <a href=\"http://www.openstreetmap.org\" target='_blank'>OpenStreetMap</a> and contributors, <a href=\"http://creativecommons.org/licenses/by-sa/2.0/\">CC-BY-SA</a>. ", + tileUrl: "https://otile{s}-s.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.jpg", + //name: "mpqosm", + //alt: "MapQuest", subdomains: "1234", - tileSize: 256, + //tileSize: 256, minZoom: 0, maxZoom: 18 }; - map.addLayer(new L.TileLayer(tileOptions.tileUrl, tileOptions)); - mapLarge.addLayer(new L.TileLayer(tileOptions.tileUrl, tileOptions)); + map.addLayer(L.tileLayer(tileOptions.tileUrl, tileOptions)); + + L.tileLayer(tileOptions.tileUrl, tileOptions) + .addTo(mapLarge); var pinIcon = L.Icon.extend({ iconSize: new L.Point(20, 23), @@ -1697,36 +1744,37 @@ var gaToken = 'UA-2020240-1';//]]> return false; }); - var mkA2 = new L.Marker(new L.LatLng(mapLatLng.lat, mapLatLng.lng), { - icon: new pinIcon({iconUrl:'/images/wpttypes/pins/' + mapLatLng.type + '.png', iconAnchor: new L.Point(10,23)}), - clickable: false, zIndexOffset:99, title: mapLatLng.name - }); + L.marker([mapLatLng.lat, mapLatLng.lng], { + icon: L.icon({iconUrl: '/images/wpttypes/pins/' + mapLatLng.type + '.png', iconSize: [20, 23], iconAnchor: [10, 23]}), + clickable: false, + //zIndexOffset: 99, + title: mapLatLng.name + }).addTo(mapLarge); map.addLayer(mkA); - mapLarge.addLayer(mkA2); - - $("#ctl00_ContentBody_uxNotesAboutPrinting").fancybox({ - overlayShow: false - }); if (cmapAdditionalWaypoints != null && cmapAdditionalWaypoints.length > 0) { + var llBounds = L.latLngBounds([mapLatLng.lat, mapLatLng.lng], [mapLatLng.lat, mapLatLng.lng]); + for (var x = 0, len = cmapAdditionalWaypoints.length; x < len; x++) { - + var item = cmapAdditionalWaypoints[x], - ll = new L.LatLng(item.lat, item.lng), - marker = new L.Marker(ll, { - icon: new pinIcon({iconUrl:'/images/wpttypes/pins/' + item.type + '.png', iconAnchor: new L.Point(10,23)}), - title: item.name, - clickable:false - }); + ll = [item.lat, item.lng]; + + L.marker(ll, { + icon: L.icon({ iconUrl: '/images/wpttypes/pins/'+ item.type + '.png', iconSize: [20, 23], iconAnchor: [10, 23] }), + title: item.name, + clickable: false + }).addTo(mapLarge); llBounds.extend(ll); - mapLarge.addLayer(marker); } - var bz = mapLarge.getBoundsZoom(llBounds.pad(1.1)); - - mapLarge.setView(new L.LatLng(mapLatLng.lat, mapLatLng.lng), bz); + + window.setTimeout(function () { + mapLarge.fitBounds(llBounds.pad(.5)); + }, 100); + } } @@ -1876,11 +1924,13 @@ var gaToken = 'UA-2020240-1';//]]> var score = 0; - if(scoreResult) + if(scoreResult) { score = scoreResult; + } - if(score > 100) + if(score > 100) { score = 100; + } $('#imgFavoriteScore').attr('src', '/images/favorites/piecharts/' + score + '.png'); var pieDesc = (score < 1 ? "<1" : score) + '% ' + $("#loc_favPointsScoreDesc").text().trim(); @@ -1894,7 +1944,13 @@ var gaToken = 'UA-2020240-1';//]]> }); } var scrollId = false, + logTop = 0; + + if (document.getElementById("cache_logs_container") != null){ logTop = $("#cache_logs_container").position().top; + } + + $(window).on("scroll", function() { if (scrollId != false) { window.clearTimeout(scrollId); @@ -1919,10 +1975,6 @@ var gaToken = 'UA-2020240-1';//]]> favContainer.removeClass('favorite-container-open'); }); } - if ($('#log-dropdown-options').is(':visible') && !$clicked.parents().hasClass('Dropdown')){ - $('#log-dropdown-options').toggleClass('hideMe'); - $('.Dropdown').toggleClass('Active'); - } }); $('#uxFavContainerLink').click(function () { @@ -1943,15 +1995,13 @@ var gaToken = 'UA-2020240-1';//]]> } }); - $('#dropdown-arrow').on('click', function(e){ - e.preventDefault(); - $('#log-dropdown-options').toggleClass('hideMe'); - $('.Dropdown').toggleClass('Active'); - }); // End --> </script> + <script type="text/javascript"> + + $(document).ready(function () { $('#hlUpgrade').bind('click', function () { var _this = $(this); @@ -1962,52 +2012,45 @@ var gaToken = 'UA-2020240-1';//]]> return false; }); }); - </script> - <script type="text/javascript"> + + + _gaq.push(['_setCustomVar', 1, 'AccountId', 3514898, 1]); + + _gaq.push(['_require', 'inpage_linkid', '//www.google-analytics.com/plugins/ga/inpage_linkid.js']); _gaq.push(['_setAccount', gaToken]); _gaq.push(['_trackPageview']); - (function () { - var ga = document.createElement('script'); - ga.src = ('https:' == document.location.protocol ? - 'https://ssl' : 'http://www') + - '.google-analytics.com/ga.js'; - ga.setAttribute('async', 'true'); - document.documentElement.firstChild.appendChild(ga); - })(); + $(function () { $("a.language").click(function (e) { e.preventDefault(); window.location.replace(window.location.href + (window.location.search.indexOf("?") == -1 ? "?" : "&") + "lang=" + $(this).attr("lang")); }); }); + + var _qevents = _qevents || []; + _qevents.push({ qacct: "p-f6VPrfmR4cujU" }); + + (function () { + var elem = document.createElement('script'); + + elem.src = (document.location.protocol == "https:" ? "https://secure" : "http://edge") + ".quantserve.com/quant.js"; + elem.async = true; + elem.type = "text/javascript"; + var scpt = document.getElementsByTagName('script')[0]; + scpt.parentNode.insertBefore(elem, scpt); + })(); + + + </script> - <!-- Quantcast Tag --> - <div id="Quantcast"> - <script type="text/javascript"> - var _qevents = _qevents || []; - - (function () { - var elem = document.createElement('script'); - - elem.src = (document.location.protocol == "https:" ? "https://secure" : "http://edge") + ".quantserve.com/quant.js"; - elem.async = true; - elem.type = "text/javascript"; - var scpt = document.getElementsByTagName('script')[0]; - scpt.parentNode.insertBefore(elem, scpt); - })(); - </script> - <script type="text/javascript"> - _qevents.push({ qacct: "p-f6VPrfmR4cujU" }); - </script> - <noscript> - <div style="display: none;"> - <img src="http://pixel.quantserve.com/pixel/p-f6VPrfmR4cujU.gif" height="1" width="1" - alt="Quantcast" /> - </div> - </noscript> - </div> - <!-- End Quantcast tag --> - <!-- Server: WEB17; Build: Web.HotFix_20130821.1 --> + <noscript> + <div style="display: none;"> + <img src="http://pixel.quantserve.com/pixel/p-f6VPrfmR4cujU.gif" height="1" width="1" + alt="Quantcast" /> + </div> + </noscript> + <!-- Server: WEB02; Build: Tucson.Main.release-20141211.Release_227 + --> </body> </html> diff --git a/tests/src/cgeo/geocaching/test/mock/GC3XX5J.java b/tests/src/cgeo/geocaching/test/mock/GC3XX5J.java index 03959b9..36e2358 100644 --- a/tests/src/cgeo/geocaching/test/mock/GC3XX5J.java +++ b/tests/src/cgeo/geocaching/test/mock/GC3XX5J.java @@ -41,6 +41,7 @@ public class GC3XX5J extends MockedCache { return "David & Ajda"; } + @NonNull @Override public CacheSize getSize() { return CacheSize.SMALL; diff --git a/tests/src/cgeo/geocaching/test/mock/MockedCache.java b/tests/src/cgeo/geocaching/test/mock/MockedCache.java index 14d8d70..1981283 100644 --- a/tests/src/cgeo/geocaching/test/mock/MockedCache.java +++ b/tests/src/cgeo/geocaching/test/mock/MockedCache.java @@ -10,16 +10,18 @@ import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.utils.TextUtils; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import org.mapsforge.core.IOUtils; -import junit.framework.Assert; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.util.Collections; import java.util.List; +import junit.framework.Assert; + public abstract class MockedCache extends Geocache { final protected Geopoint coords; @@ -141,8 +143,9 @@ public abstract class MockedCache extends Geocache { } @Override + @NonNull public List<Image> getSpoilers() { - return null; + return Collections.emptyList(); } @Override diff --git a/tests/src/cgeo/geocaching/ImageUtilsTest.java b/tests/src/cgeo/geocaching/utils/ImageUtilsTest.java index 7f6c7e5..59253c5 100644 --- a/tests/src/cgeo/geocaching/ImageUtilsTest.java +++ b/tests/src/cgeo/geocaching/utils/ImageUtilsTest.java @@ -1,10 +1,9 @@ -package cgeo.geocaching; +package cgeo.geocaching.utils; import static org.assertj.core.api.Assertions.assertThat; import cgeo.geocaching.test.AbstractResourceInstrumentationTestCase; import cgeo.geocaching.test.R; -import cgeo.geocaching.utils.ImageUtils; import java.io.ByteArrayOutputStream; import java.io.IOException; diff --git a/tests/src/cgeo/geocaching/utils/RxUtilsTest.java b/tests/src/cgeo/geocaching/utils/RxUtilsTest.java new file mode 100644 index 0000000..5259998 --- /dev/null +++ b/tests/src/cgeo/geocaching/utils/RxUtilsTest.java @@ -0,0 +1,45 @@ +package cgeo.geocaching.utils; + +import static org.assertj.core.api.Assertions.assertThat; + +import rx.Observable; +import rx.Subscription; +import rx.functions.Func1; +import rx.subjects.PublishSubject; + +import android.test.AndroidTestCase; + +public class RxUtilsTest extends AndroidTestCase { + + public static void testTakeUntil() { + final Observable<Integer> observable = Observable.range(1, 10).lift(RxUtils.operatorTakeUntil(new Func1<Integer, Boolean>() { + @Override + public Boolean call(final Integer value) { + return value > 6; + } + })); + assertThat(observable.toList().toBlocking().single().toArray()).isEqualTo(new int[]{1, 2, 3, 4, 5, 6, 7}); + } + + public static void testRememberLast() { + final PublishSubject<String> rawObservable = PublishSubject.create(); + final Observable<String> observable = RxUtils.rememberLast(rawObservable, "initial"); + + // Check that the initial value is present, and is kept there + assertThat(observable.toBlocking().first()).isEqualTo("initial"); + assertThat(observable.toBlocking().first()).isEqualTo("initial"); + + // Check that if the observable is not subscribed, changes are not propagated (similar to not keeping the + // inner subscription active). + rawObservable.onNext("without subscribers"); + assertThat(observable.toBlocking().first()).isEqualTo("initial"); + + // Check that new values are propagated and cached + final Subscription subscription = observable.subscribe(); + rawObservable.onNext("first"); + assertThat(observable.toBlocking().first()).isEqualTo("first"); + subscription.unsubscribe(); + assertThat(observable.toBlocking().first()).isEqualTo("first"); + } + +} diff --git a/tests/src/cgeo/test/Compare.java b/tests/src/cgeo/test/Compare.java index 0a2fa24..c6d38ae 100644 --- a/tests/src/cgeo/test/Compare.java +++ b/tests/src/cgeo/test/Compare.java @@ -57,8 +57,8 @@ public abstract class Compare { // The inventories can differ too often, therefore we don't compare them. Also, the personal note // cannot be expected to match with different tester accounts. - final int actualSpoilersSize = null != actual.getSpoilers() ? actual.getSpoilers().size() : 0; - final int expectedSpoilersSize = null != expected.getSpoilers() ? expected.getSpoilers().size() : 0; + final int actualSpoilersSize = actual.getSpoilers().size(); + final int expectedSpoilersSize = expected.getSpoilers().size(); assertThat(actualSpoilersSize).as(cacheStr + "spoiler count").isEqualTo(expectedSpoilersSize); } } diff --git a/tests/tests.iml b/tests/tests.iml index ac5381c..9cb488e 100644 --- a/tests/tests.iml +++ b/tests/tests.iml @@ -16,7 +16,7 @@ <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" /> </content> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="module" module-name="cgeo" exported="" scope="TEST" /> + <orderEntry type="module" module-name="cgeo" scope="TEST" /> <orderEntry type="module-library"> <library> <CLASSES> @@ -31,17 +31,5 @@ </library> </orderEntry> <orderEntry type="inheritedJdk" /> - <orderEntry type="module-library" scope="PROVIDED"> - <library> - <CLASSES> - <root url="jar://$MODULE_DIR$/../main/compile-libs/org.eclipse.jdt.annotation_1.1.0.v20130513-1648.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$MODULE_DIR$/../main/compile-libs/org.eclipse.jdt.annotation_1.1.0.v20130513-1648.jar!/src" /> - </SOURCES> - </library> - </orderEntry> </component> -</module> - +</module>
\ No newline at end of file |
