aboutsummaryrefslogtreecommitdiffstats
path: root/main
diff options
context:
space:
mode:
Diffstat (limited to 'main')
-rw-r--r--main/AndroidManifest.xml63
-rw-r--r--main/libs/android-support-v4.jarbin621451 -> 627582 bytes
-rw-r--r--main/libs/rxjava-android-0.15.1.jarbin0 -> 16776 bytes
-rw-r--r--main/libs/rxjava-core-0.15.1.jarbin0 -> 496439 bytes
-rw-r--r--main/proguard-project.txt5
-rw-r--r--main/res/layout/main_activity.xml17
-rw-r--r--main/res/values-de/strings.xml2
-rw-r--r--main/res/values/changelog_master.xml7
-rw-r--r--main/res/values/preference_keys.xml1
-rw-r--r--main/res/values/strings.xml3
-rw-r--r--main/res/xml/preferences.xml9
-rw-r--r--main/src/cgeo/geocaching/AbstractPopupActivity.java2
-rw-r--r--main/src/cgeo/geocaching/CacheDetailActivity.java28
-rw-r--r--main/src/cgeo/geocaching/CacheListActivity.java48
-rw-r--r--main/src/cgeo/geocaching/CgeoApplication.java66
-rw-r--r--main/src/cgeo/geocaching/CompassActivity.java5
-rw-r--r--main/src/cgeo/geocaching/DataStore.java45
-rw-r--r--main/src/cgeo/geocaching/DirectionProvider.java90
-rw-r--r--main/src/cgeo/geocaching/GeoDataProvider.java146
-rw-r--r--main/src/cgeo/geocaching/MainActivity.java178
-rw-r--r--main/src/cgeo/geocaching/SearchResult.java2
-rw-r--r--main/src/cgeo/geocaching/StatusFragment.java129
-rw-r--r--main/src/cgeo/geocaching/TrackableActivity.java2
-rw-r--r--main/src/cgeo/geocaching/activity/AbstractListActivity.java23
-rw-r--r--main/src/cgeo/geocaching/connector/ConnectorFactory.java40
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCConnector.java19
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCLogin.java12
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCMap.java2
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCParser.java36
-rw-r--r--main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java4
-rw-r--r--main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java27
-rw-r--r--main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java8
-rw-r--r--main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java4
-rw-r--r--main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java17
-rw-r--r--main/src/cgeo/geocaching/maps/CGeoMap.java4
-rw-r--r--main/src/cgeo/geocaching/network/StatusUpdater.java57
-rw-r--r--main/src/cgeo/geocaching/settings/Settings.java12
-rw-r--r--main/src/cgeo/geocaching/settings/SettingsActivity.java41
-rw-r--r--main/src/cgeo/geocaching/speech/SpeechService.java4
-rw-r--r--main/src/cgeo/geocaching/utils/GeoDirHandler.java107
-rw-r--r--main/src/cgeo/geocaching/utils/IObserver.java22
-rw-r--r--main/src/cgeo/geocaching/utils/ISubject.java57
-rw-r--r--main/src/cgeo/geocaching/utils/MemorySubject.java45
-rw-r--r--main/src/cgeo/geocaching/utils/Subject.java76
44 files changed, 659 insertions, 806 deletions
diff --git a/main/AndroidManifest.xml b/main/AndroidManifest.xml
index 1884a9b..6e10c96 100644
--- a/main/AndroidManifest.xml
+++ b/main/AndroidManifest.xml
@@ -308,6 +308,7 @@
android:name="cgeo.geocaching.TrackableActivity"
android:configChanges="keyboardHidden|orientation"
android:label="@string/app_name" >
+ <!-- TravelBug URL via coord.info redirection -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@@ -326,6 +327,19 @@
<category android:name="android.intent.category.BROWSABLE" />
<data
+ android:host="www.coord.info"
+ android:pathPrefix="/TB"
+ android:scheme="http" />
+ </intent-filter>
+
+ <!-- TravelBug URL tracking page -->
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+
+ <data
android:host="geocaching.com"
android:pathPrefix="/track/details.aspx"
android:scheme="http" />
@@ -341,6 +355,55 @@
android:pathPrefix="/track/details.aspx"
android:scheme="http" />
</intent-filter>
+
+ <!-- GeoKrety URLs -->
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+
+ <data
+ android:host="geokrety.org"
+ android:pathPrefix="/konkret.php"
+ android:scheme="http" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+
+ <data
+ android:host="www.geokrety.org"
+ android:pathPrefix="/konkret.php"
+ android:scheme="http" />
+ </intent-filter>
+
+ <!-- Geokrety QR code URLs, not yet implemented
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+
+ <data
+ android:host="geokrety.org"
+ android:pathPrefix="/m/qr.php"
+ android:scheme="http" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+
+ <data
+ android:host="www.geokrety.org"
+ android:pathPrefix="/m/qr.php"
+ android:scheme="http" />
+ </intent-filter>
+ -->
</activity>
<activity
android:name=".CompassActivity"
diff --git a/main/libs/android-support-v4.jar b/main/libs/android-support-v4.jar
index 9056828..96644ed 100644
--- a/main/libs/android-support-v4.jar
+++ b/main/libs/android-support-v4.jar
Binary files differ
diff --git a/main/libs/rxjava-android-0.15.1.jar b/main/libs/rxjava-android-0.15.1.jar
new file mode 100644
index 0000000..4e50a44
--- /dev/null
+++ b/main/libs/rxjava-android-0.15.1.jar
Binary files differ
diff --git a/main/libs/rxjava-core-0.15.1.jar b/main/libs/rxjava-core-0.15.1.jar
new file mode 100644
index 0000000..5759337
--- /dev/null
+++ b/main/libs/rxjava-core-0.15.1.jar
Binary files differ
diff --git a/main/proguard-project.txt b/main/proguard-project.txt
index 03ef6b3..eae0a42 100644
--- a/main/proguard-project.txt
+++ b/main/proguard-project.txt
@@ -13,6 +13,11 @@
# apache.commons.collections has some bean related collections, which are undefined in Android
-dontwarn java.beans.*
+# rxjava includes references to the test frameworks within their class files
+-dontwarn org.mockito.**
+-dontwarn org.junit.**
+-dontwarn org.robolectric.**
+
#-dontnote org.apache.commons.logging.**
-keep public class cgeo.geocaching.*
diff --git a/main/res/layout/main_activity.xml b/main/res/layout/main_activity.xml
index ef86954..9e124a1 100644
--- a/main/res/layout/main_activity.xml
+++ b/main/res/layout/main_activity.xml
@@ -35,8 +35,7 @@
android:gravity="center"
android:orientation="vertical" >
- <LinearLayout
- style="@style/icon_mainscreen_row" >
+ <LinearLayout style="@style/icon_mainscreen_row" >
<LinearLayout
style="@style/icon_mainscreen_cell"
@@ -52,8 +51,7 @@
android:text="@string/live_map_button" />
</LinearLayout>
- <LinearLayout
- style="@style/icon_mainscreen_cell" >
+ <LinearLayout style="@style/icon_mainscreen_cell" >
<ImageView
android:id="@+id/nearest"
@@ -76,8 +74,7 @@
style="@style/icon_mainscreen_count"
android:textIsSelectable="false" />
- <LinearLayout
- style="@style/icon_mainscreen_cell_counter" >
+ <LinearLayout style="@style/icon_mainscreen_cell_counter" >
<ImageView
android:id="@+id/search_offline"
@@ -91,8 +88,7 @@
</RelativeLayout>
</LinearLayout>
- <LinearLayout
- style="@style/icon_mainscreen_row" >
+ <LinearLayout style="@style/icon_mainscreen_row" >
<LinearLayout
style="@style/icon_mainscreen_cell"
@@ -146,7 +142,6 @@
android:layout_alignParentBottom="true"
android:layout_marginLeft="6dip"
android:layout_marginRight="6dip"
- android:onClick="cgeoNavSettings"
android:orientation="vertical" >
<LinearLayout
@@ -158,11 +153,13 @@
<TextView
android:id="@+id/nav_location"
style="@style/location_current"
+ android:onClick="cgeoNavSettings"
android:text="@string/loc_trying" />
<RelativeLayout
android:layout_width="fill_parent"
- android:layout_height="wrap_content" >
+ android:layout_height="wrap_content"
+ android:onClick="cgeoNavSettings" >
<TextView
android:id="@+id/nav_type"
diff --git a/main/res/values-de/strings.xml b/main/res/values-de/strings.xml
index 927cce0..e36a62e 100644
--- a/main/res/values-de/strings.xml
+++ b/main/res/values-de/strings.xml
@@ -806,7 +806,7 @@
<string name="user_menu_view_found">Gefundene Caches</string>
<string name="user_menu_open_browser">Profil im Browser öffnen</string>
<string name="user_menu_send_message">Nachricht senden</string>
- <string name="user_menu_open_contact">Öffne Adressbuch</string>
+ <string name="user_menu_open_contact">Öffne Kontakt</string>
<string name="navigation">Navigation</string>
<string name="compass_title">Kompass</string>
<string name="use_gps">Nur GPS nutzen</string>
diff --git a/main/res/values/changelog_master.xml b/main/res/values/changelog_master.xml
index 1e3c3d4..8ebc0f7 100644
--- a/main/res/values/changelog_master.xml
+++ b/main/res/values/changelog_master.xml
@@ -2,5 +2,12 @@
<resources>
<!-- changelog for the master branch -->
<string name="changelog_master" translatable="false">
+ <b>Next feature release</b>\n
+ <b>New Features:</b>\n
+ · Parallel loading from different platforms to speed up live map\n
+ <b>Bugfixing:</b>\n
+ · \n
+ \n
+ \n
</string>
</resources>
diff --git a/main/res/values/preference_keys.xml b/main/res/values/preference_keys.xml
index 0e4675d..ad08ec9 100644
--- a/main/res/values/preference_keys.xml
+++ b/main/res/values/preference_keys.xml
@@ -60,6 +60,7 @@
<string name="pref_fakekey_preference_backup_info">fakekey_preference_backup_info</string>
<string name="pref_fakekey_preference_backup">fakekey_preference_backup</string>
<string name="pref_fakekey_preference_restore">fakekey_preference_restore</string>
+ <string name="pref_fakekey_preference_maintenance_directories">pref_fakekey_preference_maintenance_directories</string>
<string name="pref_dbonsdcard">dbonsdcard</string>
<string name="pref_debug">debug</string>
<!-- preferences used internally -->
diff --git a/main/res/values/strings.xml b/main/res/values/strings.xml
index 7b40811..75b0ae6 100644
--- a/main/res/values/strings.xml
+++ b/main/res/values/strings.xml
@@ -525,6 +525,9 @@
<string name="init_use_native_ua">Android browser</string>
<string name="init_summary_use_native_ua">Identify as Android browser. Solves login problems when using certain network providers.</string>
<string name="init_rendertheme_folder">Map Themes Directory</string>
+ <string name="init_maintenance">Maintenance</string>
+ <string name="init_maintenance_directories_note">c:geo stores images, log images and other files related to a cache in a separate directory. In some cases (like importing/exporting the database) this directory may contain outdated files, which can be deleted here.</string>
+ <string name="init_maintenance_directories">Delete orphaned files</string>
<string name="settings_open_website">Open website</string>
<string name="settings_settings">Settings</string>
<string name="settings_information">Information</string>
diff --git a/main/res/xml/preferences.xml b/main/res/xml/preferences.xml
index de0914e..2cef219 100644
--- a/main/res/xml/preferences.xml
+++ b/main/res/xml/preferences.xml
@@ -609,6 +609,15 @@
android:key="@string/pref_dbonsdcard"
android:title="@string/init_dbonsdcard" />
</PreferenceCategory>
+ <PreferenceCategory android:title="@string/init_maintenance" >
+ <cgeo.geocaching.settings.TextPreference
+ android:layout="@layout/text_preference"
+ android:text="@string/init_maintenance_directories_note" />
+
+ <Preference
+ android:key="@string/pref_fakekey_preference_maintenance_directories"
+ android:title="@string/init_maintenance_directories" />
+ </PreferenceCategory>
<PreferenceCategory android:title="@string/init_debug_title" >
<cgeo.geocaching.settings.TextPreference
android:layout="@layout/text_preference"
diff --git a/main/src/cgeo/geocaching/AbstractPopupActivity.java b/main/src/cgeo/geocaching/AbstractPopupActivity.java
index 5f24030..38e37da 100644
--- a/main/src/cgeo/geocaching/AbstractPopupActivity.java
+++ b/main/src/cgeo/geocaching/AbstractPopupActivity.java
@@ -40,7 +40,7 @@ public abstract class AbstractPopupActivity extends AbstractActivity implements
private final GeoDirHandler geoUpdate = new GeoDirHandler() {
@Override
- protected void updateGeoData(final IGeoData geo) {
+ public void updateGeoData(final IGeoData geo) {
try {
if (geo.getCoords() != null && cache != null && cache.getCoords() != null) {
cacheDistance.setText(Units.getDistanceFromKilometers(geo.getCoords().distanceTo(cache.getCoords())));
diff --git a/main/src/cgeo/geocaching/CacheDetailActivity.java b/main/src/cgeo/geocaching/CacheDetailActivity.java
index 4068e38..dce4a13 100644
--- a/main/src/cgeo/geocaching/CacheDetailActivity.java
+++ b/main/src/cgeo/geocaching/CacheDetailActivity.java
@@ -176,24 +176,15 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
// set title in code, as the activity needs a hard coded title due to the intent filters
setTitle(res.getString(R.string.cache));
- String geocode = null;
-
- // TODO Why can it happen that search is not null? onCreate should be called only once and it is not set before.
- if (search != null) {
- cache = search.getFirstCacheFromResult(LoadFlags.LOAD_ALL_DB_ONLY);
- if (cache != null && cache.getGeocode() != null) {
- geocode = cache.getGeocode();
- }
- }
-
// get parameters
final Bundle extras = getIntent().getExtras();
final Uri uri = getIntent().getData();
// try to get data from extras
String name = null;
+ String geocode = null;
String guid = null;
- if (geocode == null && extras != null) {
+ if (extras != null) {
geocode = extras.getString(Intents.EXTRA_GEOCODE);
name = extras.getString(Intents.EXTRA_NAME);
guid = extras.getString(Intents.EXTRA_GUID);
@@ -542,20 +533,19 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
if (UPDATE_LOAD_PROGRESS_DETAIL == msg.what && msg.obj instanceof String) {
updateStatusMsg((String) msg.obj);
} else {
- CacheDetailActivity activity = ((CacheDetailActivity) activityRef.get());
+ final CacheDetailActivity activity = ((CacheDetailActivity) activityRef.get());
if (activity == null) {
return;
}
- SearchResult search = activity.getSearch();
- if (search == null) {
+ if (activity.search == null) {
showToast(R.string.err_dwld_details_failed);
dismissProgress();
finishActivity();
return;
}
- if (search.getError() != null) {
- activity.showToast(activity.getResources().getString(R.string.err_dwld_details_failed) + " " + search.getError().getErrorString(activity.getResources()) + ".");
+ if (activity.search.getError() != null) {
+ activity.showToast(activity.getResources().getString(R.string.err_dwld_details_failed) + " " + activity.search.getError().getErrorString(activity.getResources()) + ".");
dismissProgress();
finishActivity();
return;
@@ -1368,7 +1358,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
final LinearLayout layout = (LinearLayout) view.findViewById(R.id.favpoint_box);
final boolean supportsFavoritePoints = cache.supportsFavoritePoints();
layout.setVisibility(supportsFavoritePoints ? View.VISIBLE : View.GONE);
- if (!supportsFavoritePoints || cache.isOwner() || !Settings.isPremiumMember()) {
+ if (!supportsFavoritePoints || cache.isOwner() || !Settings.isGCPremiumMember()) {
return;
}
final Button buttonAdd = (Button) view.findViewById(R.id.add_to_favpoint);
@@ -2255,10 +2245,6 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
return cache;
}
- public SearchResult getSearch() {
- return search;
- }
-
private static class StoreCacheHandler extends SimpleCancellableHandler {
public StoreCacheHandler(CacheDetailActivity activity, Progress progress) {
diff --git a/main/src/cgeo/geocaching/CacheListActivity.java b/main/src/cgeo/geocaching/CacheListActivity.java
index cc8b178..5e189b3 100644
--- a/main/src/cgeo/geocaching/CacheListActivity.java
+++ b/main/src/cgeo/geocaching/CacheListActivity.java
@@ -304,13 +304,6 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA
showProgress(false);
progress.dismiss();
-
- if (!isPaused()) {
- // If the current activity has been paused, then we do not want to fiddle with the
- // GPS and direction states. If the activity later gets resumed, its onResume()
- // function will take care of turning the GPS back on.
- startGeoAndDir();
- }
}
}
};
@@ -472,12 +465,15 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA
public void onResume() {
super.onResume();
- startGeoAndDir();
+ geoDirHandler.startGeo();
+ if (Settings.isLiveMap()) {
+ geoDirHandler.startDir();
+ }
adapter.setSelectMode(false);
setAdapterCurrentCoordinates(true);
- if (loadCachesHandler != null && search != null) {
+ if (search != null) {
replaceCacheListFromSearch();
loadCachesHandler.sendEmptyMessage(0);
}
@@ -503,8 +499,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA
@Override
public void onPause() {
- removeGeoAndDir();
-
+ geoDirHandler.stopGeoAndDir();
super.onPause();
}
@@ -742,8 +737,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA
public void run(IFilter selectedFilter) {
if (selectedFilter != null) {
setFilter(selectedFilter);
- }
- else {
+ } else {
// clear filter
setFilter(null);
}
@@ -960,7 +954,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA
boolean enableMore = (type != CacheListType.OFFLINE && cacheList.size() < MAX_LIST_ITEMS);
if (enableMore && search != null) {
final int count = search.getTotalCountGC();
- enableMore = enableMore && count > 0 && cacheList.size() < count;
+ enableMore = count > 0 && cacheList.size() < count;
}
if (enableMore) {
@@ -973,17 +967,6 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA
listFooter.setClickable(enableMore);
}
- private void startGeoAndDir() {
- geoDirHandler.startGeo();
- if (Settings.isLiveMap()) {
- geoDirHandler.startDir();
- }
- }
-
- private void removeGeoAndDir() {
- geoDirHandler.stopGeoAndDir();
- }
-
private void importGpx() {
GpxFileListActivity.startSubActivity(this, listId);
}
@@ -1002,7 +985,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA
// provided to this method as a parameter. Pull that uri using "resultData.getData()"
if (data != null) {
final Uri uri = data.getData();
- new GPXImporter(CacheListActivity.this, listId, importGpxAttachementFinishedHandler).importGPX(uri, null, getDisplayName(uri));
+ new GPXImporter(this, listId, importGpxAttachementFinishedHandler).importGPX(uri, null, getDisplayName(uri));
}
}
@@ -1146,7 +1129,6 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA
@Override
public void run() {
- removeGeoAndDir();
// First refresh caches that do not yet have static maps to get them a chance to get a copy
// before the limit expires, unless we do not want to store offline maps.
final List<Geocache> allCaches = Settings.isStoreOfflineMaps() ?
@@ -1190,7 +1172,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA
}
}
- private class LoadFromWebThread extends Thread {
+ private static class LoadFromWebThread extends Thread {
final private Handler handler;
final private int listIdLFW;
@@ -1207,9 +1189,6 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA
@Override
public void run() {
-
- removeGeoAndDir();
-
int delay = -1;
int times = 0;
@@ -1267,8 +1246,6 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA
}
handler.sendEmptyMessage(ret);
-
- startGeoAndDir();
}
}
@@ -1283,9 +1260,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA
@Override
protected Void doInBackgroundInternal(Geocache[] caches) {
- removeGeoAndDir();
DataStore.markDropped(Arrays.asList(caches));
- startGeoAndDir();
return null;
}
@@ -1670,8 +1645,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA
break;
case POCKET:
final String guid = extras.getString(Intents.EXTRA_POCKET_GUID);
- final String pocket_name = extras.getString(Intents.EXTRA_NAME);
- title = pocket_name;
+ title = extras.getString(Intents.EXTRA_NAME);
loader = new PocketGeocacheListLoader(app, guid);
break;
}
diff --git a/main/src/cgeo/geocaching/CgeoApplication.java b/main/src/cgeo/geocaching/CgeoApplication.java
index 2500d10..1cea5f1 100644
--- a/main/src/cgeo/geocaching/CgeoApplication.java
+++ b/main/src/cgeo/geocaching/CgeoApplication.java
@@ -1,10 +1,10 @@
package cgeo.geocaching;
-import cgeo.geocaching.network.StatusUpdater;
import cgeo.geocaching.ui.dialog.Dialogs;
-import cgeo.geocaching.utils.IObserver;
import cgeo.geocaching.utils.Log;
+import rx.Observable;
+
import android.app.Activity;
import android.app.Application;
import android.app.ProgressDialog;
@@ -14,12 +14,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
public class CgeoApplication extends Application {
- private volatile GeoDataProvider geo;
- private volatile DirectionProvider dir;
+ private volatile Observable<IGeoData> geo;
+ private volatile Observable<Float> dir;
private boolean forceRelog = false; // c:geo needs to log into cache providers
public boolean showLoginToast = true; //login toast shown just once.
private boolean liveMapHintShown = false; // livemap hint has been shown
- final private StatusUpdater statusUpdater = new StatusUpdater();
private static CgeoApplication instance;
public CgeoApplication() {
@@ -35,26 +34,11 @@ public class CgeoApplication extends Application {
}
@Override
- public void onCreate() {
- new Thread(statusUpdater).start();
- }
-
- @Override
public void onLowMemory() {
Log.i("Cleaning applications cache.");
DataStore.removeAllFromCache();
}
- @Override
- public void onTerminate() {
- Log.d("Terminating c:geo…");
-
- DataStore.clean();
- DataStore.closeDb();
-
- super.onTerminate();
- }
-
/**
* Move the database to/from external cgdata in a new thread,
* showing a progress window
@@ -82,29 +66,11 @@ public class CgeoApplication extends Application {
}.start();
}
- /**
- * Register an observer to receive GeoData information.
- * <br/>
- * If there is a chance that no observers are registered before this
- * method is called, it is necessary to call it from a task implementing
- * a looper interface as the data provider will use listeners that
- * require a looper thread to run.
- *
- * @param observer a geodata observer
- */
- public void addGeoObserver(final IObserver<? super IGeoData> observer) {
- currentGeoObject().addObserver(observer);
- }
-
- public void deleteGeoObserver(final IObserver<? super IGeoData> observer) {
- currentGeoObject().deleteObserver(observer);
- }
-
- private GeoDataProvider currentGeoObject() {
+ public Observable<IGeoData> currentGeoObject() {
if (geo == null) {
synchronized(this) {
if (geo == null) {
- geo = new GeoDataProvider(this);
+ geo = GeoDataProvider.create(this);
}
}
}
@@ -112,22 +78,14 @@ public class CgeoApplication extends Application {
}
public IGeoData currentGeo() {
- return currentGeoObject().getMemory();
+ return currentGeoObject().first().toBlockingObservable().single();
}
- public void addDirectionObserver(final IObserver<? super Float> observer) {
- currentDirObject().addObserver(observer);
- }
-
- public void deleteDirectionObserver(final IObserver<? super Float> observer) {
- currentDirObject().deleteObserver(observer);
- }
-
- private DirectionProvider currentDirObject() {
+ public Observable<Float> currentDirObject() {
if (dir == null) {
synchronized(this) {
if (dir == null) {
- dir = new DirectionProvider(this);
+ dir = DirectionProvider.create(this);
}
}
}
@@ -135,11 +93,7 @@ public class CgeoApplication extends Application {
}
public Float currentDirection() {
- return currentDirObject().getMemory();
- }
-
- public StatusUpdater getStatusUpdater() {
- return statusUpdater;
+ return currentDirObject().first().toBlockingObservable().single();
}
public boolean isLiveMapHintShown() {
diff --git a/main/src/cgeo/geocaching/CompassActivity.java b/main/src/cgeo/geocaching/CompassActivity.java
index 8955afd..6fc2de1 100644
--- a/main/src/cgeo/geocaching/CompassActivity.java
+++ b/main/src/cgeo/geocaching/CompassActivity.java
@@ -150,12 +150,13 @@ public class CompassActivity extends AbstractActivity {
final CgeoApplication app = CgeoApplication.getInstance();
final IGeoData geo = app.currentGeo();
if (geo != null) {
- geoDirHandler.update(geo);
+ geoDirHandler.updateGeoData(geo);
}
final Float dir = app.currentDirection();
if (dir != null) {
- geoDirHandler.update(dir);
+ geoDirHandler.updateDirection(dir);
}
+
}
@Override
diff --git a/main/src/cgeo/geocaching/DataStore.java b/main/src/cgeo/geocaching/DataStore.java
index 6da1af8..9d8a640 100644
--- a/main/src/cgeo/geocaching/DataStore.java
+++ b/main/src/cgeo/geocaching/DataStore.java
@@ -811,12 +811,19 @@ public class DataStore {
/**
* Remove obsolete cache directories in c:geo private storage.
- *
+ */
+ public static void removeObsoleteCacheDirectories() {
+ removeObsoleteCacheDirectories(database);
+ }
+
+ /**
+ * Remove obsolete cache directories in c:geo private storage.
+ *
* @param db
* the read-write database to use
*/
private static void removeObsoleteCacheDirectories(final SQLiteDatabase db) {
- final Pattern oldFilePattern = Pattern.compile("^[GC|TB|O][A-Z0-9]{4,7}$");
+ final Pattern oldFilePattern = Pattern.compile("^[GC|TB|EC|GK|O][A-Z0-9]{4,7}$");
final SQLiteStatement select = db.compileStatement("select count(*) from " + dbTableCaches + " where geocode = ?");
final File[] files = LocalStorage.getStorage().listFiles();
final ArrayList<File> toRemove = new ArrayList<File>(files.length);
@@ -2302,11 +2309,6 @@ public class DataStore {
return new SearchResult(geocodes);
}
- /** delete caches from the DB store 3 days or more before */
- public static void clean() {
- clean(false);
- }
-
/**
* Remove caches with listId = 0
*
@@ -2355,6 +2357,8 @@ public class DataStore {
cursor.close();
+ geocodes = exceptCachesWithOfflineLog(geocodes);
+
if (!geocodes.isEmpty()) {
Log.d("Database clean: removing " + geocodes.size() + " geocaches from listId=0");
removeCaches(geocodes, LoadFlags.REMOVE_ALL);
@@ -2367,6 +2371,33 @@ public class DataStore {
databaseCleaned = true;
}
+ /**
+ * remove all geocodes from the given list of geocodes where an offline log exists
+ *
+ * @param geocodes
+ * @return
+ */
+ private static Set<String> exceptCachesWithOfflineLog(Set<String> geocodes) {
+ if (geocodes.isEmpty()) {
+ return geocodes;
+ }
+
+ init();
+ final Cursor cursor = database.query(
+ dbTableLogsOffline,
+ new String[] { "geocode" },
+ null,
+ null,
+ null,
+ null,
+ null);
+
+ final List<String> geocodesWithOfflineLog = Arrays.asList(getFirstColumn(cursor));
+
+ geocodes.removeAll(geocodesWithOfflineLog);
+ return geocodes;
+ }
+
public static void removeAllFromCache() {
// clean up CacheCache
cacheCache.removeAllFromCache();
diff --git a/main/src/cgeo/geocaching/DirectionProvider.java b/main/src/cgeo/geocaching/DirectionProvider.java
index ae58fed..cc44155 100644
--- a/main/src/cgeo/geocaching/DirectionProvider.java
+++ b/main/src/cgeo/geocaching/DirectionProvider.java
@@ -1,9 +1,13 @@
package cgeo.geocaching;
import cgeo.geocaching.compatibility.Compatibility;
-import cgeo.geocaching.utils.MemorySubject;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import rx.Observable;
+import rx.Observable.OnSubscribeFunc;
+import rx.Observer;
+import rx.Subscription;
+import rx.observables.ConnectableObservable;
+import rx.subjects.BehaviorSubject;
import android.app.Activity;
import android.content.Context;
@@ -12,58 +16,61 @@ import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
-public class DirectionProvider extends MemorySubject<Float> implements SensorEventListener {
+public class DirectionProvider implements OnSubscribeFunc<Float> {
private final SensorManager sensorManager;
+ private final BehaviorSubject<Float> subject = BehaviorSubject.create(0.0f);
- // Previous values signaled to observers to avoid re-sending the same value when the
- // device doesn't change orientation. The orientation is usually given with a 1 degree
- // precision by Android, so it is not uncommon to obtain exactly the same value several
- // times.
- private float previous = -1;
-
- public DirectionProvider(final Context context) {
- sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
-
+ static public Observable<Float> create(final Context context) {
+ return new DirectionProvider((SensorManager) context.getSystemService(Context.SENSOR_SERVICE)).worker.refCount();
}
- @Override
- protected void onFirstObserver() {
- @SuppressWarnings("deprecation")
- // This will be removed when using a new location service. Until then, it is okay to be used.
- final Sensor defaultSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
- sensorManager.registerListener(this, defaultSensor, SensorManager.SENSOR_DELAY_NORMAL);
+ private DirectionProvider(final SensorManager sensorManager) {
+ this.sensorManager = sensorManager;
}
@Override
- protected void onLastObserver() {
- sensorManager.unregisterListener(this);
+ public Subscription onSubscribe(final Observer<? super Float> observer) {
+ return subject.distinctUntilChanged().subscribe(observer);
}
- @Override
- public void onAccuracyChanged(final Sensor sensor, int accuracy) {
- /*
- * There is a bug in Android, which apparently causes this method to be called every
- * time the sensor _value_ changed, even if the _accuracy_ did not change. So logging
- * this event leads to the log being flooded with multiple entries _per second_,
- * which I experienced when running cgeo in a building (with GPS and network being
- * unreliable).
- *
- * See for example https://code.google.com/p/android/issues/detail?id=14792
- */
+ private final ConnectableObservable<Float> worker = new ConnectableObservable<Float>(this) {
+ @Override
+ public Subscription connect() {
+ @SuppressWarnings("deprecation")
+ // This will be removed when using a new location service. Until then, it is okay to be used.
+ final Sensor defaultSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
+ final SensorEventListener listener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(final SensorEvent event) {
+ subject.onNext(event.values[0]);
+ }
- //Log.i(Settings.tag, "Compass' accuracy is low (" + accuracy + ")");
- }
+ @Override
+ public void onAccuracyChanged(final Sensor sensor, final int accuracy) {
+ /*
+ * There is a bug in Android, which apparently causes this method to be called every
+ * time the sensor _value_ changed, even if the _accuracy_ did not change. So logging
+ * this event leads to the log being flooded with multiple entries _per second_,
+ * which I experienced when running cgeo in a building (with GPS and network being
+ * unreliable).
+ *
+ * See for example https://code.google.com/p/android/issues/detail?id=14792
+ */
- @Override
- @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY")
- public void onSensorChanged(final SensorEvent event) {
- final float direction = event.values[0];
- if (direction != previous) {
- notifyObservers(direction);
- previous = direction;
+ //Log.i(Settings.tag, "Compass' accuracy is low (" + accuracy + ")");
+ }
+ };
+
+ sensorManager.registerListener(listener, defaultSensor, SensorManager.SENSOR_DELAY_NORMAL);
+ return new Subscription() {
+ @Override
+ public void unsubscribe() {
+ sensorManager.unregisterListener(listener);
+ }
+ };
}
- }
+ };
/**
* Take the phone rotation (through a given activity) in account and adjust the direction.
@@ -72,6 +79,7 @@ public class DirectionProvider extends MemorySubject<Float> implements SensorEve
* @param direction the unadjusted direction in degrees, in the [0, 360[ range
* @return the adjusted direction in degrees, in the [0, 360[ range
*/
+
public static float getDirectionNow(final Activity activity, final float direction) {
return Compatibility.getDirectionNow(direction, activity);
}
diff --git a/main/src/cgeo/geocaching/GeoDataProvider.java b/main/src/cgeo/geocaching/GeoDataProvider.java
index 73aefce..c61b7e7 100644
--- a/main/src/cgeo/geocaching/GeoDataProvider.java
+++ b/main/src/cgeo/geocaching/GeoDataProvider.java
@@ -3,9 +3,14 @@ package cgeo.geocaching;
import cgeo.geocaching.enumerations.LocationProviderType;
import cgeo.geocaching.geopoint.Geopoint;
import cgeo.geocaching.utils.Log;
-import cgeo.geocaching.utils.MemorySubject;
import org.apache.commons.lang3.StringUtils;
+import rx.Observable;
+import rx.Observable.OnSubscribeFunc;
+import rx.Observer;
+import rx.Subscription;
+import rx.observables.ConnectableObservable;
+import rx.subjects.BehaviorSubject;
import android.content.Context;
import android.location.GpsSatellite;
@@ -15,22 +20,14 @@ import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Provide information about the user location. This class should be instantiated only once per application.
- */
-class GeoDataProvider extends MemorySubject<IGeoData> {
+class GeoDataProvider implements OnSubscribeFunc<IGeoData> {
private static final String LAST_LOCATION_PSEUDO_PROVIDER = "last";
private final LocationManager geoManager;
- private final GpsStatus.Listener gpsStatusListener = new GpsStatusListener();
private final LocationData gpsLocation = new LocationData();
private final LocationData netLocation = new LocationData();
- private final Listener networkListener = new Listener(LocationManager.NETWORK_PROVIDER, netLocation);
- private final Listener gpsListener = new Listener(LocationManager.GPS_PROVIDER, gpsLocation);
- private final Unregisterer unregisterer = new Unregisterer();
+ private final BehaviorSubject<IGeoData> subject;
+
public boolean gpsEnabled = false;
public int satellitesVisible = 0;
public int satellitesFixed = 0;
@@ -111,51 +108,6 @@ class GeoDataProvider extends MemorySubject<IGeoData> {
}
}
- private class Unregisterer extends Thread {
-
- private boolean unregisterRequested = false;
- private final ArrayBlockingQueue<Boolean> queue = new ArrayBlockingQueue<Boolean>(1);
-
- public void cancelUnregister() {
- try {
- queue.put(false);
- } catch (final InterruptedException e) {
- // Do nothing
- }
- }
-
- public void lateUnregister() {
- try {
- queue.put(true);
- } catch (final InterruptedException e) {
- // Do nothing
- }
- }
-
- @Override
- public void run() {
- try {
- while (true) {
- if (unregisterRequested) {
- final Boolean element = queue.poll(2500, TimeUnit.MILLISECONDS);
- if (element == null) {
- // Timeout
- unregisterListeners();
- unregisterRequested = false;
- } else {
- unregisterRequested = element;
- }
- } else {
- unregisterRequested = queue.take();
- }
- }
- } catch (final InterruptedException e) {
- // Do nothing
- }
- }
-
- }
-
/**
* Build a new geo data provider object.
* <p/>
@@ -164,10 +116,50 @@ class GeoDataProvider extends MemorySubject<IGeoData> {
*
* @param context the context used to retrieve the system services
*/
- GeoDataProvider(final Context context) {
+ protected GeoDataProvider(final Context context) {
geoManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
- unregisterer.start();
+ subject = BehaviorSubject.create(findInitialLocation());
+ }
+
+ public static Observable<IGeoData> create(final Context context) {
+ final GeoDataProvider provider = new GeoDataProvider(context);
+ return provider.worker.refCount();
+ }
+ @Override
+ public Subscription onSubscribe(final Observer<? super IGeoData> observer) {
+ return subject.subscribe(observer);
+ }
+
+ final ConnectableObservable<IGeoData> worker = new ConnectableObservable<IGeoData>(this) {
+ @Override
+ public Subscription connect() {
+ final GpsStatus.Listener gpsStatusListener = new GpsStatusListener();
+ geoManager.addGpsStatusListener(gpsStatusListener);
+
+ final Listener networkListener = new Listener(LocationManager.NETWORK_PROVIDER, netLocation);
+ final Listener gpsListener = new Listener(LocationManager.GPS_PROVIDER, gpsLocation);
+
+ for (final Listener listener : new Listener[] { networkListener, gpsListener }) {
+ try {
+ geoManager.requestLocationUpdates(listener.locationProvider, 0, 0, listener);
+ } catch (final Exception e) {
+ Log.w("There is no location provider " + listener.locationProvider);
+ }
+ }
+
+ return new Subscription() {
+ @Override
+ public void unsubscribe() {
+ geoManager.removeUpdates(networkListener);
+ geoManager.removeUpdates(gpsListener);
+ geoManager.removeGpsStatusListener(gpsStatusListener);
+ }
+ };
+ }
+ };
+
+ private IGeoData findInitialLocation() {
final Location initialLocation = new Location(LAST_LOCATION_PSEUDO_PROVIDER);
try {
// Try to find a sensible initial location from the last locations known to Android.
@@ -195,45 +187,13 @@ class GeoDataProvider extends MemorySubject<IGeoData> {
}
// Start with an historical GeoData just in case someone queries it before we get
// a chance to get any information.
- notifyObservers(new GeoData(initialLocation, false, 0, 0));
+ return new GeoData(initialLocation, false, 0, 0);
}
private static void copyCoords(final Location target, final Location source) {
target.setLatitude(source.getLatitude());
target.setLongitude(source.getLongitude());
}
- private void registerListeners() {
- geoManager.addGpsStatusListener(gpsStatusListener);
-
- for (final Listener listener : new Listener[] { networkListener, gpsListener }) {
- try {
- geoManager.requestLocationUpdates(listener.locationProvider, 0, 0, listener);
- } catch (final Exception e) {
- Log.w("There is no location provider " + listener.locationProvider);
- }
- }
- }
-
- private synchronized void unregisterListeners() {
- // This method must be synchronized because it will be called asynchronously from the Unregisterer thread.
- // We check that no observers have been re-added to prevent a race condition.
- if (sizeObservers() == 0) {
- geoManager.removeUpdates(networkListener);
- geoManager.removeUpdates(gpsListener);
- geoManager.removeGpsStatusListener(gpsStatusListener);
- }
- }
-
- @Override
- protected void onFirstObserver() {
- unregisterer.cancelUnregister();
- registerListeners();
- }
-
- @Override
- protected void onLastObserver() {
- unregisterer.lateUnregister();
- }
private class Listener implements LocationListener {
private final String locationProvider;
@@ -336,7 +296,7 @@ class GeoDataProvider extends MemorySubject<IGeoData> {
// We do not necessarily get signalled when satellites go to 0/0.
final int visible = gpsLocation.isRecent() ? satellitesVisible : 0;
final IGeoData current = new GeoData(locationData.location, gpsEnabled, visible, satellitesFixed);
- notifyObservers(current);
+ subject.onNext(current);
}
}
diff --git a/main/src/cgeo/geocaching/MainActivity.java b/main/src/cgeo/geocaching/MainActivity.java
index 7905fcf..4927b10 100644
--- a/main/src/cgeo/geocaching/MainActivity.java
+++ b/main/src/cgeo/geocaching/MainActivity.java
@@ -24,9 +24,15 @@ import cgeo.geocaching.utils.Version;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
-
-import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
+import rx.Observable;
+import rx.Observable.OnSubscribeFunc;
+import rx.Observer;
+import rx.Subscription;
+import rx.android.observables.AndroidObservable;
+import rx.concurrency.Schedulers;
+import rx.subscriptions.Subscriptions;
+import rx.util.functions.Action1;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
@@ -77,8 +83,6 @@ public class MainActivity extends AbstractActivity {
private boolean cleanupRunning = false;
private int countBubbleCnt = 0;
private Geopoint addCoords = null;
- private List<Address> addresses = null;
- private boolean addressObtaining = false;
private boolean initialized = false;
private final UpdateLocation locationUpdater = new UpdateLocation();
@@ -115,40 +119,24 @@ public class MainActivity extends AbstractActivity {
}
};
- private Handler obtainAddressHandler = new Handler() {
-
- @Override
- public void handleMessage(final Message msg) {
- try {
- if (CollectionUtils.isNotEmpty(addresses)) {
- final Address address = addresses.get(0);
- final ArrayList<String> addressParts = new ArrayList<String>();
-
- final String countryName = address.getCountryName();
- if (countryName != null) {
- addressParts.add(countryName);
- }
- final String locality = address.getLocality();
- if (locality != null) {
- addressParts.add(locality);
- } else {
- final String adminArea = address.getAdminArea();
- if (adminArea != null) {
- addressParts.add(adminArea);
- }
- }
-
- addCoords = app.currentGeo().getCoords();
+ private static String formatAddress(final Address address) {
+ final ArrayList<String> addressParts = new ArrayList<String>();
- navLocation.setText(StringUtils.join(addressParts, ", "));
- }
- } catch (RuntimeException e) {
- // nothing
+ final String countryName = address.getCountryName();
+ if (countryName != null) {
+ addressParts.add(countryName);
+ }
+ final String locality = address.getLocality();
+ if (locality != null) {
+ addressParts.add(locality);
+ } else {
+ final String adminArea = address.getAdminArea();
+ if (adminArea != null) {
+ addressParts.add(adminArea);
}
-
- addresses = null;
}
- };
+ return StringUtils.join(addressParts, ", ");
+ }
private class SatellitesHandler extends GeoDirHandler {
@@ -285,7 +273,7 @@ public class MainActivity extends AbstractActivity {
@Override
public boolean onPrepareOptionsMenu(final Menu menu) {
super.onPrepareOptionsMenu(menu);
- menu.findItem(R.id.menu_pocket_queries).setVisible(Settings.isPremiumMember());
+ menu.findItem(R.id.menu_pocket_queries).setVisible(Settings.isGCPremiumMember());
return true;
}
@@ -309,7 +297,7 @@ public class MainActivity extends AbstractActivity {
startScannerApplication();
return true;
case R.id.menu_pocket_queries:
- if (!Settings.isPremiumMember()) {
+ if (!Settings.isGCPremiumMember()) {
return true;
}
new PocketQueryList.UserInterface(MainActivity.this).promptForListSelection(new RunnableWithArgument<PocketQueryList>() {
@@ -525,52 +513,61 @@ public class MainActivity extends AbstractActivity {
@Override
public void updateGeoData(final IGeoData geo) {
- try {
- if (geo.getCoords() != null) {
- if (!nearestView.isClickable()) {
- nearestView.setFocusable(true);
- nearestView.setClickable(true);
- nearestView.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(final View v) {
- cgeoFindNearest(v);
- }
- });
- nearestView.setBackgroundResource(R.drawable.main_nearby);
+ if (!nearestView.isClickable()) {
+ nearestView.setFocusable(true);
+ nearestView.setClickable(true);
+ nearestView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ cgeoFindNearest(v);
}
+ });
+ nearestView.setBackgroundResource(R.drawable.main_nearby);
+ }
- navType.setText(res.getString(geo.getLocationProvider().resourceId));
+ navType.setText(res.getString(geo.getLocationProvider().resourceId));
- if (geo.getAccuracy() >= 0) {
- int speed = Math.round(geo.getSpeed()) * 60 * 60 / 1000;
- navAccuracy.setText("±" + Units.getDistanceFromMeters(geo.getAccuracy()) + Formatter.SEPARATOR + Units.getSpeed(speed));
- } else {
- navAccuracy.setText(null);
- }
+ if (geo.getAccuracy() >= 0) {
+ int speed = Math.round(geo.getSpeed()) * 60 * 60 / 1000;
+ navAccuracy.setText("±" + Units.getDistanceFromMeters(geo.getAccuracy()) + Formatter.SEPARATOR + Units.getSpeed(speed));
+ } else {
+ navAccuracy.setText(null);
+ }
- if (Settings.isShowAddress()) {
- if (addCoords == null) {
- navLocation.setText(res.getString(R.string.loc_no_addr));
- }
- if (addCoords == null || (geo.getCoords().distanceTo(addCoords) > 0.5 && !addressObtaining)) {
- (new ObtainAddressThread()).start();
+ if (Settings.isShowAddress()) {
+ if (addCoords == null) {
+ navLocation.setText(R.string.loc_no_addr);
+ }
+ if (addCoords == null || (geo.getCoords().distanceTo(addCoords) > 0.5)) {
+ final Observable<String> address = Observable.create(new OnSubscribeFunc<String>() {
+ @Override
+ public Subscription onSubscribe(final Observer<? super String> observer) {
+ 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);
+ if (!addresses.isEmpty()) {
+ observer.onNext(formatAddress(addresses.get(0)));
+ }
+ observer.onCompleted();
+ } catch (final IOException e) {
+ observer.onError(e);
+ }
+ return Subscriptions.empty();
}
- } else {
- navLocation.setText(geo.getCoords().toString());
- }
- } else {
- if (nearestView.isClickable()) {
- nearestView.setFocusable(false);
- nearestView.setClickable(false);
- nearestView.setOnClickListener(null);
- nearestView.setBackgroundResource(R.drawable.main_nearby_disabled);
- }
- navType.setText(null);
- navAccuracy.setText(null);
- navLocation.setText(res.getString(R.string.loc_trying));
+ }).subscribeOn(Schedulers.threadPoolForIO());
+ AndroidObservable.fromActivity(MainActivity.this, address)
+ .onErrorResumeNext(Observable.just(geo.getCoords().toString()))
+ .subscribe(new Action1<String>() {
+ @Override
+ public void call(final String address) {
+ navLocation.setText(address);
+ }
+ });
}
- } catch (RuntimeException e) {
- Log.w("Failed to update location.");
+ } else {
+ navLocation.setText(geo.getCoords().toString());
}
}
}
@@ -714,33 +711,6 @@ public class MainActivity extends AbstractActivity {
}
}
- private class ObtainAddressThread extends Thread {
-
- public ObtainAddressThread() {
- setPriority(Thread.MIN_PRIORITY);
- }
-
- @Override
- public void run() {
- if (addressObtaining) {
- return;
- }
- addressObtaining = true;
-
- try {
- final Geocoder geocoder = new Geocoder(MainActivity.this, Locale.getDefault());
- final Geopoint coords = app.currentGeo().getCoords();
- addresses = geocoder.getFromLocation(coords.getLatitude(), coords.getLongitude(), 1);
- } catch (IOException e) {
- Log.i("Failed to obtain address");
- }
-
- obtainAddressHandler.sendEmptyMessage(0);
-
- addressObtaining = false;
- }
- }
-
/**
* @param view
* unused here but needed since this method is referenced from XML layout
diff --git a/main/src/cgeo/geocaching/SearchResult.java b/main/src/cgeo/geocaching/SearchResult.java
index 131a01c..46ac38e 100644
--- a/main/src/cgeo/geocaching/SearchResult.java
+++ b/main/src/cgeo/geocaching/SearchResult.java
@@ -10,6 +10,7 @@ import cgeo.geocaching.gcvote.GCVote;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jdt.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -228,6 +229,7 @@ public class SearchResult implements Parcelable {
return result;
}
+ @Nullable
public Geocache getFirstCacheFromResult(final EnumSet<LoadFlag> loadFlags) {
return CollectionUtils.isNotEmpty(geocodes) ? DataStore.loadCache(geocodes.iterator().next(), loadFlags) : null;
}
diff --git a/main/src/cgeo/geocaching/StatusFragment.java b/main/src/cgeo/geocaching/StatusFragment.java
index 4f70f0e..a4b5973 100644
--- a/main/src/cgeo/geocaching/StatusFragment.java
+++ b/main/src/cgeo/geocaching/StatusFragment.java
@@ -1,15 +1,17 @@
package cgeo.geocaching;
+import cgeo.geocaching.network.StatusUpdater;
import cgeo.geocaching.network.StatusUpdater.Status;
-import cgeo.geocaching.utils.IObserver;
import cgeo.geocaching.utils.Log;
+import rx.Subscription;
+import rx.android.observables.AndroidObservable;
+import rx.util.functions.Action1;
+
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
@@ -20,91 +22,68 @@ import android.widget.TextView;
public class StatusFragment extends Fragment {
- private ViewGroup status;
- private ImageView statusIcon;
- private TextView statusMessage;
-
- final private StatusHandler statusHandler = new StatusHandler();
+ private Subscription statusSubscription;
@Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
- status = (ViewGroup) inflater.inflate(R.layout.status, container, false);
- statusIcon = (ImageView) status.findViewById(R.id.status_icon);
- statusMessage = (TextView) status.findViewById(R.id.status_message);
- return status;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- CgeoApplication.getInstance().getStatusUpdater().addObserver(statusHandler);
- }
-
- @Override
- public void onPause() {
- CgeoApplication.getInstance().getStatusUpdater().deleteObserver(statusHandler);
- super.onPause();
- }
-
- private class StatusHandler extends Handler implements IObserver<Status> {
-
- @Override
- public void update(final Status data) {
- obtainMessage(0, data).sendToTarget();
- }
-
- @Override
- public void handleMessage(final Message msg) {
- final Status data = (Status) msg.obj;
- updateDisplay(data != null && data.message != null ? data : Status.defaultStatus());
- }
-
- private void updateDisplay(final Status data) {
-
- if (data == null) {
- status.setVisibility(View.INVISIBLE);
- return;
- }
-
- final Resources res = getResources();
- final String packageName = getActivity().getPackageName();
+ final ViewGroup statusGroup = (ViewGroup) inflater.inflate(R.layout.status, container, false);
+ final ImageView statusIcon = (ImageView) statusGroup.findViewById(R.id.status_icon);
+ final TextView statusMessage = (TextView) statusGroup.findViewById(R.id.status_message);
+ statusSubscription = AndroidObservable.fromFragment(this, StatusUpdater.latestStatus).subscribe(new Action1<Status>() {
+ @Override
+ public void call(final Status status) {
+ if (status == null) {
+ statusGroup.setVisibility(View.INVISIBLE);
+ return;
+ }
- if (data.icon != null) {
- final int iconId = res.getIdentifier(data.icon, "drawable", packageName);
- if (iconId != 0) {
- statusIcon.setImageResource(iconId);
- statusIcon.setVisibility(View.VISIBLE);
+ final Resources res = getResources();
+ final String packageName = getActivity().getPackageName();
+
+ if (status.icon != null) {
+ final int iconId = res.getIdentifier(status.icon, "drawable", packageName);
+ if (iconId != 0) {
+ statusIcon.setImageResource(iconId);
+ statusIcon.setVisibility(View.VISIBLE);
+ } else {
+ Log.w("StatusHandler: could not find icon corresponding to @drawable/" + status.icon);
+ statusIcon.setVisibility(View.GONE);
+ }
} else {
- Log.w("StatusHandler: could not find icon corresponding to @drawable/" + data.icon);
statusIcon.setVisibility(View.GONE);
}
- } else {
- statusIcon.setVisibility(View.GONE);
- }
- String message = data.message;
- if (data.messageId != null) {
- final int messageId = res.getIdentifier(data.messageId, "string", packageName);
- if (messageId != 0) {
- message = res.getString(messageId);
+ String message = status.message;
+ if (status.messageId != null) {
+ final int messageId = res.getIdentifier(status.messageId, "string", packageName);
+ if (messageId != 0) {
+ message = res.getString(messageId);
+ }
}
- }
- statusMessage.setText(message);
- status.setVisibility(View.VISIBLE);
+ statusMessage.setText(message);
+ statusGroup.setVisibility(View.VISIBLE);
- if (data.url != null) {
- status.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(final View v) {
- startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(data.url)));
- }
- });
- } else {
- status.setClickable(false);
+ if (status.url != null) {
+ statusGroup.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(status.url)));
+ }
+ });
+ } else {
+ statusGroup.setClickable(false);
+ }
}
- }
+ });
+ return statusGroup;
+ }
+ @Override
+ public void onDestroy() {
+ statusSubscription.unsubscribe();
+ super.onDestroy();
}
+
}
diff --git a/main/src/cgeo/geocaching/TrackableActivity.java b/main/src/cgeo/geocaching/TrackableActivity.java
index dcfd80a..34f546a 100644
--- a/main/src/cgeo/geocaching/TrackableActivity.java
+++ b/main/src/cgeo/geocaching/TrackableActivity.java
@@ -130,6 +130,8 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi
// try to get data from URI
if (geocode == null && guid == null && id == null && uri != null) {
+ geocode = ConnectorFactory.getTrackableFromURL(uri.toString());
+
final String uriHost = uri.getHost().toLowerCase(Locale.US);
if (uriHost.contains("geocaching.com")) {
geocode = uri.getQueryParameter("tracker");
diff --git a/main/src/cgeo/geocaching/activity/AbstractListActivity.java b/main/src/cgeo/geocaching/activity/AbstractListActivity.java
index 2adae7a..a5d5c14 100644
--- a/main/src/cgeo/geocaching/activity/AbstractListActivity.java
+++ b/main/src/cgeo/geocaching/activity/AbstractListActivity.java
@@ -11,7 +11,6 @@ public abstract class AbstractListActivity extends FragmentListActivity implemen
IAbstractActivity {
private boolean keepScreenOn = false;
- private boolean paused = true;
protected CgeoApplication app = null;
protected Resources res = null;
@@ -85,26 +84,4 @@ public abstract class AbstractListActivity extends FragmentListActivity implemen
// initialize action bar title with activity title
ActivityMixin.setTitle(this, getTitle());
}
-
- @Override
- public void onResume() {
- paused = false;
- super.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- paused = true;
- }
-
- /**
- * Check if the current activity is paused. This must be called and acted
- * upon only from the UI thread.
- *
- * @return <code>true</code> if the current activity is paused
- */
- protected boolean isPaused() {
- return paused;
- }
}
diff --git a/main/src/cgeo/geocaching/connector/ConnectorFactory.java b/main/src/cgeo/geocaching/connector/ConnectorFactory.java
index 0081951..68e3142 100644
--- a/main/src/cgeo/geocaching/connector/ConnectorFactory.java
+++ b/main/src/cgeo/geocaching/connector/ConnectorFactory.java
@@ -27,6 +27,10 @@ import cgeo.geocaching.geopoint.Viewport;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
+import rx.Observable;
+import rx.concurrency.Schedulers;
+import rx.util.functions.Func1;
+import rx.util.functions.Func2;
import java.util.ArrayList;
import java.util.Arrays;
@@ -176,14 +180,30 @@ public final class ConnectorFactory {
}
/** @see ISearchByViewPort#searchByViewport */
- public static SearchResult searchByViewport(final @NonNull Viewport viewport, final MapTokens tokens) {
- final SearchResult result = new SearchResult();
- for (final ISearchByViewPort connector : searchByViewPortConns) {
- if (connector.isActive()) {
- result.addSearchResult(connector.searchByViewport(viewport, tokens));
+ public static Observable<SearchResult> searchByViewport(final @NonNull Viewport viewport, final MapTokens tokens) {
+ return Observable.from(searchByViewPortConns).filter(new Func1<ISearchByViewPort, Boolean>() {
+ @Override
+ public Boolean call(final ISearchByViewPort connector) {
+ return connector.isActive();
}
- }
- return result;
+ }).parallel(new Func1<Observable<ISearchByViewPort>, Observable<SearchResult>>() {
+ @Override
+ public Observable<SearchResult> call(final Observable<ISearchByViewPort> connector) {
+ return connector.map(new Func1<ISearchByViewPort, SearchResult>() {
+ @Override
+ public SearchResult call(final ISearchByViewPort connector) {
+ return connector.searchByViewport(viewport, tokens);
+ }
+ });
+ }
+ }, Schedulers.threadPoolForIO()).reduce(new SearchResult(), new Func2<SearchResult, SearchResult, SearchResult>() {
+
+ @Override
+ public SearchResult call(final SearchResult result, final SearchResult searchResult) {
+ result.addSearchResult(searchResult);
+ return result;
+ }
+ });
}
public static String getGeocodeFromURL(final String url) {
@@ -200,6 +220,12 @@ public final class ConnectorFactory {
return TRACKABLE_CONNECTORS;
}
+ /**
+ * Get the geocode of a trackable from a URL.
+ *
+ * @param url
+ * @return {@code null} if the URL cannot be decoded
+ */
public static String getTrackableFromURL(final String url) {
if (url == null) {
return null;
diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java
index e946748..9c6b831 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java
@@ -91,7 +91,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
@Override
public boolean supportsPersonalNote() {
- return Settings.isPremiumMember();
+ return Settings.isGCPremiumMember();
}
@Override
@@ -285,7 +285,22 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
@Override
protected String getCacheUrlPrefix() {
- return CACHE_URL_SHORT;
+ return null; // UNUSED
+ }
+
+ @Override
+ public String getGeocodeFromUrl(String url) {
+ // coord.info URLs
+ String code = StringUtils.substringAfterLast(url, "coord.info/");
+ if (code != null && canHandle(code)) {
+ return code;
+ }
+ // expanded geocaching.com URLs
+ code = StringUtils.substringBetween(url, "/geocache/", "_");
+ if (code != null && canHandle(code)) {
+ return code;
+ }
+ return null;
}
@Override
diff --git a/main/src/cgeo/geocaching/connector/gc/GCLogin.java b/main/src/cgeo/geocaching/connector/gc/GCLogin.java
index a7cf6cf..f747911 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCLogin.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCLogin.java
@@ -97,7 +97,7 @@ public class GCLogin extends AbstractLogin {
}
if (getLoginStatus(loginData)) {
- Log.i("Already logged in Geocaching.com as " + username + " (" + Settings.getMemberStatus() + ')');
+ Log.i("Already logged in Geocaching.com as " + username + " (" + Settings.getGCMemberStatus() + ')');
if (switchToEnglish(loginData) && retry) {
return login(false);
}
@@ -132,7 +132,7 @@ public class GCLogin extends AbstractLogin {
assert loginData != null; // Caught above
if (getLoginStatus(loginData)) {
- Log.i("Successfully logged in Geocaching.com as " + username + " (" + Settings.getMemberStatus() + ')');
+ Log.i("Successfully logged in Geocaching.com as " + username + " (" + Settings.getGCMemberStatus() + ')');
if (switchToEnglish(loginData) && retry) {
return login(false);
@@ -204,9 +204,9 @@ public class GCLogin extends AbstractLogin {
Log.e("getLoginStatus: bad cache count", e);
}
setActualCachesFound(cachesCount);
- Settings.setMemberStatus(TextUtils.getMatch(page, GCConstants.PATTERN_MEMBER_STATUS, true, null));
+ Settings.setGCMemberStatus(TextUtils.getMatch(page, GCConstants.PATTERN_MEMBER_STATUS, true, null));
if ( page.contains(GCConstants.MEMBER_STATUS_RENEW) ) {
- Settings.setMemberStatus(GCConstants.MEMBER_STATUS_PM);
+ Settings.setGCMemberStatus(GCConstants.MEMBER_STATUS_PM);
}
return true;
}
@@ -259,9 +259,9 @@ public class GCLogin extends AbstractLogin {
final String responseData = StringUtils.defaultString(Network.getResponseData(Network.getRequest("http://www.geocaching.com/my/")));
final String profile = TextUtils.replaceWhitespace(responseData);
- Settings.setMemberStatus(TextUtils.getMatch(profile, GCConstants.PATTERN_MEMBER_STATUS, true, null));
+ Settings.setGCMemberStatus(TextUtils.getMatch(profile, GCConstants.PATTERN_MEMBER_STATUS, true, null));
if (profile.contains(GCConstants.MEMBER_STATUS_RENEW)) {
- Settings.setMemberStatus(GCConstants.MEMBER_STATUS_PM);
+ Settings.setGCMemberStatus(GCConstants.MEMBER_STATUS_PM);
}
setActualCachesFound(Integer.parseInt(TextUtils.getMatch(profile, GCConstants.PATTERN_CACHES_FOUND, true, "-1").replaceAll("[,.]", "")));
diff --git a/main/src/cgeo/geocaching/connector/gc/GCMap.java b/main/src/cgeo/geocaching/connector/gc/GCMap.java
index 6c94150..2782b64 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCMap.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCMap.java
@@ -368,7 +368,7 @@ public class GCMap {
}
}
- if (strategy.flags.contains(StrategyFlag.SEARCH_NEARBY) && Settings.isPremiumMember()) {
+ if (strategy.flags.contains(StrategyFlag.SEARCH_NEARBY) && Settings.isGCPremiumMember()) {
final Geopoint center = viewport.getCenter();
if ((lastSearchViewport == null) || !lastSearchViewport.contains(center)) {
//FIXME We don't have a RecaptchaReceiver!?
diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java
index 62ccb14..fe962e6 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCParser.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java
@@ -282,7 +282,7 @@ public abstract class GCParser {
recaptchaText = thread.getText();
}
- if (!cids.isEmpty() && (Settings.isPremiumMember() || showCaptcha) && ((thread == null || StringUtils.isBlank(thread.getChallenge())) || StringUtils.isNotBlank(recaptchaText))) {
+ if (!cids.isEmpty() && (Settings.isGCPremiumMember() || showCaptcha) && ((thread == null || StringUtils.isBlank(thread.getChallenge())) || StringUtils.isNotBlank(recaptchaText))) {
Log.i("Trying to get .loc for " + cids.size() + " caches");
try {
@@ -347,6 +347,9 @@ public abstract class GCParser {
// attention: parseCacheFromText already stores implicitly through searchResult.addCache
if (searchResult != null && !searchResult.getGeocodes().isEmpty()) {
final Geocache cache = searchResult.getFirstCacheFromResult(LoadFlags.LOAD_CACHE_OR_DB);
+ if (cache == null) {
+ return null;
+ }
getExtraOnlineInfo(cache, page, handler);
// too late: it is already stored through parseCacheFromText
cache.setDetailedUpdatedNow();
@@ -741,7 +744,7 @@ public abstract class GCParser {
cache.parseWaypointsFromNote();
// logs
- cache.setLogs(loadLogsFromDetails(page, cache, false, true));
+ cache.setLogs(getLogsFromDetails(page, false));
// last check for necessary cache conditions
if (StringUtils.isBlank(cache.getGeocode())) {
@@ -1380,8 +1383,7 @@ public abstract class GCParser {
}
private static boolean changeFavorite(final Geocache cache, final boolean add) {
- final String page = requestHtmlPage(cache.getGeocode(), null, "n", "0");
- final String userToken = TextUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, "");
+ final String userToken = getUserToken(cache);
if (StringUtils.isEmpty(userToken)) {
return false;
}
@@ -1400,6 +1402,11 @@ public abstract class GCParser {
return false;
}
+ private static String getUserToken(final Geocache cache) {
+ final String page = requestHtmlPage(cache.getGeocode(), null, "n", "0");
+ return TextUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, "");
+ }
+
/**
* Removes the cache from the favorites.
*
@@ -1613,19 +1620,20 @@ public abstract class GCParser {
}
/**
- * Load logs from a cache details page.
+ * Extract logs from a cache details page.
*
* @param page
* the text of the details page
- * @param cache
- * the cache object to put the logs in
* @param friends
- * retrieve friend logs
+ * return friends logs only (will require a network request)
+ * @return a list of log entries or <code>null</code> if the logs could not be retrieved
+ *
*/
- private static List<LogEntry> loadLogsFromDetails(final String page, final Geocache cache, final boolean friends, final boolean getDataFromPage) {
+ @Nullable
+ private static List<LogEntry> getLogsFromDetails(final String page, final boolean friends) {
String rawResponse;
- if (!getDataFromPage) {
+ if (friends) {
final MatcherWrapper userTokenMatcher = new MatcherWrapper(GCConstants.PATTERN_USERTOKEN, page);
if (!userTokenMatcher.find()) {
Log.e("GCParser.loadLogsFromDetails: unable to extract userToken");
@@ -1825,7 +1833,7 @@ public abstract class GCParser {
if (Settings.isFriendLogsWanted()) {
CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_logs);
final List<LogEntry> allLogs = cache.getLogs();
- final List<LogEntry> friendLogs = loadLogsFromDetails(page, cache, true, false);
+ final List<LogEntry> friendLogs = getLogsFromDetails(page, true);
if (friendLogs != null) {
for (final LogEntry log : friendLogs) {
if (allLogs.contains(log)) {
@@ -1860,8 +1868,7 @@ public abstract class GCParser {
}
public static boolean editModifiedCoordinates(Geocache cache, Geopoint wpt) {
- final String page = requestHtmlPage(cache.getGeocode(), null, "n", "0");
- final String userToken = TextUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, "");
+ final String userToken = getUserToken(cache);
if (StringUtils.isEmpty(userToken)) {
return false;
}
@@ -1896,8 +1903,7 @@ public abstract class GCParser {
}
public static boolean uploadPersonalNote(Geocache cache) {
- final String page = requestHtmlPage(cache.getGeocode(), null, "n", "0");
- final String userToken = TextUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, "");
+ final String userToken = getUserToken(cache);
if (StringUtils.isEmpty(userToken)) {
return false;
}
diff --git a/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java b/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java
index 69efddc..fb554b9 100644
--- a/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java
+++ b/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java
@@ -4,6 +4,7 @@ import cgeo.geocaching.connector.AbstractConnector;
import cgeo.geocaching.connector.UserAction;
import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
import java.util.List;
@@ -15,7 +16,8 @@ public abstract class AbstractTrackableConnector implements TrackableConnector {
}
@Override
- public String getTrackableCodeFromUrl(@NonNull String url) {
+ public @Nullable
+ String getTrackableCodeFromUrl(@NonNull String url) {
return null;
}
diff --git a/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java b/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java
index 8387076..03052f9 100644
--- a/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java
+++ b/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java
@@ -4,6 +4,10 @@ import cgeo.geocaching.Trackable;
import cgeo.geocaching.network.Network;
import cgeo.geocaching.utils.Log;
+import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
import java.util.regex.Pattern;
public class GeokretyConnector extends AbstractTrackableConnector {
@@ -29,7 +33,7 @@ public class GeokretyConnector extends AbstractTrackableConnector {
return GeokretyParser.parse(page);
}
- private static int getId(String geocode) {
+ protected static int getId(String geocode) {
try {
final String hex = geocode.substring(2);
return Integer.parseInt(hex, 16);
@@ -39,4 +43,25 @@ public class GeokretyConnector extends AbstractTrackableConnector {
return -1;
}
+ @Override
+ public @Nullable
+ String getTrackableCodeFromUrl(@NonNull String url) {
+ // http://geokrety.org/konkret.php?id=38545
+ String id = StringUtils.substringAfterLast(url, "konkret.php?id=");
+ if (StringUtils.isNumeric(id)) {
+ return geocode(Integer.parseInt(id));
+ }
+ return null;
+ }
+
+ /**
+ * Get geocode from geokrety id
+ *
+ * @param id
+ * @return
+ */
+ public static String geocode(final int id) {
+ return String.format("GK%04X", id);
+ }
+
}
diff --git a/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java b/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java
index ee8c8c0..0e64abd 100644
--- a/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java
+++ b/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java
@@ -37,8 +37,8 @@ public class GeokretyParser {
public void start(Attributes attributes) {
try {
final String kretyId = attributes.getValue("id");
- if (StringUtils.isNotBlank(kretyId)) {
- trackable.setGeocode(geocode(Integer.parseInt(kretyId)));
+ if (StringUtils.isNumeric(kretyId)) {
+ trackable.setGeocode(GeokretyConnector.geocode(Integer.parseInt(kretyId)));
}
final String distance = attributes.getValue("dist");
if (StringUtils.isNotBlank(distance)) {
@@ -88,8 +88,4 @@ public class GeokretyParser {
}
return null;
}
-
- protected static String geocode(final int id) {
- return String.format("GK%04X", id);
- }
}
diff --git a/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java b/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java
index 0990d96..6071b5f 100644
--- a/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java
+++ b/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java
@@ -4,6 +4,7 @@ import cgeo.geocaching.Trackable;
import cgeo.geocaching.connector.UserAction;
import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
import java.util.List;
@@ -21,7 +22,8 @@ public interface TrackableConnector {
public Trackable searchTrackable(String geocode, String guid, String id);
- public String getTrackableCodeFromUrl(final @NonNull String url);
+ public @Nullable
+ String getTrackableCodeFromUrl(final @NonNull String url);
public @NonNull
List<UserAction> getUserActions();
diff --git a/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java b/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java
index dad285c..77848d7 100644
--- a/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java
+++ b/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java
@@ -7,6 +7,7 @@ import cgeo.geocaching.connector.gc.GCParser;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
import java.util.List;
import java.util.regex.Pattern;
@@ -20,7 +21,7 @@ public class TravelBugConnector extends AbstractTrackableConnector {
@Override
public boolean canHandleTrackable(String geocode) {
- return TravelBugConnector.PATTERN_TB_CODE.matcher(geocode).matches();
+ return TravelBugConnector.PATTERN_TB_CODE.matcher(geocode).matches() && !StringUtils.startsWithIgnoreCase(geocode, "GC");
}
@Override
@@ -54,8 +55,18 @@ public class TravelBugConnector extends AbstractTrackableConnector {
}
@Override
- public String getTrackableCodeFromUrl(@NonNull String url) {
- return StringUtils.substringAfterLast(url, "?tracker=");
+ public @Nullable
+ String getTrackableCodeFromUrl(@NonNull String url) {
+ // coord.info URLs
+ String code = StringUtils.substringAfterLast(url, "coord.info/");
+ if (code != null && canHandleTrackable(code)) {
+ return code;
+ }
+ code = StringUtils.substringAfterLast(url, "?tracker=");
+ if (code != null && canHandleTrackable(code)) {
+ return code;
+ }
+ return null;
}
@Override
diff --git a/main/src/cgeo/geocaching/maps/CGeoMap.java b/main/src/cgeo/geocaching/maps/CGeoMap.java
index c98ba72..65d8861 100644
--- a/main/src/cgeo/geocaching/maps/CGeoMap.java
+++ b/main/src/cgeo/geocaching/maps/CGeoMap.java
@@ -898,7 +898,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto
private long timeLastPositionOverlayCalculation = 0;
@Override
- protected void updateGeoData(final IGeoData geo) {
+ public void updateGeoData(final IGeoData geo) {
if (geo.isPseudoLocation()) {
locationValid = false;
} else {
@@ -1182,7 +1182,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto
}
}
}
- final SearchResult searchResult = ConnectorFactory.searchByViewport(viewport.resize(0.8), tokens);
+ final SearchResult searchResult = ConnectorFactory.searchByViewport(viewport.resize(0.8), tokens).toBlockingObservable().single();
downloaded = true;
Set<Geocache> result = searchResult.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB);
diff --git a/main/src/cgeo/geocaching/network/StatusUpdater.java b/main/src/cgeo/geocaching/network/StatusUpdater.java
index cb4c7f4..40281ad 100644
--- a/main/src/cgeo/geocaching/network/StatusUpdater.java
+++ b/main/src/cgeo/geocaching/network/StatusUpdater.java
@@ -1,21 +1,22 @@
package cgeo.geocaching.network;
import cgeo.geocaching.CgeoApplication;
-import cgeo.geocaching.utils.MemorySubject;
-import cgeo.geocaching.utils.PeriodicHandler;
-import cgeo.geocaching.utils.PeriodicHandler.PeriodicHandlerListener;
import cgeo.geocaching.utils.Version;
import org.json.JSONException;
import org.json.JSONObject;
+import rx.Observable;
+import rx.concurrency.Schedulers;
+import rx.subjects.BehaviorSubject;
+import rx.util.functions.Func1;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
-import android.os.Looper;
import java.util.Locale;
+import java.util.concurrent.TimeUnit;
-public class StatusUpdater extends MemorySubject<StatusUpdater.Status> implements Runnable, PeriodicHandlerListener {
+public class StatusUpdater {
static public class Status {
final public String message;
@@ -30,24 +31,41 @@ public class StatusUpdater extends MemorySubject<StatusUpdater.Status> implement
this.url = url;
}
+ Status(final JSONObject response) {
+ message = get(response, "message");
+ messageId = get(response, "message_id");
+ icon = get(response, "icon");
+ url = get(response, "url");
+ }
+
final static public Status closeoutStatus =
new Status("", "status_closeout_warning", "attribute_abandonedbuilding", "http://faq.cgeo.org/#7_69");
- final static public Status defaultStatus() {
+ final static public Status defaultStatus(final Status upToDate) {
+ if (upToDate != null && upToDate.message != null) {
+ return upToDate;
+ }
return VERSION.SDK_INT < VERSION_CODES.ECLAIR_MR1 ? closeoutStatus : null;
}
}
- @Override
- public void onPeriodic() {
- final JSONObject response =
- Network.requestJSON("http://status.cgeo.org/api/status.json",
- new Parameters("version_code", String.valueOf(Version.getVersionCode(CgeoApplication.getInstance())),
- "version_name", Version.getVersionName(CgeoApplication.getInstance()),
- "locale", Locale.getDefault().toString()));
- if (response != null) {
- notifyObservers(new Status(get(response, "message"), get(response, "message_id"), get(response, "icon"), get(response, "url")));
- }
+ final static private Observable<Status> statusObservable =
+ Observable.interval(30, TimeUnit.MINUTES).startWith(-1L).flatMap(new Func1<Long, Observable<Status>>() {
+ @Override
+ public Observable<Status> call(Long id) {
+ final JSONObject response =
+ Network.requestJSON("http://status.cgeo.org/api/status.json",
+ new Parameters("version_code", String.valueOf(Version.getVersionCode(CgeoApplication.getInstance())),
+ "version_name", Version.getVersionName(CgeoApplication.getInstance()),
+ "locale", Locale.getDefault().toString()));
+ return response != null ? Observable.just(Status.defaultStatus((new Status(response)))) : Observable.<Status>empty();
+ }
+ }).subscribeOn(Schedulers.threadPoolForIO());
+
+ final static public BehaviorSubject<Status> latestStatus = BehaviorSubject.create(Status.defaultStatus(null));
+
+ static {
+ statusObservable.subscribe(latestStatus);
}
private static String get(final JSONObject json, final String key) {
@@ -58,11 +76,4 @@ public class StatusUpdater extends MemorySubject<StatusUpdater.Status> implement
}
}
- @Override
- public void run() {
- Looper.prepare();
- new PeriodicHandler(1800000L, this).start();
- Looper.loop();
- }
-
}
diff --git a/main/src/cgeo/geocaching/settings/Settings.java b/main/src/cgeo/geocaching/settings/Settings.java
index 0732866..ee97c13 100644
--- a/main/src/cgeo/geocaching/settings/Settings.java
+++ b/main/src/cgeo/geocaching/settings/Settings.java
@@ -313,16 +313,16 @@ public class Settings {
return getBoolean(R.string.pref_connectorOXActive, false);
}
- public static boolean isPremiumMember() {
+ public static boolean isGCPremiumMember() {
// Basic Member, Premium Member, ???
- return GCConstants.MEMBER_STATUS_PM.equalsIgnoreCase(Settings.getMemberStatus());
+ return GCConstants.MEMBER_STATUS_PM.equalsIgnoreCase(Settings.getGCMemberStatus());
}
- public static String getMemberStatus() {
+ public static String getGCMemberStatus() {
return getString(R.string.pref_memberstatus, "");
}
- public static boolean setMemberStatus(final String memberStatus) {
+ public static boolean setGCMemberStatus(final String memberStatus) {
if (StringUtils.isBlank(memberStatus)) {
return remove(R.string.pref_memberstatus);
}
@@ -478,7 +478,7 @@ public class Settings {
}
public static boolean getLoadDirImg() {
- return !isPremiumMember() && getBoolean(R.string.pref_loaddirectionimg, true);
+ return !isGCPremiumMember() && getBoolean(R.string.pref_loaddirectionimg, true);
}
public static void setGcCustomDate(final String format) {
@@ -506,7 +506,7 @@ public class Settings {
}
public static boolean isShowCaptcha() {
- return !isPremiumMember() && getBoolean(R.string.pref_showcaptcha, false);
+ return !isGCPremiumMember() && getBoolean(R.string.pref_showcaptcha, false);
}
public static boolean isExcludeDisabledCaches() {
diff --git a/main/src/cgeo/geocaching/settings/SettingsActivity.java b/main/src/cgeo/geocaching/settings/SettingsActivity.java
index 58acfc1..04880c4 100644
--- a/main/src/cgeo/geocaching/settings/SettingsActivity.java
+++ b/main/src/cgeo/geocaching/settings/SettingsActivity.java
@@ -1,6 +1,7 @@
package cgeo.geocaching.settings;
import cgeo.geocaching.CgeoApplication;
+import cgeo.geocaching.DataStore;
import cgeo.geocaching.Intents;
import cgeo.geocaching.R;
import cgeo.geocaching.SelectMapfileActivity;
@@ -19,8 +20,10 @@ import cgeo.geocaching.utils.Log;
import org.apache.commons.lang3.StringUtils;
import org.openintents.intents.FileManagerIntents;
+import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -121,6 +124,7 @@ public class SettingsActivity extends PreferenceActivity {
initSend2CgeoPreferences();
initServicePreferences();
initNavigationMenuPreferences();
+ initMaintenanceButtons();
for (int k : new int[] { R.string.pref_username, R.string.pref_password,
R.string.pref_pass_vote, R.string.pref_signature,
@@ -142,7 +146,7 @@ public class SettingsActivity extends PreferenceActivity {
}
}
getPreference(R.string.pref_fakekey_basicmembers_screen)
- .setEnabled(!Settings.isPremiumMember());
+ .setEnabled(!Settings.isGCPremiumMember());
redrawScreen(R.string.pref_fakekey_navigation_menu_screen);
}
@@ -310,6 +314,35 @@ public class SettingsActivity extends PreferenceActivity {
});
}
+ public void initMaintenanceButtons() {
+ Preference dirMaintenance = getPreference(R.string.pref_fakekey_preference_maintenance_directories);
+ dirMaintenance.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(final Preference preference) {
+ // disable the button, as the cleanup runs in background and should not be invoked a second time
+ preference.setEnabled(false);
+
+ Resources res = getResources();
+ final SettingsActivity activity = SettingsActivity.this;
+ final ProgressDialog dialog = ProgressDialog.show(activity, res.getString(R.string.init_maintenance), res.getString(R.string.init_maintenance_directories), true, false);
+ new Thread() {
+ @Override
+ public void run() {
+ DataStore.removeObsoleteCacheDirectories();
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ dialog.dismiss();
+ }
+ });
+ }
+ }.start();
+
+ return true;
+ }
+ });
+ }
+
private void initDbLocationPreference() {
Preference p = getPreference(R.string.pref_dbonsdcard);
p.setPersistent(false);
@@ -337,11 +370,11 @@ public class SettingsActivity extends PreferenceActivity {
void initBasicMemberPreferences() {
getPreference(R.string.pref_fakekey_basicmembers_screen)
- .setEnabled(!Settings.isPremiumMember());
+ .setEnabled(!Settings.isGCPremiumMember());
getPreference(R.string.pref_loaddirectionimg)
- .setEnabled(!Settings.isPremiumMember());
+ .setEnabled(!Settings.isGCPremiumMember());
getPreference(R.string.pref_showcaptcha)
- .setEnabled(!Settings.isPremiumMember());
+ .setEnabled(!Settings.isGCPremiumMember());
redrawScreen(R.string.pref_fakekey_services_screen);
}
diff --git a/main/src/cgeo/geocaching/speech/SpeechService.java b/main/src/cgeo/geocaching/speech/SpeechService.java
index 2a72bbf..8c650c3 100644
--- a/main/src/cgeo/geocaching/speech/SpeechService.java
+++ b/main/src/cgeo/geocaching/speech/SpeechService.java
@@ -47,7 +47,7 @@ public class SpeechService extends Service implements OnInitListener {
GeoDirHandler geoHandler = new GeoDirHandler() {
@Override
- protected void updateDirection(float newDirection) {
+ public void updateDirection(float newDirection) {
if (CgeoApplication.getInstance().currentGeo().getSpeed() <= 5) {
direction = DirectionProvider.getDirectionNow(startingActivity, newDirection);
directionInitialized = true;
@@ -56,7 +56,7 @@ public class SpeechService extends Service implements OnInitListener {
}
@Override
- protected void updateGeoData(cgeo.geocaching.IGeoData newGeo) {
+ public void updateGeoData(cgeo.geocaching.IGeoData newGeo) {
position = newGeo.getCoords();
positionInitialized = true;
if (!Settings.isUseCompass() || newGeo.getSpeed() > 5) {
diff --git a/main/src/cgeo/geocaching/utils/GeoDirHandler.java b/main/src/cgeo/geocaching/utils/GeoDirHandler.java
index c85648b..b9c605b 100644
--- a/main/src/cgeo/geocaching/utils/GeoDirHandler.java
+++ b/main/src/cgeo/geocaching/utils/GeoDirHandler.java
@@ -4,12 +4,15 @@ import cgeo.geocaching.CgeoApplication;
import cgeo.geocaching.IGeoData;
import cgeo.geocaching.settings.Settings;
-import android.os.Handler;
-import android.os.Message;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.concurrency.AndroidSchedulers;
+import rx.util.functions.Action1;
+
+import java.util.concurrent.TimeUnit;
/**
- * GeoData and Direction handler. Manipulating geodata and direction information
- * through a GeoDirHandler ensures that all listeners are registered from a {@link android.os.Looper} thread.
+ * GeoData and Direction handler.
* <p>
* To use this class, override at least one of {@link #updateDirection(float)} or {@link #updateGeoData(IGeoData)}. You
* need to start the handler using one of
@@ -21,47 +24,11 @@ import android.os.Message;
* A good place might be the {@code onResume} method of the Activity. Stop the Handler accordingly in {@code onPause}.
* </p>
*/
-public abstract class GeoDirHandler extends Handler implements IObserver<Object> {
-
- private static final int OBSERVABLE = 1 << 1;
- private static final int START_GEO = 1 << 2;
- private static final int START_DIR = 1 << 3;
- private static final int STOP_GEO = 1 << 4;
- private static final int STOP_DIR = 1 << 5;
-
+public abstract class GeoDirHandler {
private static final CgeoApplication app = CgeoApplication.getInstance();
- @Override
- final public void handleMessage(final Message message) {
- if ((message.what & START_GEO) != 0) {
- app.addGeoObserver(this);
- }
-
- if ((message.what & START_DIR) != 0) {
- app.addDirectionObserver(this);
- }
-
- if ((message.what & STOP_GEO) != 0) {
- app.deleteGeoObserver(this);
- }
-
- if ((message.what & STOP_DIR) != 0) {
- app.deleteDirectionObserver(this);
- }
-
- if ((message.what & OBSERVABLE) != 0) {
- if (message.obj instanceof IGeoData) {
- updateGeoData((IGeoData) message.obj);
- } else {
- updateDirection((Float) message.obj);
- }
- }
- }
-
- @Override
- final public void update(final Object o) {
- obtainMessage(OBSERVABLE, o).sendToTarget();
- }
+ private Subscription dirSubscription = null;
+ private Subscription geoSubscription = null;
/**
* Update method called when new IGeoData is available.
@@ -69,7 +36,7 @@ public abstract class GeoDirHandler extends Handler implements IObserver<Object>
* @param data
* the new data
*/
- protected void updateGeoData(final IGeoData data) {
+ public void updateGeoData(final IGeoData data) {
// Override this in children
}
@@ -79,24 +46,38 @@ public abstract class GeoDirHandler extends Handler implements IObserver<Object>
* @param direction
* the new direction
*/
- protected void updateDirection(final float direction) {
+ public void updateDirection(final float direction) {
// Override this in children
}
/**
* Register the current GeoDirHandler for GeoData information.
*/
- public void startGeo() {
- sendEmptyMessage(START_GEO);
+ public synchronized void startGeo() {
+ geoSubscription = app.currentGeoObject()
+ .subscribeOn(AndroidSchedulers.mainThread())
+ .subscribe(new Action1<IGeoData>() {
+ @Override
+ public void call(final IGeoData geoData) {
+ updateGeoData(geoData);
+ }
+ });
}
/**
* Register the current GeoDirHandler for direction information if the preferences
* allow it.
*/
- public void startDir() {
+ public synchronized void startDir() {
if (Settings.isUseCompass()) {
- sendEmptyMessage(START_DIR);
+ dirSubscription = app.currentDirObject()
+ .subscribeOn(AndroidSchedulers.mainThread())
+ .subscribe(new Action1<Float>() {
+ @Override
+ public void call(final Float direction) {
+ updateDirection(direction);
+ }
+ });
}
}
@@ -105,27 +86,43 @@ public abstract class GeoDirHandler extends Handler implements IObserver<Object>
* preferences allow it).
*/
public void startGeoAndDir() {
- sendEmptyMessage(START_GEO | (Settings.isUseCompass() ? START_DIR : 0));
+ startGeo();
+ startDir();
}
/**
* Unregister the current GeoDirHandler for GeoData information.
*/
- public void stopGeo() {
- sendEmptyMessage(STOP_GEO);
+ public synchronized void stopGeo() {
+ // Delay the unsubscription by 2.5 seconds, so that another activity has
+ // the time to subscribe and the GPS receiver will not be turned down.
+ if (geoSubscription != null) {
+ final Subscription subscription = geoSubscription;
+ geoSubscription = null;
+ Observable.interval(2500, TimeUnit.MILLISECONDS).take(1).subscribe(new Action1<Long>() {
+ @Override
+ public void call(final Long aLong) {
+ subscription.unsubscribe();
+ }
+ });
+ }
}
/**
* Unregister the current GeoDirHandler for direction information.
*/
- public void stopDir() {
- sendEmptyMessage(STOP_DIR);
+ public synchronized void stopDir() {
+ if (dirSubscription != null) {
+ dirSubscription.unsubscribe();
+ dirSubscription = null;
+ }
}
/**
* Unregister the current GeoDirHandler for GeoData and direction information.
*/
public void stopGeoAndDir() {
- sendEmptyMessage(STOP_GEO | STOP_DIR);
+ stopGeo();
+ stopDir();
}
}
diff --git a/main/src/cgeo/geocaching/utils/IObserver.java b/main/src/cgeo/geocaching/utils/IObserver.java
deleted file mode 100644
index bfcc798..0000000
--- a/main/src/cgeo/geocaching/utils/IObserver.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package cgeo.geocaching.utils;
-
-/**
- * Observer interface.
- * <p/>
- * An observer will receive updates about the observed object (implementing the {@link ISubject} interface) through its
- * {@link #update(Object)} method.
- *
- * @param <T>
- * the kind of data to observe
- */
-public interface IObserver<T> {
-
- /**
- * Called when an observed object has updated its data.
- *
- * @param data
- * the updated data
- */
- void update(final T data);
-
-}
diff --git a/main/src/cgeo/geocaching/utils/ISubject.java b/main/src/cgeo/geocaching/utils/ISubject.java
deleted file mode 100644
index c325db0..0000000
--- a/main/src/cgeo/geocaching/utils/ISubject.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package cgeo.geocaching.utils;
-
-/**
- * Interface for subjects objects. Those can be observed by objects implementing the {@link IObserver} interface.
- *
- * @param <T>
- * the kind of data to observe
- */
-
-public interface ISubject<T> {
-
- /**
- * Add an observer to the observers list.
- * <p/>
- * Observers will be notified with no particular order.
- *
- * @param observer
- * the observer to add
- * @return true if the observer has been added, false if it was present already
- */
- public boolean addObserver(final IObserver<? super T> observer);
-
- /**
- * Delete an observer from the observers list.
- *
- * @param observer
- * the observer to remove
- * @return true if the observer has been removed, false if it was not in the list of observers
- */
- public boolean deleteObserver(final IObserver<? super T> observer);
-
- /**
- * Number of observers currently observing the object.
- *
- * @return the number of observers
- */
- public int sizeObservers();
-
- /**
- * Notify all the observers that new data is available.
- * <p/>
- * The {@link IObserver#update(Object)} method of each observer will be called with no particular order.
- *
- * @param data
- * the updated data
- * @return true if at least one observer was notified, false if there were no observers
- */
- public boolean notifyObservers(final T data);
-
- /**
- * Clear the observers list.
- *
- * @return true if there were observers before calling this method, false if the observers list was empty
- */
- public boolean clearObservers();
-
-}
diff --git a/main/src/cgeo/geocaching/utils/MemorySubject.java b/main/src/cgeo/geocaching/utils/MemorySubject.java
deleted file mode 100644
index c424528..0000000
--- a/main/src/cgeo/geocaching/utils/MemorySubject.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package cgeo.geocaching.utils;
-
-/**
- * Synchronized implementation of the {@link ISubject} interface with an added pull interface.
- *
- * @param <T>
- * the kind of data to observe
- */
-public class MemorySubject<T> extends Subject<T> {
-
- /**
- * The latest version of the observed data.
- * <p/>
- * A child class implementation may want to set this field from its constructors, in case early observers request
- * the data before it got a chance to get updated. Otherwise, <code>null</code> will be returned until updated
- * data is available.
- */
- private T memory;
-
- @Override
- public synchronized boolean addObserver(final IObserver<? super T> observer) {
- final boolean added = super.addObserver(observer);
- if (added && memory != null) {
- observer.update(memory);
- }
- return added;
- }
-
- @Override
- public synchronized boolean notifyObservers(final T data) {
- memory = data;
- return super.notifyObservers(data);
- }
-
- /**
- * Get the memorized version of the data.
- *
- * @return the initial data set by the subject (which may be <code>null</code>),
- * or the updated data if it is available
- */
- public synchronized T getMemory() {
- return memory;
- }
-
-}
diff --git a/main/src/cgeo/geocaching/utils/Subject.java b/main/src/cgeo/geocaching/utils/Subject.java
deleted file mode 100644
index b1754cc..0000000
--- a/main/src/cgeo/geocaching/utils/Subject.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package cgeo.geocaching.utils;
-
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-/**
- * Synchronized implementation of the {@link ISubject} interface.
- *
- * @param <T>
- * the kind of data to observe
- */
-public class Subject<T> implements ISubject<T> {
-
- /**
- * Collection of observers.
- */
- protected final Set<IObserver<? super T>> observers = new LinkedHashSet<IObserver<? super T>>();
-
- @Override
- public synchronized boolean addObserver(final IObserver<? super T> observer) {
- final boolean added = observers.add(observer);
- if (added && observers.size() == 1) {
- onFirstObserver();
- }
- return added;
- }
-
- @Override
- public synchronized boolean deleteObserver(final IObserver<? super T> observer) {
- final boolean removed = observers.remove(observer);
- if (removed && observers.isEmpty()) {
- onLastObserver();
- }
- return removed;
- }
-
- @Override
- public synchronized boolean notifyObservers(final T arg) {
- final boolean nonEmpty = !observers.isEmpty();
- for (final IObserver<? super T> observer : observers) {
- observer.update(arg);
- }
- return nonEmpty;
- }
-
- @Override
- public synchronized int sizeObservers() {
- return observers.size();
- }
-
- @Override
- public synchronized boolean clearObservers() {
- final boolean nonEmpty = !observers.isEmpty();
- for (final IObserver<? super T> observer : observers) {
- deleteObserver(observer);
- }
- return nonEmpty;
- }
-
- /**
- * Method called when the collection of observers goes from empty to non-empty.
- * <p/>
- * The default implementation does nothing and may be overwritten by child classes.
- */
- protected void onFirstObserver() {
- }
-
- /**
- * Method called when the collection of observers goes from non-empty to empty.
- * <p/>
- * The default implementation does nothing and may be overwritten by child classes.
- */
- protected void onLastObserver() {
- }
-
-}