aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--main/res/values-de/strings.xml27
-rw-r--r--main/res/values-pt/strings.xml2
-rw-r--r--main/res/values/strings.xml16
-rw-r--r--main/res/values/strings_not_translatable.xml1
-rw-r--r--main/src/cgeo/geocaching/CacheDetailActivity.java43
-rw-r--r--main/src/cgeo/geocaching/CacheListActivity.java8
-rw-r--r--main/src/cgeo/geocaching/CacheMenuHandler.java5
-rw-r--r--main/src/cgeo/geocaching/CachePopup.java2
-rw-r--r--main/src/cgeo/geocaching/CgeoApplication.java2
-rw-r--r--main/src/cgeo/geocaching/DataStore.java24
-rw-r--r--main/src/cgeo/geocaching/Geocache.java8
-rw-r--r--main/src/cgeo/geocaching/LogCacheActivity.java2
-rw-r--r--main/src/cgeo/geocaching/PocketQueryList.java3
-rw-r--r--main/src/cgeo/geocaching/SearchActivity.java16
-rw-r--r--main/src/cgeo/geocaching/Waypoint.java59
-rw-r--r--main/src/cgeo/geocaching/apps/AbstractLocusApp.java6
-rw-r--r--main/src/cgeo/geocaching/apps/cache/navi/AbstractPointNavigationApp.java13
-rw-r--r--main/src/cgeo/geocaching/connector/ConnectorFactory.java17
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCParser.java17
-rw-r--r--main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java6
-rw-r--r--main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java4
-rw-r--r--main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java7
-rw-r--r--main/src/cgeo/geocaching/export/GpxSerializer.java25
-rw-r--r--main/src/cgeo/geocaching/filter/FilterUserInterface.java3
-rw-r--r--main/src/cgeo/geocaching/filter/PersonalNoteFilter.java28
-rw-r--r--main/src/cgeo/geocaching/filter/PopularityFilter.java43
-rw-r--r--main/src/cgeo/geocaching/filter/PopularityRatioFilter.java73
-rw-r--r--main/src/cgeo/geocaching/filter/StateFilter.java2
-rw-r--r--main/src/cgeo/geocaching/sorting/PopularityRatioComparator.java5
-rw-r--r--main/src/cgeo/geocaching/ui/CacheDetailsCreator.java18
-rw-r--r--main/src/cgeo/geocaching/ui/CacheListAdapter.java6
-rw-r--r--main/src/cgeo/geocaching/ui/Formatter.java10
-rw-r--r--main/src/cgeo/geocaching/utils/DateUtils.java12
-rw-r--r--main/src/cgeo/geocaching/utils/TextUtils.java12
-rw-r--r--tests/src/cgeo/geocaching/GeocacheTest.java8
-rw-r--r--tests/src/cgeo/geocaching/WaypointTest.java38
36 files changed, 446 insertions, 125 deletions
diff --git a/main/res/values-de/strings.xml b/main/res/values-de/strings.xml
index deacfb1..e67690d 100644
--- a/main/res/values-de/strings.xml
+++ b/main/res/values-de/strings.xml
@@ -202,6 +202,8 @@
<string name="warn_deprecated_mapfile">Es wird eine veraltete Offline-Karte v0.2.4 verwendet.\nBitte zukünftig Offline-Karten in der Version 0.3.0 verwenden.\nDer Support für die alte Version wird im nächsten Release eingestellt.</string>
<string name="warn_nonexistant_mapfile">Die gewählte Datei existiert nicht.\nOffline Karte ist nicht verfügbar.</string>
<string name="warn_rendertheme_missing">Das gewählte Karten-Theme wurde nicht gefunden.</string>
+ <string name="warn_pocket_query_select">Kein Pocket Query ausgewählt.</string>
+ <string name="warn_no_pocket_query_found">Kein Pocket Query online gefunden.</string>
<string name="info_log_posted">Log erfolgreich gesendet.</string>
<string name="info_log_saved">Log erfolgreich gespeichert.</string>
<string name="info_log_cleared">Log wurde geleert.</string>
@@ -245,8 +247,8 @@
<string name="caches_sort_difficulty">Schwierigkeit</string>
<string name="caches_sort_terrain">Gelände</string>
<string name="caches_sort_size">Größe</string>
- <string name="caches_sort_favorites">Beliebtheit</string>
- <string name="caches_sort_favorites_ratio">Beliebtheit [%]</string>
+ <string name="caches_sort_favorites">Favoriten</string>
+ <string name="caches_sort_favorites_ratio">Favoriten [%]</string>
<string name="caches_sort_name">Name</string>
<string name="caches_sort_geocode">Geo-Code</string>
<string name="caches_sort_rating">Bewertung</string>
@@ -289,6 +291,8 @@
<string name="caches_filter_modified">Mit geänderten Koordinaten</string>
<string name="caches_filter_origin">Herkunft</string>
<string name="caches_filter_distance">Entfernung</string>
+ <string name="caches_filter_popularity">Favoriten</string>
+ <string name="caches_filter_popularity_ratio">Favoriten [%]</string>
<string name="caches_removing_from_history">Lösche aus Verlauf…</string>
<string name="caches_clear_offlinelogs">Offline-Logs löschen</string>
<string name="caches_clear_offlinelogs_progress">Lösche Offline-Logs</string>
@@ -343,6 +347,8 @@
<string name="settings_gc_legal_note">Mit Benutzung der Dienste von geocaching.com werden die Groundspeak-Nutzungsbedingungen (englisch) akzeptiert.</string>
<string name="settings_info_facebook_login_title">Facebook-Login</string>
<string name="settings_info_facebook_login">c:geo kann sich zwar nicht per Facebook bei Geocaching.com einloggen, aber es gibt eine einfache Abhilfe …</string>
+ <string name="settings_authorize">c:geo autorisieren</string>
+ <string name="settings_reauthorize">c:geo erneut autorisieren</string>
<string name="init_oc">Opencaching.de</string>
<string name="settings_activate_oc">Aktivieren</string>
<string name="init_oc_de_description">Autorisiere c:geo auf opencaching.de zuzugreifen um Caches zu suchen und nach deinen Funden zu filtern.</string>
@@ -456,6 +462,11 @@
<string name="init_use_native_ua">Android-Browser</string>
<string name="init_summary_use_native_ua">Als Android-Browser identifizieren. Löst Login-Probleme einiger Netzanbieter.</string>
<string name="init_rendertheme_folder">Karten-Themes-Verzeichnis</string>
+ <string name="settings_open_website">Öffne Webseite</string>
+ <string name="settings_settings">Einstellungen</string>
+ <string name="settings_information">Information</string>
+ <string name="settings_twitter_cache_message">Nachricht für gefundene Caches</string>
+ <string name="settings_twitter_trackable_message">Nachricht für gefundene Trackables</string>
<string name="map_source_google_map">Google: Karte</string>
<string name="map_source_google_satellite">Google: Satellit</string>
<string name="map_source_osm_mapnik">OSM: Mapnik</string>
@@ -467,7 +478,7 @@
<string name="init_sendToCgeo_description">Send to c:geo erlaubt es, mit einem speziellen Plugin für Firefox oder Chrome, Caches direkt von der Geocachingseite zu empfangen. Bitte dazu die Details vor einer Registrierung auf <a href="http://send2.cgeo.org/">http://send2.cgeo.org/</a> durchlesen.</string>
<string name="init_sendToCgeo_register">Registrierung anfordern</string>
<string name="init_sendToCgeo_registering">Das Gerät wird für \"Send to c:geo\" registriert…</string>
- <string name="init_sendToCgeo_register_ok">Registrierung erfolgreich. Der PIN Code ist ####. Bitte diesen auf der der c:geo Website angeben, um das Endgerät im Browser hinzuzufügen.</string>
+ <string name="init_sendToCgeo_register_ok">Registrierung erfolgreich. Der PIN Code ist ####. Bitte diesen auf der c:geo Website angeben, um das Endgerät im Browser hinzuzufügen.</string>
<string name="init_sendToCgeo_register_fail">Registrierung fehlgeschlagen.</string>
<string name="sendToCgeo_download_fail">c:geo konnte Caches nicht laden. Entweder besteht keine Internetverbindung oder send2c:geo funktioniert nicht.</string>
<string name="sendToCgeo_no_registration">c:geo konnte Caches nicht laden. Registrierung für send2c:geo ungültig. Bitte in Einstellungen neu registrieren.</string>
@@ -477,7 +488,7 @@
<string name="auth_again">Neustarten</string>
<string name="auth_ocde">opencaching.de</string>
<string name="auth_ocpl">opencaching.pl</string>
- <string name="auth_dialog_completed_oc">c:geo ist nun autorisiert, caches zu laden und auf %s zu loggen.</string>
+ <string name="auth_dialog_completed_oc">c:geo ist nun autorisiert, Caches zu laden und auf %s zu loggen.</string>
<string name="cache_offline">Offline</string>
<string name="cache_offline_refresh">Aktualisieren</string>
<string name="cache_offline_drop">Löschen</string>
@@ -736,6 +747,9 @@
<string name="search_address_started">Suche nach Orten</string>
<string name="search_address_result">Gefundene Orte</string>
<string name="search_own_caches">Meine Caches suchen</string>
+ <string name="search_pocket_title">Pocket Query</string>
+ <string name="search_pocket_loading">Lade Liste der Pocket Queries</string>
+ <string name="search_pocket_select">Pocket Query wählen</string>
<string name="trackable">Trackable</string>
<string name="trackable_details_loading">Lade Details des Trackable…</string>
<string name="trackable_log_touch">Logge Fund</string>
@@ -1065,4 +1079,9 @@
<item quantity="one">gestern</item>
<item quantity="other">vor %d Tagen</item>
</plurals>
+ <plurals name="favorite_points">
+ <item quantity="one">%s Favorit</item>
+ <item quantity="other">%s Favoriten</item>
+ </plurals>
+ <string name="percent_favorite_points">% Favoriten</string>
</resources>
diff --git a/main/res/values-pt/strings.xml b/main/res/values-pt/strings.xml
index b2005ea..27b4bb2 100644
--- a/main/res/values-pt/strings.xml
+++ b/main/res/values-pt/strings.xml
@@ -342,7 +342,7 @@
<string name="settings_activate_gc">Activar</string>
<string name="settings_gc_legal_note">Quando você usa do serviço do geocaching.com, você aceita os termos de uso da Groundspeak.</string>
<string name="settings_info_facebook_login_title">Facebook Login</string>
- <string name="settings_info_facebook_login">Não consegue que c:geo faça o login em geocaching.com com a sua conta do Facebook. Mas existe uma solução simples…</string>
+ <string name="settings_info_facebook_login" tools:ignore="Typos">Não consegue que c:geo faça o login em geocaching.com com a sua conta do Facebook. Mas existe uma solução simples…</string>
<string name="settings_authorize">Autorizar c:geo</string>
<string name="settings_reauthorize">Reautorizar c:geo</string>
<string name="init_oc">Opencaching.de</string>
diff --git a/main/res/values/strings.xml b/main/res/values/strings.xml
index 280bc44..817ff77 100644
--- a/main/res/values/strings.xml
+++ b/main/res/values/strings.xml
@@ -285,8 +285,8 @@
<string name="caches_sort_difficulty">Difficulty</string>
<string name="caches_sort_terrain">Terrain</string>
<string name="caches_sort_size">Size</string>
- <string name="caches_sort_favorites">Popularity</string>
- <string name="caches_sort_favorites_ratio">Popularity [%]</string>
+ <string name="caches_sort_favorites">Favorites</string>
+ <string name="caches_sort_favorites_ratio">Favorites [%]</string>
<string name="caches_sort_name">Name</string>
<string name="caches_sort_geocode">Geo Code</string>
<string name="caches_sort_rating">Rating</string>
@@ -329,6 +329,9 @@
<string name="caches_filter_modified">With modified coordinates</string>
<string name="caches_filter_origin">Origin</string>
<string name="caches_filter_distance">Distance</string>
+ <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_removing_from_history">Removing from History…</string>
<string name="caches_clear_offlinelogs">Clear offline logs</string>
<string name="caches_clear_offlinelogs_progress">Clearing offline logs</string>
@@ -477,7 +480,7 @@
<string name="init_backup_last">Available backup from</string>
<string name="init_backup_last_no">There is no file with a backup.</string>
<string name="settings_info_offline_maps_title">Info on Offline Maps</string>
- <string name="settings_info_offline_maps">c:geo supports maps for offline use. You can download maps from Mapsforge or even create your own maps from OSM data. You need to select the offline map directory first to to get offline map support.</string>
+ <string name="settings_info_offline_maps">c:geo supports maps for offline use. You can download maps from Mapsforge or even create your own maps from OSM data. You need to select the offline map directory first to get offline map support.</string>
<string name="settings_info_themes_title">Info on Map Themes</string>
<string name="settings_info_themes">c:geo supports custom themes for offline maps. These can be used to change the color style of the map (e.g. to have a nightview map) or to highlight certain objects like cycle paths or height lines within the map.</string>
<string name="init_mapsource_select">Select Map Source</string>
@@ -1191,5 +1194,10 @@
<item quantity="one">yesterday</item>
<item quantity="other">%d days ago</item>
</plurals>
-
+ <plurals name="favorite_points">
+ <item quantity="one">%s favorite</item>
+ <item quantity="other">%s favorites</item>
+ </plurals>
+
+ <string name="percent_favorite_points">%\ favorites</string>
</resources>
diff --git a/main/res/values/strings_not_translatable.xml b/main/res/values/strings_not_translatable.xml
index e8d4e27..e5db27c 100644
--- a/main/res/values/strings_not_translatable.xml
+++ b/main/res/values/strings_not_translatable.xml
@@ -61,6 +61,7 @@
· <a href="http://www.jaytech.cz/">Jan Žatecký</a> (graphic)\n
· <a href="http://joachim-wilke.de/">JoWi24</a> (code)\n
· <a href="http://github.com/koem">Karsten Priegnitz</a> (code, artwork computation)\n
+ · <a href="http://www.geocaching.com/profile/?guid=231070d8-b6f4-4d14-8d5a-b8bdcb19b78e">KiwiStone</a> (code, localization DE)\n
· <a href="http://www.geocaching.com/email/?guid=d11a3e3d-7db0-4d43-87f2-7893238844a6">Lineflyer</a> (tester, support, localization DE)\n
· Ludovic Valente (localization FR)\n
· marco-jacob (code, loc. DE)\n
diff --git a/main/src/cgeo/geocaching/CacheDetailActivity.java b/main/src/cgeo/geocaching/CacheDetailActivity.java
index ba8f470..d0273c5 100644
--- a/main/src/cgeo/geocaching/CacheDetailActivity.java
+++ b/main/src/cgeo/geocaching/CacheDetailActivity.java
@@ -68,6 +68,7 @@ import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -107,6 +108,7 @@ import android.widget.TextView.BufferType;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Date;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
@@ -971,8 +973,9 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
}
// cache hidden
- if (cache.getHiddenDate() != null) {
- final long time = cache.getHiddenDate().getTime();
+ final Date hiddenDate = cache.getHiddenDate();
+ if (hiddenDate != null) {
+ final long time = hiddenDate.getTime();
if (time > 0) {
String dateString = Formatter.formatFullDate(time);
if (cache.isEventCache()) {
@@ -1812,6 +1815,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
}
private class WaypointsViewCreator extends AbstractCachingPageViewCreator<ListView> {
+ private final int VISITED_INSET = (int) (6.6f * CgeoApplication.getInstance().getResources().getDisplayMetrics().density + 0.5f);
@Override
public ListView getDispatchedView() {
@@ -1822,7 +1826,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
// sort waypoints: PP, Sx, FI, OWN
final List<Waypoint> sortedWaypoints = new ArrayList<Waypoint>(cache.getWaypoints());
- Collections.sort(sortedWaypoints);
+ Collections.sort(sortedWaypoints, Waypoint.WAYPOINT_COMPARATOR);
view = (ListView) getLayoutInflater().inflate(R.layout.cachedetail_waypoints_page, null);
view.setClickable(true);
@@ -1862,20 +1866,26 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
protected void fillViewHolder(View rowView, final WaypointViewHolder holder, final Waypoint wpt) {
// coordinates
+ final TextView coordinatesView = holder.coordinatesView;
if (null != wpt.getCoords()) {
- final TextView coordinatesView = holder.coordinatesView;
coordinatesView.setOnClickListener(new CoordinatesFormatSwitcher(wpt.getCoords()));
coordinatesView.setText(wpt.getCoords().toString());
coordinatesView.setVisibility(View.VISIBLE);
}
+ else {
+ coordinatesView.setVisibility(View.GONE);
+ }
// info
final String waypointInfo = Formatter.formatWaypointInfo(wpt);
+ final TextView infoView = holder.infoView;
if (StringUtils.isNotBlank(waypointInfo)) {
- final TextView infoView = holder.infoView;
infoView.setText(waypointInfo);
infoView.setVisibility(View.VISIBLE);
}
+ else {
+ infoView.setVisibility(View.GONE);
+ }
// title
final TextView nameView = holder.nameView;
@@ -1886,7 +1896,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
} else {
nameView.setText(res.getString(R.string.waypoint));
}
- wpt.setIcon(res, nameView);
+ setWaypointIcon(res, nameView, wpt);
// visited
if (wpt.isVisited()) {
@@ -1899,8 +1909,8 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
}
// note
+ final TextView noteView = holder.noteView;
if (StringUtils.isNotBlank(wpt.getNote())) {
- final TextView noteView = holder.noteView;
noteView.setVisibility(View.VISIBLE);
if (TextUtils.containsHtml(wpt.getNote())) {
noteView.setText(Html.fromHtml(wpt.getNote()), TextView.BufferType.SPANNABLE);
@@ -1909,6 +1919,9 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
noteView.setText(wpt.getNote());
}
}
+ else {
+ noteView.setVisibility(View.GONE);
+ }
final View wpNavView = holder.wpNavView;
wpNavView.setOnClickListener(new View.OnClickListener() {
@@ -1944,6 +1957,22 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
}
});
}
+
+ private void setWaypointIcon(final Resources res, final TextView nameView, final Waypoint wpt) {
+ final WaypointType waypointType = wpt.getWaypointType();
+ final Drawable icon;
+ if (wpt.isVisited()) {
+ LayerDrawable ld = new LayerDrawable(new Drawable[] {
+ res.getDrawable(waypointType.markerId),
+ res.getDrawable(R.drawable.tick) });
+ ld.setLayerInset(0, 0, 0, VISITED_INSET, VISITED_INSET);
+ ld.setLayerInset(1, VISITED_INSET, VISITED_INSET, 0, 0);
+ icon = ld;
+ } else {
+ icon = res.getDrawable(waypointType.markerId);
+ }
+ nameView.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
+ }
}
private class InventoryViewCreator extends AbstractCachingPageViewCreator<ListView> {
diff --git a/main/src/cgeo/geocaching/CacheListActivity.java b/main/src/cgeo/geocaching/CacheListActivity.java
index 59f7297..8226f38 100644
--- a/main/src/cgeo/geocaching/CacheListActivity.java
+++ b/main/src/cgeo/geocaching/CacheListActivity.java
@@ -78,7 +78,6 @@ import android.widget.TextView;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -706,11 +705,8 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA
public void deletePastEvents() {
final List<Geocache> deletion = new ArrayList<Geocache>();
for (final Geocache cache : adapter.getCheckedOrAllCaches()) {
- if (cache.isEventCache()) {
- final Date eventDate = cache.getHiddenDate();
- if (DateUtils.daysSince(eventDate.getTime()) > 0) {
- deletion.add(cache);
- }
+ if (DateUtils.isPastEvent(cache)) {
+ deletion.add(cache);
}
}
new DropDetailsTask(false).execute(deletion.toArray(new Geocache[deletion.size()]));
diff --git a/main/src/cgeo/geocaching/CacheMenuHandler.java b/main/src/cgeo/geocaching/CacheMenuHandler.java
index 9326a73..887f6cf 100644
--- a/main/src/cgeo/geocaching/CacheMenuHandler.java
+++ b/main/src/cgeo/geocaching/CacheMenuHandler.java
@@ -17,6 +17,8 @@ import android.net.Uri;
import android.view.Menu;
import android.view.MenuItem;
+import java.util.Date;
+
/**
* Shared menu handling for all activities having menu items related to a cache.
*
@@ -83,10 +85,11 @@ public class CacheMenuHandler extends AbstractUIFactory {
final boolean calendarAddOnAvailable = ProcessUtils.isIntentAvailable(ICalendar.INTENT, Uri.parse(ICalendar.URI_SCHEME + "://" + ICalendar.URI_HOST));
if (calendarAddOnAvailable) {
+ final Date hiddenDate = cache.getHiddenDate();
final Parameters params = new Parameters(
ICalendar.PARAM_NAME, cache.getName(),
ICalendar.PARAM_NOTE, StringUtils.defaultString(cache.getPersonalNote()),
- ICalendar.PARAM_HIDDEN_DATE, String.valueOf(cache.getHiddenDate().getTime()),
+ ICalendar.PARAM_HIDDEN_DATE, hiddenDate != null ? String.valueOf(hiddenDate.getTime()) : StringUtils.EMPTY,
ICalendar.PARAM_URL, StringUtils.defaultString(cache.getUrl()),
ICalendar.PARAM_COORDS, cache.getCoords() == null ? "" : cache.getCoords().format(GeopointFormatter.Format.LAT_LON_DECMINUTE_RAW),
ICalendar.PARAM_LOCATION, StringUtils.defaultString(cache.getLocation()),
diff --git a/main/src/cgeo/geocaching/CachePopup.java b/main/src/cgeo/geocaching/CachePopup.java
index 020c604..d88000f 100644
--- a/main/src/cgeo/geocaching/CachePopup.java
+++ b/main/src/cgeo/geocaching/CachePopup.java
@@ -45,7 +45,7 @@ public class CachePopup extends AbstractPopupActivity {
private class DropCacheHandler extends Handler {
@Override
public void handleMessage(Message msg) {
- init();
+ CachePopup.this.finish();
}
}
diff --git a/main/src/cgeo/geocaching/CgeoApplication.java b/main/src/cgeo/geocaching/CgeoApplication.java
index 2c419cf..0af8117 100644
--- a/main/src/cgeo/geocaching/CgeoApplication.java
+++ b/main/src/cgeo/geocaching/CgeoApplication.java
@@ -37,8 +37,6 @@ public class CgeoApplication extends Application {
@Override
public void onCreate() {
new Thread(statusUpdater).start();
- // Initialize densitiy related waypoint data
- Waypoint.initializeScale();
}
@Override
diff --git a/main/src/cgeo/geocaching/DataStore.java b/main/src/cgeo/geocaching/DataStore.java
index 7c19a83..a231e8b 100644
--- a/main/src/cgeo/geocaching/DataStore.java
+++ b/main/src/cgeo/geocaching/DataStore.java
@@ -1067,10 +1067,11 @@ public class DataStore {
values.put("name", cache.getName());
values.put("owner", cache.getOwnerDisplayName());
values.put("owner_real", cache.getOwnerUserId());
- if (cache.getHiddenDate() == null) {
+ final Date hiddenDate = cache.getHiddenDate();
+ if (hiddenDate == null) {
values.put("hidden", 0);
} else {
- values.put("hidden", cache.getHiddenDate().getTime());
+ values.put("hidden", hiddenDate.getTime());
}
values.put("hint", cache.getHint());
values.put("size", cache.getSize() == null ? "" : cache.getSize().id);
@@ -1190,10 +1191,12 @@ public class DataStore {
List<Waypoint> waypoints = cache.getWaypoints();
if (CollectionUtils.isNotEmpty(waypoints)) {
+ final ArrayList<String> currentWaypointIds = new ArrayList<String>();
ContentValues values = new ContentValues();
long timeStamp = System.currentTimeMillis();
for (Waypoint oneWaypoint : waypoints) {
if (oneWaypoint.isUserDefined()) {
+ currentWaypointIds.add(Integer.toString(oneWaypoint.getId()));
continue;
}
@@ -1215,13 +1218,28 @@ public class DataStore {
} else {
database.update(dbTableWaypoints, values, "_id = ?", new String[] { Integer.toString(oneWaypoint.getId(), 10) });
}
+ currentWaypointIds.add(Integer.toString(oneWaypoint.getId()));
}
+
+ removeOutdatedWaypointsOfCache(cache, currentWaypointIds);
}
}
/**
+ * remove all waypoints of the given cache, where the id is not in the given list
+ *
+ * @param cache
+ * @param remainingWaypointIds
+ * ids of waypoints which shall not be deleted
+ */
+ private static void removeOutdatedWaypointsOfCache(final @NonNull Geocache cache, final @NonNull Collection<String> remainingWaypointIds) {
+ final String idList = StringUtils.join(remainingWaypointIds, ',');
+ database.delete(dbTableWaypoints, "geocode = ? AND _id NOT in (" + idList + ")", new String[] { cache.getGeocode() });
+ }
+
+ /**
* Save coordinates into a ContentValues
- *
+ *
* @param values
* a ContentValues to save coordinates in
* @param oneWaypoint
diff --git a/main/src/cgeo/geocaching/Geocache.java b/main/src/cgeo/geocaching/Geocache.java
index 35d6c17..8cfedb3 100644
--- a/main/src/cgeo/geocaching/Geocache.java
+++ b/main/src/cgeo/geocaching/Geocache.java
@@ -37,6 +37,7 @@ import org.apache.commons.collections4.Predicate;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jdt.annotation.Nullable;
import android.app.Activity;
import android.content.Intent;
@@ -253,7 +254,7 @@ public class Geocache implements ICache, IWaypoint {
if (hidden == null) {
hidden = other.hidden;
}
- if (StringUtils.isBlank(getHint())) {
+ if (!detailed && StringUtils.isBlank(getHint())) {
hint = other.getHint();
}
if (size == null || CacheSize.UNKNOWN == size) {
@@ -284,7 +285,7 @@ public class Geocache implements ICache, IWaypoint {
final PersonalNote mergedNote = myNote.mergeWith(otherNote);
personalNote = mergedNote.toString();
}
- if (StringUtils.isBlank(getShortDescription())) {
+ if (!detailed && StringUtils.isBlank(getShortDescription())) {
shortdesc = other.getShortDescription();
}
if (StringUtils.isBlank(getDescription())) {
@@ -304,7 +305,7 @@ public class Geocache implements ICache, IWaypoint {
if (myVote == 0) {
myVote = other.myVote;
}
- if (attributes.isEmpty()) {
+ if (!detailed && attributes.isEmpty()) {
attributes.clear();
if (other.attributes != null) {
attributes.addAll(other.attributes);
@@ -750,6 +751,7 @@ public class Geocache implements ICache, IWaypoint {
}
@Override
+ @Nullable
public Date getHiddenDate() {
return hidden;
}
diff --git a/main/src/cgeo/geocaching/LogCacheActivity.java b/main/src/cgeo/geocaching/LogCacheActivity.java
index 34f036c..f17a008 100644
--- a/main/src/cgeo/geocaching/LogCacheActivity.java
+++ b/main/src/cgeo/geocaching/LogCacheActivity.java
@@ -344,7 +344,7 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia
rating = GCVote.NO_RATING;
if (cache.isEventCache()) {
final Date eventDate = cache.getHiddenDate();
- boolean expired = DateUtils.daysSince(eventDate.getTime()) >= 0;
+ boolean expired = DateUtils.isPastEvent(cache);
if (cache.hasOwnLog(LogType.WILL_ATTEND) || expired) {
typeSelected = cache.hasOwnLog(LogType.ATTENDED) ? LogType.NOTE : LogType.ATTENDED;
diff --git a/main/src/cgeo/geocaching/PocketQueryList.java b/main/src/cgeo/geocaching/PocketQueryList.java
index e1a921c..9d1110d 100644
--- a/main/src/cgeo/geocaching/PocketQueryList.java
+++ b/main/src/cgeo/geocaching/PocketQueryList.java
@@ -53,8 +53,7 @@ public final class PocketQueryList {
for (int i = 0; i < pocketQueryList.size(); i++) {
PocketQueryList pq = pocketQueryList.get(i);
-
- items[i] = pq.name + " (" + pq.maxCaches + ")";
+ items[i] = pq.name;
}
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
diff --git a/main/src/cgeo/geocaching/SearchActivity.java b/main/src/cgeo/geocaching/SearchActivity.java
index fe48fda..0756a00 100644
--- a/main/src/cgeo/geocaching/SearchActivity.java
+++ b/main/src/cgeo/geocaching/SearchActivity.java
@@ -110,7 +110,9 @@ public class SearchActivity extends AbstractActivity {
* Set to true if keyword search should be performed if query isn't GC or TB
* @return true if a search was performed, else false
*/
- private boolean instantSearch(final String query, final boolean keywordSearch) {
+ private boolean instantSearch(final String nonTrimmedQuery, final boolean keywordSearch) {
+ final String query = StringUtils.trim(nonTrimmedQuery);
+
// first check if this was a scanned URL
String geocode = ConnectorFactory.getGeocodeFromURL(query);
@@ -126,7 +128,17 @@ public class SearchActivity extends AbstractActivity {
}
// Check if the query is a TB code
- final TrackableConnector trackableConnector = ConnectorFactory.getTrackableConnector(geocode);
+ TrackableConnector trackableConnector = ConnectorFactory.getTrackableConnector(geocode);
+
+ // check if the query contains a TB code
+ if (trackableConnector == ConnectorFactory.UNKNOWN_TRACKABLE_CONNECTOR) {
+ final String tbCode = ConnectorFactory.getTrackableFromURL(query);
+ if (StringUtils.isNotBlank(tbCode)) {
+ trackableConnector = ConnectorFactory.getTrackableConnector(tbCode);
+ geocode = tbCode;
+ }
+ }
+
if (trackableConnector != ConnectorFactory.UNKNOWN_TRACKABLE_CONNECTOR) {
final Intent trackablesIntent = new Intent(this, TrackableActivity.class);
trackablesIntent.putExtra(Intents.EXTRA_GEOCODE, geocode.toUpperCase(Locale.US));
diff --git a/main/src/cgeo/geocaching/Waypoint.java b/main/src/cgeo/geocaching/Waypoint.java
index b204bdd..facd914 100644
--- a/main/src/cgeo/geocaching/Waypoint.java
+++ b/main/src/cgeo/geocaching/Waypoint.java
@@ -4,20 +4,14 @@ import cgeo.geocaching.enumerations.WaypointType;
import cgeo.geocaching.geopoint.Geopoint;
import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jdt.annotation.NonNull;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.widget.TextView;
-
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-/**
- * Note: this class has a natural ordering that is inconsistent with equals.
- */
-public class Waypoint implements IWaypoint, Comparable<Waypoint> {
+public class Waypoint implements IWaypoint {
public static final String PREFIX_OWN = "OWN";
private static final int ORDER_UNDEFINED = -2;
@@ -33,13 +27,6 @@ public class Waypoint implements IWaypoint, Comparable<Waypoint> {
private int cachedOrder = ORDER_UNDEFINED;
private boolean own = false;
private boolean visited = false;
- // preliminary default for mdpi screens
- private static int VISITED_INSET = 7;
-
- public static void initializeScale() {
- // Calculate visited inset based on screen density
- VISITED_INSET = (int) (6.6f * CgeoApplication.getInstance().getResources().getDisplayMetrics().density + 0.5f);
- }
/**
* require name and type for every waypoint
@@ -64,22 +51,6 @@ public class Waypoint implements IWaypoint, Comparable<Waypoint> {
id = -1;
}
- public void setIcon(final Resources res, final TextView nameView) {
- Drawable icon;
- if (visited) {
- LayerDrawable ld = new LayerDrawable(new Drawable[] {
- res.getDrawable(waypointType.markerId),
- res.getDrawable(R.drawable.tick) });
- ld.setLayerInset(0, 0, 0, VISITED_INSET, VISITED_INSET);
- ld.setLayerInset(1, VISITED_INSET, VISITED_INSET, 0, 0);
- icon = ld;
- } else {
- icon = res.getDrawable(waypointType.markerId);
- }
- final Drawable fIcon = icon;
- nameView.setCompoundDrawablesWithIntrinsicBounds(fIcon, null, null, null);
- }
-
public void merge(final Waypoint old) {
if (StringUtils.isBlank(prefix)) {
setPrefix(old.prefix);
@@ -123,11 +94,18 @@ public class Waypoint implements IWaypoint, Comparable<Waypoint> {
if (newPrefixes.containsKey(prefix)) {
newPrefixes.get(prefix).merge(oldWaypoint);
} else if (oldWaypoint.isUserDefined() || forceMerge) {
- newPoints.add(oldWaypoint);
+ // personal note waypoints should always be taken from the new list only
+ if (!isPersonalNoteWaypoint(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;
}
@@ -163,11 +141,6 @@ public class Waypoint implements IWaypoint, Comparable<Waypoint> {
return cachedOrder;
}
- @Override
- public int compareTo(Waypoint other) {
- return order() - other.order();
- }
-
public String getPrefix() {
return prefix;
}
@@ -282,4 +255,14 @@ public class Waypoint implements IWaypoint, Comparable<Waypoint> {
return (int) hash;
}
+ /**
+ * Sort waypoints by their probable order (e.g. parking first, final last).
+ */
+ public static final Comparator<? super Waypoint> WAYPOINT_COMPARATOR = new Comparator<Waypoint>() {
+
+ @Override
+ public int compare(Waypoint left, Waypoint right) {
+ return left.order() - right.order();
+ }
+ };
}
diff --git a/main/src/cgeo/geocaching/apps/AbstractLocusApp.java b/main/src/cgeo/geocaching/apps/AbstractLocusApp.java
index 4b524bf..d6c2fe6 100644
--- a/main/src/cgeo/geocaching/apps/AbstractLocusApp.java
+++ b/main/src/cgeo/geocaching/apps/AbstractLocusApp.java
@@ -20,6 +20,7 @@ import android.app.Activity;
import android.location.Location;
import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
import java.util.Locale;
@@ -119,8 +120,9 @@ public abstract class AbstractLocusApp extends AbstractApp {
pg.premiumOnly = cache.isPremiumMembersOnly();
pg.name = cache.getName();
pg.placedBy = cache.getOwnerDisplayName();
- if (cache.getHiddenDate() != null) {
- pg.hidden = ISO8601DATE.format(cache.getHiddenDate().getTime());
+ final Date hiddenDate = cache.getHiddenDate();
+ if (hiddenDate != null) {
+ pg.hidden = ISO8601DATE.format(hiddenDate.getTime());
}
int locusId = toLocusType(cache.getType());
if (locusId != NO_LOCUS_ID) {
diff --git a/main/src/cgeo/geocaching/apps/cache/navi/AbstractPointNavigationApp.java b/main/src/cgeo/geocaching/apps/cache/navi/AbstractPointNavigationApp.java
index 75ea056..a1c752c 100644
--- a/main/src/cgeo/geocaching/apps/cache/navi/AbstractPointNavigationApp.java
+++ b/main/src/cgeo/geocaching/apps/cache/navi/AbstractPointNavigationApp.java
@@ -24,7 +24,10 @@ abstract class AbstractPointNavigationApp extends AbstractApp implements CacheNa
@Override
public void navigate(Activity activity, Geocache cache) {
- final Geopoint coords = cache.getCoords();
+ navigateWithNullCheck(activity, cache.getCoords());
+ }
+
+ private void navigateWithNullCheck(Activity activity, final Geopoint coords) {
if (coords != null) {
navigate(activity, coords);
} else {
@@ -34,13 +37,7 @@ abstract class AbstractPointNavigationApp extends AbstractApp implements CacheNa
@Override
public void navigate(Activity activity, Waypoint waypoint) {
- final Geopoint coords = waypoint.getCoords();
- if (coords != null) {
- navigate(activity, coords);
- } else {
- ActivityMixin.showToast(activity, activity.getResources().getString(R.string.err_nav_no_coordinates));
- }
- navigate(activity, waypoint.getCoords());
+ navigateWithNullCheck(activity, waypoint.getCoords());
}
@Override
diff --git a/main/src/cgeo/geocaching/connector/ConnectorFactory.java b/main/src/cgeo/geocaching/connector/ConnectorFactory.java
index 3fdc11b..83f8142 100644
--- a/main/src/cgeo/geocaching/connector/ConnectorFactory.java
+++ b/main/src/cgeo/geocaching/connector/ConnectorFactory.java
@@ -21,6 +21,7 @@ import cgeo.geocaching.connector.trackable.UnknownTrackableConnector;
import cgeo.geocaching.geopoint.Viewport;
import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jdt.annotation.NonNull;
import java.util.ArrayList;
import java.util.List;
@@ -50,7 +51,7 @@ public final class ConnectorFactory {
UNKNOWN_CONNECTOR // the unknown connector MUST be the last one
};
- public static final UnknownTrackableConnector UNKNOWN_TRACKABLE_CONNECTOR = new UnknownTrackableConnector();
+ @NonNull public static final UnknownTrackableConnector UNKNOWN_TRACKABLE_CONNECTOR = new UnknownTrackableConnector();
private static final TrackableConnector[] TRACKABLE_CONNECTORS = new TrackableConnector[] {
new GeokretyConnector(), // GK must be first, as it overlaps with the secret codes of travel bugs
TravelBugConnector.getInstance(),
@@ -133,6 +134,7 @@ public final class ConnectorFactory {
return getTrackableConnector(trackable.getGeocode());
}
+ @NonNull
public static TrackableConnector getTrackableConnector(String geocode) {
for (final TrackableConnector connector : TRACKABLE_CONNECTORS) {
if (connector.canHandleTrackable(geocode)) {
@@ -187,4 +189,17 @@ public final class ConnectorFactory {
return TRACKABLE_CONNECTORS;
}
+ public static String getTrackableFromURL(final String url) {
+ if (url == null) {
+ return null;
+ }
+ for (final TrackableConnector connector : TRACKABLE_CONNECTORS) {
+ final String geocode = connector.getTrackableCodeFromUrl(url);
+ if (StringUtils.isNotBlank(geocode)) {
+ return geocode;
+ }
+ }
+ return null;
+ }
+
}
diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java
index 2be811a..4f5d293 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCParser.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java
@@ -56,6 +56,7 @@ import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
+import java.util.Comparator;
import java.util.Date;
import java.util.EnumSet;
import java.util.GregorianCalendar;
@@ -1027,6 +1028,15 @@ public abstract class GCParser {
list.add(pqList);
}
+ // just in case, lets sort the resulting list
+ Collections.sort(list, new Comparator<PocketQueryList>() {
+
+ @Override
+ public int compare(PocketQueryList left, PocketQueryList right) {
+ return String.CASE_INSENSITIVE_ORDER.compare(left.getName(), right.getName());
+ }
+ });
+
return list;
}
@@ -1694,11 +1704,12 @@ public abstract class GCParser {
// better to integrate those coordinates into the text rather than not
// display them at all.
final String latLon = entry.getString("LatLonString");
+ final String logText = (StringUtils.isEmpty(latLon) ? "" : (latLon + "<br/><br/>")) + TextUtils.removeControlCharacters(entry.getString("LogText"));
final LogEntry logDone = new LogEntry(
- entry.getString("UserName"),
+ TextUtils.removeControlCharacters(entry.getString("UserName")),
date,
LogType.getByIconName(logIconName),
- (StringUtils.isEmpty(latLon) ? "" : (latLon + "<br/><br/>")) + entry.getString("LogText"));
+ logText);
logDone.found = entry.getInt("GeocacheFindCount");
logDone.friend = friends;
@@ -1706,7 +1717,7 @@ public abstract class GCParser {
for (int i = 0; i < images.length(); i++) {
final JSONObject image = images.getJSONObject(i);
final String url = "http://img.geocaching.com/cache/log/large/" + image.getString("FileName");
- final String title = image.getString("Name");
+ final String title = TextUtils.removeControlCharacters(image.getString("Name"));
final Image logImage = new Image(url, title);
logDone.addLogImage(logImage);
}
diff --git a/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java b/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java
index cd32d87..f251efe 100644
--- a/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java
+++ b/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java
@@ -1,5 +1,6 @@
package cgeo.geocaching.connector.trackable;
+import org.eclipse.jdt.annotation.NonNull;
public abstract class AbstractTrackableConnector implements TrackableConnector {
@@ -7,4 +8,9 @@ public abstract class AbstractTrackableConnector implements TrackableConnector {
public boolean isLoggable() {
return false;
}
+
+ @Override
+ public String getTrackableCodeFromUrl(@NonNull String url) {
+ return null;
+ }
}
diff --git a/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java b/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java
index c09dc07..d1dfd7c 100644
--- a/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java
+++ b/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java
@@ -2,6 +2,8 @@ package cgeo.geocaching.connector.trackable;
import cgeo.geocaching.Trackable;
+import org.eclipse.jdt.annotation.NonNull;
+
/**
* Methods to be implemented by any connector for handling trackables
*
@@ -16,4 +18,6 @@ public interface TrackableConnector {
public Trackable searchTrackable(String geocode, String guid, String id);
+ public String getTrackableCodeFromUrl(final @NonNull String url);
+
}
diff --git a/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java b/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java
index 0dac6cc..72af0bb 100644
--- a/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java
+++ b/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java
@@ -3,6 +3,9 @@ package cgeo.geocaching.connector.trackable;
import cgeo.geocaching.Trackable;
import cgeo.geocaching.connector.gc.GCParser;
+import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jdt.annotation.NonNull;
+
import java.util.regex.Pattern;
public class TravelBugConnector extends AbstractTrackableConnector {
@@ -47,4 +50,8 @@ public class TravelBugConnector extends AbstractTrackableConnector {
return Holder.INSTANCE;
}
+ @Override
+ public String getTrackableCodeFromUrl(@NonNull String url) {
+ return StringUtils.substringAfterLast(url, "?tracker=");
+ }
} \ No newline at end of file
diff --git a/main/src/cgeo/geocaching/export/GpxSerializer.java b/main/src/cgeo/geocaching/export/GpxSerializer.java
index da179da..3ff8879 100644
--- a/main/src/cgeo/geocaching/export/GpxSerializer.java
+++ b/main/src/cgeo/geocaching/export/GpxSerializer.java
@@ -3,6 +3,7 @@ package cgeo.geocaching.export;
import cgeo.geocaching.DataStore;
import cgeo.geocaching.Geocache;
import cgeo.geocaching.LogEntry;
+import cgeo.geocaching.Trackable;
import cgeo.geocaching.Waypoint;
import cgeo.geocaching.enumerations.CacheAttribute;
import cgeo.geocaching.enumerations.LoadFlags;
@@ -11,6 +12,7 @@ import cgeo.geocaching.utils.TextUtils;
import cgeo.geocaching.utils.XmlUtils;
import cgeo.org.kxml2.io.KXmlSerializer;
+import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.CharEncoding;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;
@@ -103,7 +105,7 @@ public final class GpxSerializer {
gpx.startTag(PREFIX_GROUNDSPEAK, "cache");
gpx.attribute("", "id", cache.getCacheId());
gpx.attribute("", "available", !cache.isDisabled() ? "True" : "False");
- gpx.attribute("", "archives", cache.isArchived() ? "True" : "False");
+ gpx.attribute("", "archived", cache.isArchived() ? "True" : "False");
XmlUtils.multipleTexts(gpx, PREFIX_GROUNDSPEAK,
"name", cache.getName(),
@@ -130,6 +132,7 @@ public final class GpxSerializer {
gpx.endTag(PREFIX_GROUNDSPEAK, "long_description");
writeLogs(cache);
+ writeTravelBugs(cache);
gpx.endTag(PREFIX_GROUNDSPEAK, "cache");
gpx.endTag(PREFIX_GPX, "wpt");
@@ -229,6 +232,26 @@ public final class GpxSerializer {
gpx.endTag(PREFIX_GROUNDSPEAK, "logs");
}
+ private void writeTravelBugs(final Geocache cache) throws IOException {
+ List<Trackable> inventory = cache.getInventory();
+ if (CollectionUtils.isEmpty(inventory)) {
+ return;
+ }
+ gpx.startTag(PREFIX_GROUNDSPEAK, "travelbugs");
+
+ for (final Trackable trackable : inventory) {
+ gpx.startTag(PREFIX_GROUNDSPEAK, "travelbug");
+
+ // in most cases the geocode will be empty (only the guid is known). those travel bugs cannot be imported again!
+ gpx.attribute("", "ref", trackable.getGeocode());
+ XmlUtils.simpleText(gpx, PREFIX_GROUNDSPEAK, "name", trackable.getName());
+
+ gpx.endTag(PREFIX_GROUNDSPEAK, "travelbug");
+ }
+
+ gpx.endTag(PREFIX_GROUNDSPEAK, "travelbugs");
+ }
+
private void writeAttributes(final Geocache cache) throws IOException {
if (cache.getAttributes().isEmpty()) {
return;
diff --git a/main/src/cgeo/geocaching/filter/FilterUserInterface.java b/main/src/cgeo/geocaching/filter/FilterUserInterface.java
index 8ff700a..d77341b 100644
--- a/main/src/cgeo/geocaching/filter/FilterUserInterface.java
+++ b/main/src/cgeo/geocaching/filter/FilterUserInterface.java
@@ -56,6 +56,9 @@ public final class FilterUserInterface {
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);
// sort by localized names
Collections.sort(registry, new Comparator<FactoryEntry>() {
diff --git a/main/src/cgeo/geocaching/filter/PersonalNoteFilter.java b/main/src/cgeo/geocaching/filter/PersonalNoteFilter.java
new file mode 100644
index 0000000..15d262f
--- /dev/null
+++ b/main/src/cgeo/geocaching/filter/PersonalNoteFilter.java
@@ -0,0 +1,28 @@
+package cgeo.geocaching.filter;
+
+import cgeo.geocaching.CgeoApplication;
+import cgeo.geocaching.Geocache;
+import cgeo.geocaching.R;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Collections;
+import java.util.List;
+
+public class PersonalNoteFilter extends AbstractFilter implements IFilterFactory {
+
+ protected PersonalNoteFilter() {
+ super(CgeoApplication.getInstance().getString(R.string.caches_filter_personal_note));
+ }
+
+ @Override
+ public boolean accepts(Geocache cache) {
+ return StringUtils.isNotBlank(cache.getPersonalNote());
+ }
+
+ @Override
+ 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
new file mode 100644
index 0000000..d4f54ef
--- /dev/null
+++ b/main/src/cgeo/geocaching/filter/PopularityFilter.java
@@ -0,0 +1,43 @@
+package cgeo.geocaching.filter;
+
+import cgeo.geocaching.CgeoApplication;
+import cgeo.geocaching.Geocache;
+import cgeo.geocaching.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class PopularityFilter extends AbstractFilter {
+ private final int minFavorites;
+ private final int maxFavorites;
+
+ public PopularityFilter(String name, final int minFavorites, final int maxFavorites) {
+ super(name);
+ this.minFavorites = minFavorites;
+ this.maxFavorites = maxFavorites;
+ }
+
+ @Override
+ public boolean accepts(final Geocache cache) {
+ return (cache.getFavoritePoints() > minFavorites) && (cache.getFavoritePoints() <= maxFavorites);
+ }
+
+ public static class Factory implements IFilterFactory {
+
+ private static final int[] FAVORITES = { 10, 20, 50, 100, 200, 500 };
+
+ @Override
+ public List<IFilter> getFilters() {
+ final List<IFilter> filters = new ArrayList<IFilter>(FAVORITES.length);
+ for (int i = 0; i < FAVORITES.length; i++) {
+ final int minRange = FAVORITES[i];
+ final int maxRange = Integer.MAX_VALUE;
+ final String range = "> " + minRange;
+ final String name = CgeoApplication.getInstance().getResources().getQuantityString(R.plurals.favorite_points, minRange, range);
+ filters.add(new PopularityFilter(name, minRange, maxRange));
+ }
+ return filters;
+ }
+
+ }
+}
diff --git a/main/src/cgeo/geocaching/filter/PopularityRatioFilter.java b/main/src/cgeo/geocaching/filter/PopularityRatioFilter.java
new file mode 100644
index 0000000..2d7207a
--- /dev/null
+++ b/main/src/cgeo/geocaching/filter/PopularityRatioFilter.java
@@ -0,0 +1,73 @@
+package cgeo.geocaching.filter;
+
+import cgeo.geocaching.CgeoApplication;
+import cgeo.geocaching.DataStore;
+import cgeo.geocaching.Geocache;
+import cgeo.geocaching.R;
+import cgeo.geocaching.enumerations.LogType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * filters caches by popularity ratio (favorites per find in %).
+ */
+class PopularityRatioFilter extends AbstractFilter {
+ private final int minRatio;
+ private final int maxRatio;
+
+ public PopularityRatioFilter(String name, final int minRatio, final int maxRatio) {
+ super(name);
+ this.minRatio = minRatio;
+ this.maxRatio = maxRatio;
+ }
+
+ @Override
+ public boolean accepts(final Geocache cache) {
+
+ int finds;
+ int favorites;
+ float ratio;
+
+ finds = getFindsCount(cache);
+
+ // prevent division by zero
+ if (finds == 0) {
+ return false;
+ }
+
+ favorites = cache.getFavoritePoints();
+ ratio = ((float) favorites / (float) finds) * 100.0f;
+
+ return (ratio > minRatio) && (ratio <= maxRatio);
+ }
+
+ private static int getFindsCount(Geocache cache) {
+ if (cache.getLogCounts().isEmpty()) {
+ cache.setLogCounts(DataStore.loadLogCounts(cache.getGeocode()));
+ }
+ Integer logged = cache.getLogCounts().get(LogType.FOUND_IT);
+ if (logged != null) {
+ return logged;
+ }
+ return 0;
+ }
+
+ public static class Factory implements IFilterFactory {
+
+ private static final int[] RATIOS = { 10, 20, 30, 40, 50, 75 };
+
+ @Override
+ public List<IFilter> getFilters() {
+ final List<IFilter> filters = new ArrayList<IFilter>(RATIOS.length);
+ for (int i = 0; i < RATIOS.length; i++) {
+ final int minRange = RATIOS[i];
+ final int maxRange = Integer.MAX_VALUE;
+ final String name = "> " + minRange + " " + CgeoApplication.getInstance().getResources().getString(R.string.percent_favorite_points);
+ filters.add(new PopularityRatioFilter(name, minRange, maxRange));
+ }
+ return filters;
+ }
+
+ }
+}
diff --git a/main/src/cgeo/geocaching/filter/StateFilter.java b/main/src/cgeo/geocaching/filter/StateFilter.java
index d5b3027..f452259 100644
--- a/main/src/cgeo/geocaching/filter/StateFilter.java
+++ b/main/src/cgeo/geocaching/filter/StateFilter.java
@@ -127,7 +127,7 @@ abstract class StateFilter extends AbstractFilter {
@Override
public int compare(final StateFilter filter1, final StateFilter filter2) {
- return filter1.getName().compareToIgnoreCase(filter2.getName());
+ return String.CASE_INSENSITIVE_ORDER.compare(filter1.getName(), filter2.getName());
}
});
diff --git a/main/src/cgeo/geocaching/sorting/PopularityRatioComparator.java b/main/src/cgeo/geocaching/sorting/PopularityRatioComparator.java
index fab7bb1..b5edf17 100644
--- a/main/src/cgeo/geocaching/sorting/PopularityRatioComparator.java
+++ b/main/src/cgeo/geocaching/sorting/PopularityRatioComparator.java
@@ -9,7 +9,6 @@ import cgeo.geocaching.enumerations.LogType;
/**
* sorts caches by popularity ratio (favorites per find in %).
- * only caches with 10 finds and more are counted to obtain meaningful statistics
*/
public class PopularityRatioComparator extends AbstractCacheComparator {
@@ -27,10 +26,10 @@ public class PopularityRatioComparator extends AbstractCacheComparator {
int finds1 = getFindsCount(cache1);
int finds2 = getFindsCount(cache2);
- if (finds1 != 0 && finds1 > 9) {
+ if (finds1 != 0) {
ratio1 = (((float) cache1.getFavoritePoints()) / ((float) finds1));
}
- if (finds2 != 0 && finds2 > 9) {
+ if (finds2 != 0) {
ratio2 = (((float) cache2.getFavoritePoints()) / ((float) finds2));
}
diff --git a/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java b/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java
index 5db562e..f1cee05 100644
--- a/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java
+++ b/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java
@@ -22,6 +22,7 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
public final class CacheDetailsCreator {
@@ -179,12 +180,17 @@ public final class CacheDetailsCreator {
}
public void addEventDate(@NonNull Geocache cache) {
- if (cache.isEventCache() && cache.getHiddenDate() != null) {
- final long time = cache.getHiddenDate().getTime();
- if (time > 0) {
- final String dateString = DateUtils.formatDateTime(CgeoApplication.getInstance().getBaseContext(), time, DateUtils.FORMAT_SHOW_WEEKDAY) + ", " + Formatter.formatFullDate(time);
- add(R.string.cache_event, dateString);
- }
+ if (!cache.isEventCache()) {
+ return;
+ }
+ final Date hiddenDate = cache.getHiddenDate();
+ if (hiddenDate == null) {
+ return;
+ }
+ final long time = hiddenDate.getTime();
+ if (time > 0) {
+ final String dateString = DateUtils.formatDateTime(CgeoApplication.getInstance().getBaseContext(), time, DateUtils.FORMAT_SHOW_WEEKDAY) + ", " + Formatter.formatFullDate(time);
+ add(R.string.cache_event, dateString);
}
}
}
diff --git a/main/src/cgeo/geocaching/ui/CacheListAdapter.java b/main/src/cgeo/geocaching/ui/CacheListAdapter.java
index 56cc60a..9646c6a 100644
--- a/main/src/cgeo/geocaching/ui/CacheListAdapter.java
+++ b/main/src/cgeo/geocaching/ui/CacheListAdapter.java
@@ -400,7 +400,7 @@ public class CacheListAdapter extends ArrayAdapter<Geocache> {
}
Spannable spannable = null;
- if (cache.isDisabled() || cache.isArchived() || isPastEvent(cache)) { // strike
+ if (cache.isDisabled() || cache.isArchived() || DateUtils.isPastEvent(cache)) { // strike
spannable = Spannable.Factory.getInstance().newSpannable(cache.getName());
spannable.setSpan(new StrikethroughSpan(), 0, spannable.toString().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
@@ -489,10 +489,6 @@ public class CacheListAdapter extends ArrayAdapter<Geocache> {
return v;
}
- private static boolean isPastEvent(final Geocache cache) {
- return cache.isEventCache() && DateUtils.daysSince(cache.getHiddenDate().getTime()) > 0;
- }
-
private static Drawable getCacheIcon(Geocache cache) {
int hashCode = getIconHashCode(cache.getType(), cache.hasUserModifiedCoords() || cache.hasFinalDefined());
final Drawable drawable = gcIconDrawables.get(hashCode);
diff --git a/main/src/cgeo/geocaching/ui/Formatter.java b/main/src/cgeo/geocaching/ui/Formatter.java
index 49c7a50..9242b9a 100644
--- a/main/src/cgeo/geocaching/ui/Formatter.java
+++ b/main/src/cgeo/geocaching/ui/Formatter.java
@@ -15,6 +15,7 @@ import android.text.format.DateUtils;
import java.text.DateFormat;
import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
public abstract class Formatter {
@@ -110,7 +111,7 @@ public abstract class Formatter {
/**
* Generate a numeric date and time string according to system-wide settings (locale,
* date format) such as "7 september at 12:35".
- *
+ *
* @param date
* milliseconds since the epoch
* @return the formatted string
@@ -153,8 +154,11 @@ public abstract class Formatter {
// don't show "not chosen" for events and virtuals, that should be the normal case
if (cache.getSize() != CacheSize.UNKNOWN && cache.showSize()) {
infos.add(cache.getSize().getL10n());
- } else if (cache.isEventCache() && cache.getHiddenDate() != null) {
- infos.add(Formatter.formatShortDate(cache.getHiddenDate().getTime()));
+ } else if (cache.isEventCache()) {
+ final Date hiddenDate = cache.getHiddenDate();
+ if (hiddenDate != null) {
+ infos.add(Formatter.formatShortDate(hiddenDate.getTime()));
+ }
}
}
diff --git a/main/src/cgeo/geocaching/utils/DateUtils.java b/main/src/cgeo/geocaching/utils/DateUtils.java
index b148979..9aa4222 100644
--- a/main/src/cgeo/geocaching/utils/DateUtils.java
+++ b/main/src/cgeo/geocaching/utils/DateUtils.java
@@ -1,6 +1,9 @@
package cgeo.geocaching.utils;
+import cgeo.geocaching.Geocache;
+
import java.util.Calendar;
+import java.util.Date;
public final class DateUtils {
@@ -20,4 +23,13 @@ public final class DateUtils {
today.set(Calendar.HOUR_OF_DAY, 0);
return (int) Math.round((today.getTimeInMillis() - logDate.getTimeInMillis()) / 86400000d);
}
+
+ public static boolean isPastEvent(final Geocache cache) {
+ if (!cache.isEventCache()) {
+ return false;
+ }
+ final Date hiddenDate = cache.getHiddenDate();
+ return hiddenDate != null && DateUtils.daysSince(hiddenDate.getTime()) > 0;
+ }
+
}
diff --git a/main/src/cgeo/geocaching/utils/TextUtils.java b/main/src/cgeo/geocaching/utils/TextUtils.java
index 302a65d..14caf1d 100644
--- a/main/src/cgeo/geocaching/utils/TextUtils.java
+++ b/main/src/cgeo/geocaching/utils/TextUtils.java
@@ -153,4 +153,16 @@ public final class TextUtils {
return str.indexOf('<') != -1 || str.indexOf('&') != -1;
}
+ /**
+ * Remove all control characters (which are not valid in XML or HTML), as those should not appear in cache texts
+ * anyway
+ *
+ * @param input
+ * @return
+ */
+ public static String removeControlCharacters(final String input) {
+ Matcher remover = PATTERN_REMOVE_NONPRINTABLE.matcher(input);
+ return remover.replaceAll(" ").trim();
+ }
+
}
diff --git a/tests/src/cgeo/geocaching/GeocacheTest.java b/tests/src/cgeo/geocaching/GeocacheTest.java
index f7e4e84..5d9a31c 100644
--- a/tests/src/cgeo/geocaching/GeocacheTest.java
+++ b/tests/src/cgeo/geocaching/GeocacheTest.java
@@ -100,6 +100,11 @@ public class GeocacheTest extends CGeoTestCase {
stored.setType(CacheType.TRADITIONAL);
stored.setCoords(new Geopoint(40.0, 8.0));
stored.setDescription("Test1");
+ ArrayList<String> attributes = new ArrayList<String>(1);
+ attributes.add("TestAttribute");
+ stored.setAttributes(attributes);
+ stored.setShortDescription("Short");
+ stored.setHint("Hint");
Geocache download = new Geocache();
download.setGeocode("GC12345");
@@ -117,6 +122,9 @@ public class GeocacheTest extends CGeoTestCase {
assertEquals("Longitude not merged correctly", 9.0, download.getCoords().getLongitude(), 0.1);
assertEquals("Latitude not merged correctly", 41.0, download.getCoords().getLatitude(), 0.1);
assertEquals("Description not merged correctly", "Test2", download.getDescription());
+ assertEquals("ShortDescription not merged correctly", "", download.getShortDescription());
+ assertEquals("Attributes not merged correctly", new ArrayList<String>(0), download.getAttributes());
+ assertEquals("Hint not merged correctly", "", download.getHint());
}
public static void testMergeLivemapStored() {
diff --git a/tests/src/cgeo/geocaching/WaypointTest.java b/tests/src/cgeo/geocaching/WaypointTest.java
index dc2853a..3ddc32c 100644
--- a/tests/src/cgeo/geocaching/WaypointTest.java
+++ b/tests/src/cgeo/geocaching/WaypointTest.java
@@ -14,23 +14,27 @@ public class WaypointTest extends AndroidTestCase {
final Waypoint own = new Waypoint("own", WaypointType.OWN, true);
final Waypoint parking = new Waypoint("parking", WaypointType.PARKING, false);
- assertTrue(trailhead.compareTo(puzzle) < 0);
- assertTrue(trailhead.compareTo(stage) < 0);
- assertTrue(trailhead.compareTo(cache) < 0);
-
- assertTrue(stage.compareTo(cache) < 0);
- assertTrue(puzzle.compareTo(cache) < 0);
-
- assertTrue(trailhead.compareTo(own) < 0);
- assertTrue(puzzle.compareTo(own) < 0);
- assertTrue(stage.compareTo(own) < 0);
- assertTrue(cache.compareTo(own) < 0);
-
- assertTrue(parking.compareTo(puzzle) < 0);
- assertTrue(parking.compareTo(stage) < 0);
- assertTrue(parking.compareTo(cache) < 0);
- assertTrue(parking.compareTo(own) < 0);
- assertTrue(parking.compareTo(trailhead) < 0);
+ assertOrdered(trailhead, puzzle);
+ assertOrdered(trailhead, stage);
+ assertOrdered(trailhead, cache);
+
+ assertOrdered(stage, cache);
+ assertOrdered(puzzle, cache);
+
+ assertOrdered(trailhead, own);
+ assertOrdered(puzzle, own);
+ assertOrdered(stage, own);
+ assertOrdered(cache, own);
+
+ assertOrdered(parking, puzzle);
+ assertOrdered(parking, stage);
+ assertOrdered(parking, cache);
+ assertOrdered(parking, own);
+ assertOrdered(parking, trailhead);
+ }
+
+ private static void assertOrdered(Waypoint first, Waypoint second) {
+ assertTrue(Waypoint.WAYPOINT_COMPARATOR.compare(first, second) < 0);
}
public static void testGeocode() {