aboutsummaryrefslogtreecommitdiffstats
path: root/main/src
diff options
context:
space:
mode:
Diffstat (limited to 'main/src')
-rw-r--r--main/src/cgeo/geocaching/CacheDetailActivity.java13
-rw-r--r--main/src/cgeo/geocaching/CacheListActivity.java3
-rw-r--r--main/src/cgeo/geocaching/CachePopup.java6
-rw-r--r--main/src/cgeo/geocaching/CgeoApplication.java31
-rw-r--r--main/src/cgeo/geocaching/DataStore.java19
-rw-r--r--main/src/cgeo/geocaching/Geocache.java13
-rw-r--r--main/src/cgeo/geocaching/LogCacheActivity.java2
-rw-r--r--main/src/cgeo/geocaching/SearchActivity.java42
-rw-r--r--main/src/cgeo/geocaching/SearchResult.java11
-rw-r--r--main/src/cgeo/geocaching/StaticMapsProvider.java3
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCParser.java361
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java6
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OkapiClient.java7
-rw-r--r--main/src/cgeo/geocaching/filter/StateFilter.java14
-rw-r--r--main/src/cgeo/geocaching/list/StoredList.java16
-rw-r--r--main/src/cgeo/geocaching/maps/CGeoMap.java9
-rw-r--r--main/src/cgeo/geocaching/network/HtmlImage.java26
-rw-r--r--main/src/cgeo/geocaching/network/StatusUpdater.java7
-rw-r--r--main/src/cgeo/geocaching/sensors/GeoDataProvider.java14
-rw-r--r--main/src/cgeo/geocaching/settings/SettingsActivity.java2
-rw-r--r--main/src/cgeo/geocaching/ui/CacheListAdapter.java39
-rw-r--r--main/src/cgeo/geocaching/ui/CompassView.java7
-rw-r--r--main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java22
-rw-r--r--main/src/cgeo/geocaching/utils/RxUtils.java8
24 files changed, 418 insertions, 263 deletions
diff --git a/main/src/cgeo/geocaching/CacheDetailActivity.java b/main/src/cgeo/geocaching/CacheDetailActivity.java
index a66d181..11fe672 100644
--- a/main/src/cgeo/geocaching/CacheDetailActivity.java
+++ b/main/src/cgeo/geocaching/CacheDetailActivity.java
@@ -56,13 +56,12 @@ import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
-
import rx.Observable;
import rx.Observable.OnSubscribe;
import rx.Observer;
-import rx.Scheduler.Inner;
import rx.Subscriber;
import rx.android.observables.AndroidObservable;
+import rx.functions.Action0;
import rx.functions.Action1;
import rx.schedulers.Schedulers;
import rx.subscriptions.CompositeSubscription;
@@ -300,9 +299,9 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
final String realGeocode = geocode;
final String realGuid = guid;
- Schedulers.io().schedule(new Action1<Inner>() {
+ Schedulers.io().createWorker().schedule(new Action0() {
@Override
- public void call(final Inner inner) {
+ public void call() {
search = Geocache.searchByGeocode(realGeocode, StringUtils.isBlank(realGeocode) ? realGuid : null, 0, false, loadCacheHandler);
loadCacheHandler.sendMessage(Message.obtain());
}
@@ -681,7 +680,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
DETAILS(R.string.detail),
DESCRIPTION(R.string.cache_description),
LOGS(R.string.cache_logs),
- LOGSFRIENDS(R.string.cache_logsfriends),
+ LOGSFRIENDS(R.string.cache_logs_friends_and_own),
WAYPOINTS(R.string.cache_waypoints),
INVENTORY(R.string.cache_inventory),
IMAGES(R.string.cache_images);
@@ -2227,9 +2226,9 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc
protected void storeCache(final int listId, final StoreCacheHandler storeCacheHandler) {
progress.show(this, res.getString(R.string.cache_dialog_offline_save_title), res.getString(R.string.cache_dialog_offline_save_message), true, storeCacheHandler.cancelMessage());
- Schedulers.io().schedule(new Action1<Inner>() {
+ Schedulers.io().createWorker().schedule(new Action0() {
@Override
- public void call(final Inner inner) {
+ public void call() {
cache.store(listId, storeCacheHandler);
}
});
diff --git a/main/src/cgeo/geocaching/CacheListActivity.java b/main/src/cgeo/geocaching/CacheListActivity.java
index 0aec119..5a385a2 100644
--- a/main/src/cgeo/geocaching/CacheListActivity.java
+++ b/main/src/cgeo/geocaching/CacheListActivity.java
@@ -574,7 +574,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA
setMenuItemLabel(menu, R.id.menu_remove_from_history, R.string.cache_remove_from_history, R.string.cache_clear_history);
setMenuItemLabel(menu, R.id.menu_export, R.string.export, R.string.export);
- menu.findItem(R.id.menu_import_android).setVisible(Compatibility.isStorageAccessFrameworkAvailable());
+ menu.findItem(R.id.menu_import_android).setVisible(Compatibility.isStorageAccessFrameworkAvailable() && isOffline);
} catch (final RuntimeException e) {
Log.e("CacheListActivity.onPrepareOptionsMenu", e);
}
@@ -1323,6 +1323,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA
listId = list.id;
title = list.title;
+ type = CacheListType.OFFLINE;
Settings.saveLastList(listId);
diff --git a/main/src/cgeo/geocaching/CachePopup.java b/main/src/cgeo/geocaching/CachePopup.java
index 543be22..f91d275 100644
--- a/main/src/cgeo/geocaching/CachePopup.java
+++ b/main/src/cgeo/geocaching/CachePopup.java
@@ -11,7 +11,7 @@ import cgeo.geocaching.utils.CancellableHandler;
import cgeo.geocaching.utils.Log;
import org.apache.commons.lang3.StringUtils;
-import rx.Scheduler.Inner;
+import rx.functions.Action0;
import rx.functions.Action1;
import rx.schedulers.Schedulers;
@@ -126,9 +126,9 @@ public class CachePopup extends AbstractPopupActivity {
protected void storeCache(final int listId) {
final StoreCacheHandler storeCacheHandler = new StoreCacheHandler(R.string.cache_dialog_offline_save_message);
progress.show(CachePopup.this, res.getString(R.string.cache_dialog_offline_save_title), res.getString(R.string.cache_dialog_offline_save_message), true, storeCacheHandler.cancelMessage());
- Schedulers.io().schedule(new Action1<Inner>() {
+ Schedulers.io().createWorker().schedule(new Action0() {
@Override
- public void call(final Inner inner) {
+ public void call() {
cache.store(listId, storeCacheHandler);
invalidateOptionsMenuCompatible();
}
diff --git a/main/src/cgeo/geocaching/CgeoApplication.java b/main/src/cgeo/geocaching/CgeoApplication.java
index 09aee93..d503969 100644
--- a/main/src/cgeo/geocaching/CgeoApplication.java
+++ b/main/src/cgeo/geocaching/CgeoApplication.java
@@ -11,6 +11,9 @@ import rx.observables.ConnectableObservable;
import android.app.Application;
+import java.io.IOException;
+import java.lang.Thread.UncaughtExceptionHandler;
+
public class CgeoApplication extends Application {
private boolean forceRelog = false; // c:geo needs to log into cache providers
@@ -22,6 +25,34 @@ public class CgeoApplication extends Application {
private volatile IGeoData currentGeo = null;
private volatile float currentDirection = 0.0f;
+ private static final UncaughtExceptionHandler defaultHandler;
+
+ static {
+ defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+
+ Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
+
+ @Override
+ public void uncaughtException(Thread thread, Throwable ex) {
+ Log.e("UncaughtException", ex);
+ Throwable exx = ex;
+ while (exx.getCause() != null) {
+ exx = exx.getCause();
+ }
+ if (exx.getClass().equals(OutOfMemoryError.class))
+ {
+ try {
+ Log.e("OutOfMemory");
+ android.os.Debug.dumpHprofData("/sdcard/dump.hprof");
+ } catch (IOException e) {
+ Log.e("Error writing dump", e);
+ }
+ }
+ defaultHandler.uncaughtException(thread, ex);
+ }
+ });
+ }
+
public CgeoApplication() {
setInstance(this);
}
diff --git a/main/src/cgeo/geocaching/DataStore.java b/main/src/cgeo/geocaching/DataStore.java
index 32a4b64..350bc6b 100644
--- a/main/src/cgeo/geocaching/DataStore.java
+++ b/main/src/cgeo/geocaching/DataStore.java
@@ -1429,17 +1429,13 @@ public class DataStore {
}
}
- public static void saveLogsWithoutTransaction(final String geocode, final List<LogEntry> logs) {
+ public static void saveLogsWithoutTransaction(final String geocode, final Iterable<LogEntry> logs) {
// TODO delete logimages referring these logs
database.delete(dbTableLogs, "geocode = ?", new String[]{geocode});
- if (logs.isEmpty()) {
- return;
- }
-
- SQLiteStatement insertLog = PreparedStatements.getInsertLog();
+ final SQLiteStatement insertLog = PreparedStatements.getInsertLog();
final long timestamp = System.currentTimeMillis();
- for (LogEntry log : logs) {
+ for (final LogEntry log : logs) {
insertLog.bindString(1, geocode);
insertLog.bindLong(2, timestamp);
insertLog.bindLong(3, log.type.id);
@@ -1448,10 +1444,10 @@ public class DataStore {
insertLog.bindLong(6, log.date);
insertLog.bindLong(7, log.found);
insertLog.bindLong(8, log.friend ? 1 : 0);
- long logId = insertLog.executeInsert();
+ final long logId = insertLog.executeInsert();
if (log.hasLogImages()) {
- SQLiteStatement insertImage = PreparedStatements.getInsertLogImage();
- for (Image img : log.getLogImages()) {
+ final SQLiteStatement insertImage = PreparedStatements.getInsertLogImage();
+ for (final Image img : log.getLogImages()) {
insertImage.bindLong(1, logId);
insertImage.bindString(2, img.getTitle());
insertImage.bindString(3, img.getUrl());
@@ -2895,6 +2891,7 @@ public class DataStore {
}
if (excludeDisabled) {
where.append(" and ").append(dbTableCaches).append(".disabled == 0");
+ where.append(" and ").append(dbTableCaches).append(".archived == 0");
}
if (type != CacheType.ALL) {
where.append(" and ").append(dbTableCaches).append(".type == '").append(type.id).append('\'');
@@ -3164,7 +3161,7 @@ public class DataStore {
}
public static String[] getSuggestionsOwnerName(String input) {
- return getSuggestions(dbTableCaches, "owner", input);
+ return getSuggestions(dbTableCaches, "owner_real", input);
}
public static String[] getSuggestionsTrackableCode(String input) {
diff --git a/main/src/cgeo/geocaching/Geocache.java b/main/src/cgeo/geocaching/Geocache.java
index 19c15fd..8836115 100644
--- a/main/src/cgeo/geocaching/Geocache.java
+++ b/main/src/cgeo/geocaching/Geocache.java
@@ -35,7 +35,6 @@ import cgeo.geocaching.utils.MatcherWrapper;
import cgeo.geocaching.utils.UncertainProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.collections4.Predicate;
@@ -44,11 +43,9 @@ import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
-
import rx.Scheduler;
-import rx.Scheduler.Inner;
import rx.Subscription;
-import rx.functions.Action1;
+import rx.functions.Action0;
import android.app.Activity;
import android.content.Intent;
@@ -1438,9 +1435,9 @@ public class Geocache implements ICache, IWaypoint {
}
public Subscription drop(final Handler handler, final Scheduler scheduler) {
- return scheduler.schedule(new Action1<Inner>() {
+ return scheduler.createWorker().schedule(new Action0() {
@Override
- public void call(final Inner inner) {
+ public void call() {
try {
dropSynchronous();
handler.sendMessage(Message.obtain());
@@ -1499,9 +1496,9 @@ public class Geocache implements ICache, IWaypoint {
}
public Subscription refresh(final int newListId, final CancellableHandler handler, final Scheduler scheduler) {
- return scheduler.schedule(new Action1<Inner>() {
+ return scheduler.createWorker().schedule(new Action0() {
@Override
- public void call(final Inner inner) {
+ public void call() {
refreshSynchronous(newListId, handler);
handler.sendEmptyMessage(CancellableHandler.DONE);
}
diff --git a/main/src/cgeo/geocaching/LogCacheActivity.java b/main/src/cgeo/geocaching/LogCacheActivity.java
index 2b05263..cccd154 100644
--- a/main/src/cgeo/geocaching/LogCacheActivity.java
+++ b/main/src/cgeo/geocaching/LogCacheActivity.java
@@ -516,7 +516,7 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia
if (logResult.getPostLogResult() == StatusCode.NO_ERROR) {
// update geocache in DB
- if (typeSelected == LogType.FOUND_IT || typeSelected == LogType.ATTENDED) {
+ if (typeSelected == LogType.FOUND_IT || typeSelected == LogType.ATTENDED || typeSelected == LogType.WEBCAM_PHOTO_TAKEN) {
cache.setFound(true);
cache.setVisitedDate(new Date().getTime());
}
diff --git a/main/src/cgeo/geocaching/SearchActivity.java b/main/src/cgeo/geocaching/SearchActivity.java
index 2a37e27..d3bb2d8 100644
--- a/main/src/cgeo/geocaching/SearchActivity.java
+++ b/main/src/cgeo/geocaching/SearchActivity.java
@@ -174,8 +174,20 @@ public class SearchActivity extends AbstractActivity {
}
private void init() {
- buttonLatitude.setOnClickListener(new FindByCoordsAction());
- buttonLongitude.setOnClickListener(new FindByCoordsAction());
+ buttonLatitude.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ updateCoordinates();
+ }
+ });
+ buttonLongitude.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ updateCoordinates();
+ }
+ });
buttonSearchCoords.setOnClickListener(new View.OnClickListener() {
@@ -277,21 +289,17 @@ public class SearchActivity extends AbstractActivity {
}
}
- private class FindByCoordsAction implements OnClickListener {
-
- @Override
- public void onClick(final View arg0) {
- final CoordinatesInputDialog coordsDialog = new CoordinatesInputDialog(SearchActivity.this, null, null, app.currentGeo());
- coordsDialog.setCancelable(true);
- coordsDialog.setOnCoordinateUpdate(new CoordinatesInputDialog.CoordinateUpdate() {
- @Override
- public void update(final Geopoint gp) {
- buttonLatitude.setText(gp.format(GeopointFormatter.Format.LAT_DECMINUTE));
- buttonLongitude.setText(gp.format(GeopointFormatter.Format.LON_DECMINUTE));
- }
- });
- coordsDialog.show();
- }
+ private void updateCoordinates() {
+ final CoordinatesInputDialog coordsDialog = new CoordinatesInputDialog(SearchActivity.this, null, null, app.currentGeo());
+ coordsDialog.setCancelable(true);
+ coordsDialog.setOnCoordinateUpdate(new CoordinatesInputDialog.CoordinateUpdate() {
+ @Override
+ public void update(final Geopoint gp) {
+ buttonLatitude.setText(gp.format(GeopointFormatter.Format.LAT_DECMINUTE));
+ buttonLongitude.setText(gp.format(GeopointFormatter.Format.LON_DECMINUTE));
+ }
+ });
+ coordsDialog.show();
}
private void findByCoordsFn() {
diff --git a/main/src/cgeo/geocaching/SearchResult.java b/main/src/cgeo/geocaching/SearchResult.java
index 12a2522..3fe1b53 100644
--- a/main/src/cgeo/geocaching/SearchResult.java
+++ b/main/src/cgeo/geocaching/SearchResult.java
@@ -12,6 +12,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 rx.Observable;
import rx.functions.Func1;
import rx.functions.Func2;
@@ -60,6 +61,14 @@ public class SearchResult implements Parcelable {
}
/**
+ * Build a new empty search result with an error status.
+ */
+ public SearchResult(final StatusCode statusCode) {
+ this();
+ error = statusCode;
+ }
+
+ /**
* Copy a search result, for example to apply different filters on it.
*
* @param searchResult the original search result, which cannot be null
@@ -218,7 +227,7 @@ public class SearchResult implements Parcelable {
int excluded = 0;
for (Geocache cache : caches) {
// Is there any reason to exclude the cache from the list?
- final boolean excludeCache = (excludeDisabled && cache.isDisabled()) ||
+ final boolean excludeCache = (excludeDisabled && (cache.isDisabled() || cache.isArchived())) ||
(excludeMine && (cache.isOwner() || cache.isFound())) ||
(!cacheType.contains(cache));
if (excludeCache) {
diff --git a/main/src/cgeo/geocaching/StaticMapsProvider.java b/main/src/cgeo/geocaching/StaticMapsProvider.java
index eaab159..ae61112 100644
--- a/main/src/cgeo/geocaching/StaticMapsProvider.java
+++ b/main/src/cgeo/geocaching/StaticMapsProvider.java
@@ -11,6 +11,7 @@ import cgeo.geocaching.utils.FileUtils;
import cgeo.geocaching.utils.Log;
import ch.boye.httpclientandroidlib.HttpResponse;
+
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jdt.annotation.NonNull;
@@ -213,7 +214,7 @@ public final class StaticMapsProvider {
url.append("marker_cache_").append(cache.getType().id);
if (cache.isFound()) {
url.append("_found");
- } else if (cache.isDisabled()) {
+ } else if (cache.isDisabled() || cache.isArchived()) {
url.append("_disabled");
}
url.append(".png");
diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java
index 5422c11..73d6c06 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCParser.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java
@@ -47,6 +47,14 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import rx.Observable;
+import rx.Observable.OnSubscribe;
+import rx.Subscriber;
+import rx.functions.Action1;
+import rx.functions.Func0;
+import rx.functions.Func2;
+import rx.schedulers.Schedulers;
+
import android.net.Uri;
import android.text.Html;
@@ -335,32 +343,50 @@ public abstract class GCParser {
}
static SearchResult parseCache(final String page, final CancellableHandler handler) {
- final SearchResult searchResult = parseCacheFromText(page, handler);
+ final ImmutablePair<StatusCode, Geocache> parsed = parseCacheFromText(page, handler);
// 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();
- if (CancellableHandler.isCancelled(handler)) {
- return null;
- }
+ if (parsed.left != StatusCode.NO_ERROR) {
+ return new SearchResult(parsed.left);
+ }
+
+ final Geocache cache = parsed.right;
+ getExtraOnlineInfo(cache, page, handler);
+ // too late: it is already stored through parseCacheFromText
+ cache.setDetailedUpdatedNow();
+ if (CancellableHandler.isCancelled(handler)) {
+ return null;
+ }
- // save full detailed caches
- CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_cache);
- DataStore.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB));
+ // save full detailed caches
+ CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_cache);
+ DataStore.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB));
- // update progress message so user knows we're still working. This is more of a place holder than
- // actual indication of what the program is doing
- CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_render);
+ // update progress message so user knows we're still working. This is more of a place holder than
+ // actual indication of what the program is doing
+ CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_render);
+ return new SearchResult(cache);
+ }
+
+ static SearchResult parseAndSaveCacheFromText(final String page, @NonNull final CancellableHandler handler) {
+ final ImmutablePair<StatusCode, Geocache> parsed = parseCacheFromText(page, handler);
+ final SearchResult result = new SearchResult(parsed.left);
+ if (parsed.left == StatusCode.NO_ERROR) {
+ result.addAndPutInCache(Collections.singletonList(parsed.right));
+ DataStore.saveLogsWithoutTransaction(parsed.right.getGeocode(), getLogsFromDetails(page).toBlockingObservable().toIterable());
}
- return searchResult;
+ return result;
}
- static SearchResult parseCacheFromText(final String pageIn, final CancellableHandler handler) {
+ /**
+ * Parse cache from text and return either an error code or a cache object in a pair. Note that inline logs are
+ * not parsed nor saved, while the cache itself is.
+ *
+ * @param pageIn the page text to parse
+ * @param handler the handler to send the progress notifications to
+ * @return a pair, with a {@link StatusCode} on the left, and a non-nulll cache objet on the right
+ * iff the status code is {@link StatusCode.NO_ERROR}.
+ */
+ static private ImmutablePair<StatusCode, Geocache> parseCacheFromText(final String pageIn, @NonNull final CancellableHandler handler) {
CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_details);
if (StringUtils.isBlank(pageIn)) {
@@ -368,22 +394,17 @@ public abstract class GCParser {
return null;
}
- final SearchResult searchResult = new SearchResult();
-
if (pageIn.contains(GCConstants.STRING_UNPUBLISHED_OTHER) || pageIn.contains(GCConstants.STRING_UNPUBLISHED_FROM_SEARCH)) {
- searchResult.setError(StatusCode.UNPUBLISHED_CACHE);
- return searchResult;
+ return ImmutablePair.of(StatusCode.UNPUBLISHED_CACHE, null);
}
if (pageIn.contains(GCConstants.STRING_PREMIUMONLY_1) || pageIn.contains(GCConstants.STRING_PREMIUMONLY_2)) {
- searchResult.setError(StatusCode.PREMIUM_ONLY);
- return searchResult;
+ return ImmutablePair.of(StatusCode.PREMIUM_ONLY, null);
}
final String cacheName = Html.fromHtml(TextUtils.getMatch(pageIn, GCConstants.PATTERN_NAME, true, "")).toString();
if (GCConstants.STRING_UNKNOWN_ERROR.equalsIgnoreCase(cacheName)) {
- searchResult.setError(StatusCode.UNKNOWN_ERROR);
- return searchResult;
+ return ImmutablePair.of(StatusCode.UNKNOWN_ERROR, null);
}
// first handle the content with line breaks, then trim everything for easier matching and reduced memory consumption in parsed fields
@@ -726,14 +747,11 @@ public abstract class GCParser {
// last check for necessary cache conditions
if (StringUtils.isBlank(cache.getGeocode())) {
- searchResult.setError(StatusCode.UNKNOWN_ERROR);
- return searchResult;
+ return ImmutablePair.of(StatusCode.UNKNOWN_ERROR, null);
}
cache.setDetailedUpdatedNow();
- searchResult.addAndPutInCache(Collections.singletonList(cache));
- DataStore.saveLogsWithoutTransaction(cache.getGeocode(), getLogsFromDetails(page, false));
- return searchResult;
+ return ImmutablePair.of(StatusCode.NO_ERROR, cache);
}
private static String getNumberString(final String numberWithPunctuation) {
@@ -1622,116 +1640,142 @@ public abstract class GCParser {
*
* @param page
* the text of the details page
- * @param friends
- * 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
+ * @return a list of log entries which will be empty if the logs could not be retrieved
*
*/
- @Nullable
- private static List<LogEntry> getLogsFromDetails(final String page, final boolean friends) {
- String rawResponse;
+ @NonNull
+ private static Observable<LogEntry> getLogsFromDetails(final String page) {
+ // extract embedded JSON data from page
+ return parseLogs(false, TextUtils.getMatch(page, GCConstants.PATTERN_LOGBOOK, ""));
+ }
- if (friends) {
- final MatcherWrapper userTokenMatcher = new MatcherWrapper(GCConstants.PATTERN_USERTOKEN, page);
- if (!userTokenMatcher.find()) {
- Log.e("GCParser.loadLogsFromDetails: unable to extract userToken");
- return null;
- }
+ private enum SpecialLogs {
+ FRIENDS("sf"),
+ OWN("sp");
- final String userToken = userTokenMatcher.group(1);
- final Parameters params = new Parameters(
- "tkn", userToken,
- "idx", "1",
- "num", String.valueOf(GCConstants.NUMBER_OF_LOGS),
- "decrypt", "true",
- // "sp", Boolean.toString(personal), // personal logs
- "sf", Boolean.toString(friends));
+ final String paramName;
- final HttpResponse response = Network.getRequest("http://www.geocaching.com/seek/geocache.logbook", params);
- if (response == null) {
- Log.e("GCParser.loadLogsFromDetails: cannot log logs, response is null");
- return null;
- }
- final int statusCode = response.getStatusLine().getStatusCode();
- if (statusCode != 200) {
- Log.e("GCParser.loadLogsFromDetails: error " + statusCode + " when requesting log information");
- return null;
- }
- rawResponse = Network.getResponseData(response);
- if (rawResponse == null) {
- Log.e("GCParser.loadLogsFromDetails: unable to read whole response");
- return null;
- }
- } else {
- // extract embedded JSON data from page
- rawResponse = TextUtils.getMatch(page, GCConstants.PATTERN_LOGBOOK, "");
+ SpecialLogs(String paramName) {
+ this.paramName = paramName;
}
- return parseLogs(friends, rawResponse);
+ private String getParamName() {
+ return this.paramName;
+ }
}
- private static List<LogEntry> parseLogs(final boolean friends, String rawResponse) {
- final List<LogEntry> logs = new ArrayList<LogEntry>();
-
- // for non logged in users the log book is not shown
- if (StringUtils.isBlank(rawResponse)) {
- return logs;
- }
+ /**
+ * Extract special logs (friends, own) through seperate request.
+ *
+ * @param page
+ * The page to extrat userToken from
+ * @param logType
+ * The logType to request
+ * @return Observable<LogEntry> The logs
+ */
+ private static Observable<LogEntry> getSpecialLogs(final String page, final SpecialLogs logType) {
+ return Observable.defer(new Func0<Observable<? extends LogEntry>>() {
+ @Override
+ public Observable<? extends LogEntry> call() {
+ final MatcherWrapper userTokenMatcher = new MatcherWrapper(GCConstants.PATTERN_USERTOKEN, page);
+ if (!userTokenMatcher.find()) {
+ Log.e("GCParser.loadLogsFromDetails: unable to extract userToken");
+ return Observable.empty();
+ }
- try {
- final JSONObject resp = new JSONObject(rawResponse);
- if (!resp.getString("status").equals("success")) {
- Log.e("GCParser.loadLogsFromDetails: status is " + resp.getString("status"));
- return null;
+ final String userToken = userTokenMatcher.group(1);
+ final Parameters params = new Parameters(
+ "tkn", userToken,
+ "idx", "1",
+ "num", String.valueOf(GCConstants.NUMBER_OF_LOGS),
+ logType.getParamName(), Boolean.toString(Boolean.TRUE),
+ "decrypt", "true");
+ final HttpResponse response = Network.getRequest("http://www.geocaching.com/seek/geocache.logbook", params);
+ if (response == null) {
+ Log.e("GCParser.loadLogsFromDetails: cannot log logs, response is null");
+ return Observable.empty();
+ }
+ final int statusCode = response.getStatusLine().getStatusCode();
+ if (statusCode != 200) {
+ Log.e("GCParser.loadLogsFromDetails: error " + statusCode + " when requesting log information");
+ return Observable.empty();
+ }
+ String rawResponse = Network.getResponseData(response);
+ if (rawResponse == null) {
+ Log.e("GCParser.loadLogsFromDetails: unable to read whole response");
+ return Observable.empty();
+ }
+ return parseLogs(true, rawResponse);
}
+ }).subscribeOn(Schedulers.io());
+ }
- final JSONArray data = resp.getJSONArray("data");
+ private static Observable<LogEntry> parseLogs(final boolean markAsFriendsLog, final String rawResponse) {
+ return Observable.create(new OnSubscribe<LogEntry>() {
+ @Override
+ public void call(final Subscriber<? super LogEntry> subscriber) {
+ // for non logged in users the log book is not shown
+ if (StringUtils.isBlank(rawResponse)) {
+ subscriber.onCompleted();
+ return;
+ }
- for (int index = 0; index < data.length(); index++) {
- final JSONObject entry = data.getJSONObject(index);
+ try {
+ final JSONObject resp = new JSONObject(rawResponse);
+ if (!resp.getString("status").equals("success")) {
+ Log.e("GCParser.loadLogsFromDetails: status is " + resp.getString("status"));
+ subscriber.onCompleted();
+ return;
+ }
- // FIXME: use the "LogType" field instead of the "LogTypeImage" one.
- final String logIconNameExt = entry.optString("LogTypeImage", ".gif");
- final String logIconName = logIconNameExt.substring(0, logIconNameExt.length() - 4);
+ final JSONArray data = resp.getJSONArray("data");
- long date = 0;
- try {
- date = GCLogin.parseGcCustomDate(entry.getString("Visited")).getTime();
- } catch (final ParseException e) {
- Log.e("GCParser.loadLogsFromDetails: failed to parse log date.");
- }
+ for (int index = 0; index < data.length(); index++) {
+ final JSONObject entry = data.getJSONObject(index);
- // TODO: we should update our log data structure to be able to record
- // proper coordinates, and make them clickable. In the meantime, it is
- // better to integrate those coordinates into the text rather than not
- // display them at all.
- final String latLon = entry.getString("LatLonString");
- final String logText = (StringUtils.isEmpty(latLon) ? "" : (latLon + "<br/><br/>")) + TextUtils.removeControlCharacters(entry.getString("LogText"));
- final LogEntry logDone = new LogEntry(
- TextUtils.removeControlCharacters(entry.getString("UserName")),
- date,
- LogType.getByIconName(logIconName),
- logText);
- logDone.found = entry.getInt("GeocacheFindCount");
- logDone.friend = friends;
-
- final JSONArray images = entry.getJSONArray("Images");
- for (int i = 0; i < images.length(); i++) {
- final JSONObject image = images.getJSONObject(i);
- final String url = "http://imgcdn.geocaching.com/cache/log/large/" + image.getString("FileName");
- final String title = TextUtils.removeControlCharacters(image.getString("Name"));
- final Image logImage = new Image(url, title);
- logDone.addLogImage(logImage);
- }
+ // FIXME: use the "LogType" field instead of the "LogTypeImage" one.
+ final String logIconNameExt = entry.optString("LogTypeImage", ".gif");
+ final String logIconName = logIconNameExt.substring(0, logIconNameExt.length() - 4);
- logs.add(logDone);
- }
- } catch (final JSONException e) {
- // failed to parse logs
- Log.w("GCParser.loadLogsFromDetails: Failed to parse cache logs", e);
- }
+ long date = 0;
+ try {
+ date = GCLogin.parseGcCustomDate(entry.getString("Visited")).getTime();
+ } catch (final ParseException e) {
+ Log.e("GCParser.loadLogsFromDetails: failed to parse log date.");
+ }
- return logs;
+ // TODO: we should update our log data structure to be able to record
+ // proper coordinates, and make them clickable. In the meantime, it is
+ // better to integrate those coordinates into the text rather than not
+ // display them at all.
+ final String latLon = entry.getString("LatLonString");
+ final String logText = (StringUtils.isEmpty(latLon) ? "" : (latLon + "<br/><br/>")) + TextUtils.removeControlCharacters(entry.getString("LogText"));
+ final LogEntry logDone = new LogEntry(
+ TextUtils.removeControlCharacters(entry.getString("UserName")),
+ date,
+ LogType.getByIconName(logIconName),
+ logText);
+ logDone.found = entry.getInt("GeocacheFindCount");
+ logDone.friend = markAsFriendsLog;
+
+ final JSONArray images = entry.getJSONArray("Images");
+ for (int i = 0; i < images.length(); i++) {
+ final JSONObject image = images.getJSONObject(i);
+ final String url = "http://imgcdn.geocaching.com/cache/log/large/" + image.getString("FileName");
+ final String title = TextUtils.removeControlCharacters(image.getString("Name"));
+ final Image logImage = new Image(url, title);
+ logDone.addLogImage(logImage);
+ }
+
+ subscriber.onNext(logDone);
+ }
+ } catch (final JSONException e) {
+ // failed to parse logs
+ Log.w("GCParser.loadLogsFromDetails: Failed to parse cache logs", e);
+ }
+ subscriber.onCompleted();
+ }
+ });
}
@NonNull
@@ -1823,32 +1867,39 @@ public abstract class GCParser {
}
private static void getExtraOnlineInfo(final Geocache cache, final String page, final CancellableHandler handler) {
+ // This method starts the page parsing for logs in the background, as well as retrieve the friends and own logs
+ // if requested. It merges them and stores them in the background, while the rating is retrieved if needed and
+ // stored. Then we wait for the log merging and saving to be completed before returning.
if (CancellableHandler.isCancelled(handler)) {
return;
}
- //cache.setLogs(loadLogsFromDetails(page, cache, false));
+ final Observable<LogEntry> logs = getLogsFromDetails(page).subscribeOn(Schedulers.computation());
+ Observable<LogEntry> specialLogs;
if (Settings.isFriendLogsWanted()) {
CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_logs);
- final List<LogEntry> friendLogs = getLogsFromDetails(page, true);
- if (friendLogs != null && !friendLogs.isEmpty()) {
- // create new list, as the existing log list is immutable
- ArrayList<LogEntry> mergedLogs = new ArrayList<LogEntry>(cache.getLogs());
- for (final LogEntry log : friendLogs) {
- if (mergedLogs.contains(log)) {
- mergedLogs.get(mergedLogs.indexOf(log)).friend = true;
- } else {
- mergedLogs.add(log);
+ specialLogs = Observable.merge(getSpecialLogs(page, SpecialLogs.FRIENDS),
+ getSpecialLogs(page, SpecialLogs.OWN));
+ } else {
+ CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_logs);
+ specialLogs = Observable.empty();
+ }
+ final Observable<List<LogEntry>> mergedLogs = Observable.zip(logs.toList(), specialLogs.toList(),
+ new Func2<List<LogEntry>, List<LogEntry>, List<LogEntry>>() {
+ @Override
+ public List<LogEntry> call(final List<LogEntry> logEntries, final List<LogEntry> specialLogEntries) {
+ mergeFriendsLogs(logEntries, specialLogEntries);
+ return logEntries;
}
- }
- DataStore.saveLogsWithoutTransaction(cache.getGeocode(), mergedLogs);
- }
- }
-
- if (Settings.isRatingWanted()) {
- if (CancellableHandler.isCancelled(handler)) {
- return;
- }
+ }).cache();
+ mergedLogs.subscribe(new Action1<List<LogEntry>>() {
+ @Override
+ public void call(final List<LogEntry> logEntries) {
+ DataStore.saveLogsWithoutTransaction(cache.getGeocode(), logEntries);
+ }
+ });
+
+ if (Settings.isRatingWanted() && !CancellableHandler.isCancelled(handler)) {
CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_gcvote);
final GCVoteRating rating = GCVote.getRating(cache.getGuid(), cache.getGeocode());
if (rating != null) {
@@ -1857,6 +1908,28 @@ public abstract class GCParser {
cache.setMyVote(rating.getMyVote());
}
}
+
+ // Wait for completion of logs parsing, retrieving and merging
+ mergedLogs.toBlockingObservable().last();
+ }
+
+ /**
+ * Merge log entries and mark them as friends logs (personal and friends) to identify
+ * them on friends/personal logs tab.
+ *
+ * @param mergedLogs
+ * the list to merge logs with
+ * @param logsToMerge
+ * the list of logs to merge
+ */
+ private static void mergeFriendsLogs(final List<LogEntry> mergedLogs, final Iterable<LogEntry> logsToMerge) {
+ for (final LogEntry log : logsToMerge) {
+ if (mergedLogs.contains(log)) {
+ mergedLogs.get(mergedLogs.indexOf(log)).friend = true;
+ } else {
+ mergedLogs.add(log);
+ }
+ }
}
public static boolean uploadModifiedCoordinates(Geocache cache, Geopoint wpt) {
diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java
index 049c633..bdcd78e 100644
--- a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java
+++ b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java
@@ -3,6 +3,7 @@ package cgeo.geocaching.connector.oc;
import cgeo.geocaching.CgeoApplication;
import cgeo.geocaching.DataStore;
import cgeo.geocaching.Geocache;
+import cgeo.geocaching.ICache;
import cgeo.geocaching.LogCacheActivity;
import cgeo.geocaching.SearchResult;
import cgeo.geocaching.connector.ILoggingManager;
@@ -149,6 +150,11 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente
}
@Override
+ public boolean isOwner(ICache cache) {
+ return StringUtils.equals(cache.getOwnerDisplayName(), getUserName());
+ }
+
+ @Override
public String getUserName() {
return userInfo.getName();
}
diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
index 3c93488..1294353 100644
--- a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
+++ b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
@@ -328,8 +328,11 @@ final class OkapiClient {
parseCoreCache(response, cache);
// not used: url
- final JSONObject owner = response.getJSONObject(CACHE_OWNER);
- cache.setOwnerDisplayName(parseUser(owner));
+ final JSONObject ownerObject = response.getJSONObject(CACHE_OWNER);
+ final String owner = parseUser(ownerObject);
+ cache.setOwnerDisplayName(owner);
+ // OpenCaching has no distinction between user id and user display name. Set the ID anyway to simplify c:geo workflows.
+ cache.setOwnerUserId(owner);
cache.getLogCounts().put(LogType.FOUND_IT, response.getInt(CACHE_FOUNDS));
cache.getLogCounts().put(LogType.DIDNT_FIND_IT, response.getInt(CACHE_NOTFOUNDS));
diff --git a/main/src/cgeo/geocaching/filter/StateFilter.java b/main/src/cgeo/geocaching/filter/StateFilter.java
index f452259..fd14b69 100644
--- a/main/src/cgeo/geocaching/filter/StateFilter.java
+++ b/main/src/cgeo/geocaching/filter/StateFilter.java
@@ -32,6 +32,19 @@ abstract class StateFilter extends AbstractFilter {
}
+ static class StateNotFoundFilter extends StateFilter {
+
+ public StateNotFoundFilter() {
+ super(res.getString(R.string.cache_not_status_found));
+ }
+
+ @Override
+ public boolean accepts(final Geocache cache) {
+ return !cache.isFound();
+ }
+
+ }
+
static class StateArchivedFilter extends StateFilter {
public StateArchivedFilter() {
super(res.getString(R.string.cache_status_archived));
@@ -115,6 +128,7 @@ abstract class StateFilter extends AbstractFilter {
public List<StateFilter> getFilters() {
final List<StateFilter> filters = new ArrayList<StateFilter>(6);
filters.add(new StateFoundFilter());
+ filters.add(new StateNotFoundFilter());
filters.add(new StateArchivedFilter());
filters.add(new StateDisabledFilter());
filters.add(new StatePremiumFilter());
diff --git a/main/src/cgeo/geocaching/list/StoredList.java b/main/src/cgeo/geocaching/list/StoredList.java
index e557fc8..84e1163 100644
--- a/main/src/cgeo/geocaching/list/StoredList.java
+++ b/main/src/cgeo/geocaching/list/StoredList.java
@@ -16,6 +16,7 @@ import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.res.Resources;
+import java.lang.ref.WeakReference;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
@@ -58,12 +59,12 @@ public final class StoredList extends AbstractList {
}
public static class UserInterface {
- private final Activity activity;
+ private final WeakReference<Activity> activityRef;
private final CgeoApplication app;
private final Resources res;
- public UserInterface(final Activity activity) {
- this.activity = activity;
+ public UserInterface(final @NonNull Activity activity) {
+ this.activityRef = new WeakReference<Activity>(activity);
app = CgeoApplication.getInstance();
res = app.getResources();
}
@@ -104,6 +105,7 @@ public final class StoredList extends AbstractList {
final CharSequence[] items = new CharSequence[listsTitle.size()];
+ final Activity activity = activityRef.get();
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(res.getString(titleId));
builder.setItems(listsTitle.toArray(items), new DialogInterface.OnClickListener() {
@@ -151,6 +153,10 @@ public final class StoredList extends AbstractList {
@SuppressWarnings("unused")
@Override
public void call(final String listName) {
+ final Activity activity = activityRef.get();
+ if (activity == null) {
+ return;
+ }
final int newId = DataStore.createList(listName);
new StoredList(newId, listName, 0);
@@ -165,6 +171,10 @@ public final class StoredList extends AbstractList {
}
private void handleListNameInput(final String defaultValue, int dialogTitle, int buttonTitle, final Action1<String> runnable) {
+ final Activity activity = activityRef.get();
+ if (activity == null) {
+ return;
+ }
Dialogs.input(activity, dialogTitle, defaultValue, buttonTitle, new Action1<String>() {
@Override
diff --git a/main/src/cgeo/geocaching/maps/CGeoMap.java b/main/src/cgeo/geocaching/maps/CGeoMap.java
index 00aee36..b5d0b0e 100644
--- a/main/src/cgeo/geocaching/maps/CGeoMap.java
+++ b/main/src/cgeo/geocaching/maps/CGeoMap.java
@@ -43,9 +43,8 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.eclipse.jdt.annotation.NonNull;
-
-import rx.Scheduler;
import rx.Subscription;
+import rx.functions.Action0;
import rx.functions.Action1;
import rx.schedulers.Schedulers;
import rx.subscriptions.CompositeSubscription;
@@ -1007,9 +1006,9 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto
* loading timer Triggers every 250ms and checks for viewport change and starts a {@link LoadRunnable}.
*/
private Subscription startLoadTimer() {
- return Schedulers.newThread().schedulePeriodically(new Action1<Scheduler.Inner>() {
+ return Schedulers.newThread().createWorker().schedulePeriodically(new Action0() {
@Override
- public void call(Scheduler.Inner inner) {
+ public void call() {
try {
if (mapView != null) {
// get current viewport
@@ -1373,7 +1372,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto
List<Geocache> removeList = new ArrayList<Geocache>();
for (Geocache cache : caches) {
- if ((excludeMine && cache.isFound()) || (excludeMine && cache.isOwner()) || (excludeDisabled && cache.isDisabled())) {
+ if ((excludeMine && cache.isFound()) || (excludeMine && cache.isOwner()) || (excludeDisabled && cache.isDisabled()) || (excludeDisabled && cache.isArchived())) {
removeList.add(cache);
}
}
diff --git a/main/src/cgeo/geocaching/network/HtmlImage.java b/main/src/cgeo/geocaching/network/HtmlImage.java
index 9c55fe9..167559d 100644
--- a/main/src/cgeo/geocaching/network/HtmlImage.java
+++ b/main/src/cgeo/geocaching/network/HtmlImage.java
@@ -13,21 +13,20 @@ import cgeo.geocaching.utils.Log;
import cgeo.geocaching.utils.RxUtils;
import ch.boye.httpclientandroidlib.HttpResponse;
+
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
+
import rx.Observable;
import rx.Observable.OnSubscribe;
-import rx.Scheduler;
-import rx.Scheduler.Inner;
import rx.Subscriber;
-import rx.functions.Action1;
+import rx.functions.Action0;
import rx.functions.Func0;
import rx.functions.Func1;
-import rx.schedulers.Schedulers;
import rx.subjects.PublishSubject;
import rx.subscriptions.CompositeSubscription;
@@ -44,6 +43,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Date;
+import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -94,8 +94,7 @@ public class HtmlImage implements Html.ImageGetter {
final private PublishSubject<Observable<String>> loading = PublishSubject.create();
final Observable<String> waitForEnd = Observable.merge(loading).publish().refCount();
final CompositeSubscription subscription = new CompositeSubscription(waitForEnd.subscribe());
- final private Scheduler downloadScheduler = Schedulers.executor(new ThreadPoolExecutor(10, 10, 5, TimeUnit.SECONDS,
- new LinkedBlockingQueue<Runnable>()));
+ final private Executor downloadExecutor = new ThreadPoolExecutor(10, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
public HtmlImage(final String geocode, final boolean returnErrorImage, final int listId, final boolean onlySave) {
this.geocode = geocode;
@@ -152,9 +151,9 @@ public class HtmlImage implements Html.ImageGetter {
@Override
public void call(final Subscriber<? super BitmapDrawable> subscriber) {
subscription.add(subscriber);
- subscriber.add(RxUtils.computationScheduler.schedule(new Action1<Inner>() {
+ subscriber.add(RxUtils.computationScheduler.createWorker().schedule(new Action0() {
@Override
- public void call(final Inner inner) {
+ public void call() {
final Pair<BitmapDrawable, Boolean> loaded = loadFromDisk();
final BitmapDrawable bitmap = loaded.getLeft();
if (loaded.getRight()) {
@@ -165,12 +164,11 @@ public class HtmlImage implements Html.ImageGetter {
if (bitmap != null && !onlySave) {
subscriber.onNext(bitmap);
}
- subscriber.add(downloadScheduler.schedule(new Action1<Inner>() {
- @Override
- public void call(final Inner inner) {
+ downloadExecutor.execute(new Runnable() {
+ @Override public void run() {
downloadAndSave(subscriber);
}
- }));
+ });
}
}));
}
@@ -205,9 +203,9 @@ public class HtmlImage implements Html.ImageGetter {
if (onlySave) {
subscriber.onCompleted();
} else {
- RxUtils.computationScheduler.schedule(new Action1<Inner>() {
+ RxUtils.computationScheduler.createWorker().schedule(new Action0() {
@Override
- public void call(final Inner inner) {
+ public void call() {
final Pair<BitmapDrawable, Boolean> loaded = loadFromDisk();
final BitmapDrawable image = loaded.getLeft();
if (image != null) {
diff --git a/main/src/cgeo/geocaching/network/StatusUpdater.java b/main/src/cgeo/geocaching/network/StatusUpdater.java
index 4055f01..bf9ebdf 100644
--- a/main/src/cgeo/geocaching/network/StatusUpdater.java
+++ b/main/src/cgeo/geocaching/network/StatusUpdater.java
@@ -5,10 +5,9 @@ import cgeo.geocaching.utils.Version;
import org.json.JSONException;
import org.json.JSONObject;
-import rx.Scheduler;
+import rx.functions.Action0;
import rx.schedulers.Schedulers;
import rx.subjects.BehaviorSubject;
-import rx.functions.Action1;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
@@ -52,9 +51,9 @@ public class StatusUpdater {
final static public BehaviorSubject<Status> latestStatus = BehaviorSubject.create(Status.defaultStatus(null));
static {
- Schedulers.io().schedulePeriodically(new Action1<Scheduler.Inner>() {
+ Schedulers.io().createWorker().schedulePeriodically(new Action0() {
@Override
- public void call(final Scheduler.Inner inner) {
+ public void call() {
final JSONObject response =
Network.requestJSON("http://status.cgeo.org/api/status.json",
new Parameters("version_code", String.valueOf(Version.getVersionCode(CgeoApplication.getInstance())),
diff --git a/main/src/cgeo/geocaching/sensors/GeoDataProvider.java b/main/src/cgeo/geocaching/sensors/GeoDataProvider.java
index a77b477..a68f2a6 100644
--- a/main/src/cgeo/geocaching/sensors/GeoDataProvider.java
+++ b/main/src/cgeo/geocaching/sensors/GeoDataProvider.java
@@ -1,18 +1,15 @@
package cgeo.geocaching.sensors;
-import android.os.*;
import cgeo.geocaching.utils.Log;
-
import cgeo.geocaching.utils.StartableHandlerThread;
+
import org.apache.commons.lang3.StringUtils;
import rx.Observable;
import rx.Observable.OnSubscribe;
-import rx.Scheduler.Inner;
import rx.Subscriber;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action0;
-import rx.functions.Action1;
import rx.observables.ConnectableObservable;
import rx.subjects.BehaviorSubject;
import rx.subscriptions.CompositeSubscription;
@@ -24,6 +21,7 @@ import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
+import android.os.Bundle;
import java.util.concurrent.TimeUnit;
@@ -98,9 +96,9 @@ public class GeoDataProvider implements OnSubscribe<IGeoData> {
@Override
public Subscription connect() {
final CompositeSubscription subscription = new CompositeSubscription();
- AndroidSchedulers.handlerThread(handlerThread.getHandler()).schedule(new Action1<Inner>() {
+ AndroidSchedulers.handlerThread(handlerThread.getHandler()).createWorker().schedule(new Action0() {
@Override
- public void call(final Inner inner) {
+ public void call() {
synchronized(lock) {
if (count++ == 0) {
Log.d("GeoDataProvider: starting the GPS and network listeners" + " (" + ++debugSessionCounter + ")");
@@ -118,9 +116,9 @@ public class GeoDataProvider implements OnSubscribe<IGeoData> {
subscription.add(Subscriptions.create(new Action0() {
@Override
public void call() {
- AndroidSchedulers.handlerThread(handlerThread.getHandler()).schedule(new Action1<Inner>() {
+ AndroidSchedulers.handlerThread(handlerThread.getHandler()).createWorker().schedule(new Action0() {
@Override
- public void call(final Inner inner) {
+ public void call() {
synchronized (lock) {
if (--count == 0) {
Log.d("GeoDataProvider: stopping the GPS and network listeners" + " (" + debugSessionCounter + ")");
diff --git a/main/src/cgeo/geocaching/settings/SettingsActivity.java b/main/src/cgeo/geocaching/settings/SettingsActivity.java
index dc1a39d..d4d9722 100644
--- a/main/src/cgeo/geocaching/settings/SettingsActivity.java
+++ b/main/src/cgeo/geocaching/settings/SettingsActivity.java
@@ -62,7 +62,7 @@ public class SettingsActivity extends PreferenceActivity {
* directory and preference key in onActivityResult() easily just by knowing
* the result code.
*/
- private enum DirChooserType {
+ private static enum DirChooserType {
GPX_IMPORT_DIR(1, R.string.pref_gpxImportDir,
Environment.getExternalStorageDirectory().getPath() + "/gpx", false),
GPX_EXPORT_DIR(2, R.string.pref_gpxExportDir,
diff --git a/main/src/cgeo/geocaching/ui/CacheListAdapter.java b/main/src/cgeo/geocaching/ui/CacheListAdapter.java
index 0d90d9f..3a451a5 100644
--- a/main/src/cgeo/geocaching/ui/CacheListAdapter.java
+++ b/main/src/cgeo/geocaching/ui/CacheListAdapter.java
@@ -45,6 +45,7 @@ import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -377,7 +378,7 @@ public class CacheListAdapter extends ArrayAdapter<Geocache> {
final boolean lightSkin = Settings.isLightSkin();
- final TouchListener touchListener = new TouchListener(cache);
+ final TouchListener touchListener = new TouchListener(cache, this);
v.setOnClickListener(touchListener);
v.setOnLongClickListener(touchListener);
v.setOnTouchListener(touchListener);
@@ -536,24 +537,30 @@ public class CacheListAdapter extends ArrayAdapter<Geocache> {
}
}
- private class TouchListener implements View.OnClickListener, View.OnLongClickListener, View.OnTouchListener {
+ private static class TouchListener implements View.OnClickListener, View.OnLongClickListener, View.OnTouchListener {
private final Geocache cache;
private final GestureDetector gestureDetector;
+ private final @NonNull WeakReference<CacheListAdapter> adapterRef;
- public TouchListener(final Geocache cache) {
+ public TouchListener(final Geocache cache, final @NonNull CacheListAdapter adapter) {
this.cache = cache;
- gestureDetector = new GestureDetector(getContext(), new FlingGesture(cache));
+ gestureDetector = new GestureDetector(adapter.getContext(), new FlingGesture(cache, adapter));
+ adapterRef = new WeakReference<CacheListAdapter>(adapter);
}
// Tap on item
@Override
public void onClick(final View view) {
- if (isSelectMode()) {
+ final CacheListAdapter adapter = adapterRef.get();
+ if (adapter == null) {
+ return;
+ }
+ if (adapter.isSelectMode()) {
cache.setStatusChecked(!cache.isStatusChecked());
- notifyDataSetChanged();
+ adapter.notifyDataSetChanged();
} else {
- CacheDetailActivity.startActivity(getContext(), cache.getGeocode(), cache.getName());
+ CacheDetailActivity.startActivity(adapter.getContext(), cache.getGeocode(), cache.getName());
}
}
@@ -572,12 +579,14 @@ public class CacheListAdapter extends ArrayAdapter<Geocache> {
}
}
- private class FlingGesture extends GestureDetector.SimpleOnGestureListener {
+ private static class FlingGesture extends GestureDetector.SimpleOnGestureListener {
private final Geocache cache;
+ private final @NonNull WeakReference<CacheListAdapter> adapterRef;
- public FlingGesture(final Geocache cache) {
+ public FlingGesture(final Geocache cache, final @NonNull CacheListAdapter adapter) {
this.cache = cache;
+ adapterRef = new WeakReference<CacheListAdapter>(adapter);
}
@Override
@@ -586,11 +595,15 @@ public class CacheListAdapter extends ArrayAdapter<Geocache> {
if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) {
return false;
}
+ final CacheListAdapter adapter = adapterRef.get();
+ if (adapter == null) {
+ return false;
+ }
// left to right swipe
if ((e2.getX() - e1.getX()) > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > Math.abs(velocityY)) {
- if (!selectMode) {
- switchSelectMode();
+ if (!adapter.selectMode) {
+ adapter.switchSelectMode();
cache.setStatusChecked(true);
}
return true;
@@ -598,8 +611,8 @@ public class CacheListAdapter extends ArrayAdapter<Geocache> {
// right to left swipe
if ((e1.getX() - e2.getX()) > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > Math.abs(velocityY)) {
- if (selectMode) {
- switchSelectMode();
+ if (adapter.selectMode) {
+ adapter.switchSelectMode();
}
return true;
}
diff --git a/main/src/cgeo/geocaching/ui/CompassView.java b/main/src/cgeo/geocaching/ui/CompassView.java
index f7111f7..915303b 100644
--- a/main/src/cgeo/geocaching/ui/CompassView.java
+++ b/main/src/cgeo/geocaching/ui/CompassView.java
@@ -3,10 +3,9 @@ package cgeo.geocaching.ui;
import cgeo.geocaching.R;
import cgeo.geocaching.utils.AngleUtils;
-import rx.Scheduler;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
-import rx.functions.Action1;
+import rx.functions.Action0;
import android.content.Context;
import android.content.res.Resources;
@@ -88,9 +87,9 @@ public class CompassView extends View {
initialDisplay = true;
- periodicUpdate = AndroidSchedulers.mainThread().schedulePeriodically(new Action1<Scheduler.Inner>() {
+ periodicUpdate = AndroidSchedulers.mainThread().createWorker().schedulePeriodically(new Action0() {
@Override
- public void call(final Scheduler.Inner inner) {
+ public void call() {
final float newAzimuthShown = smoothUpdate(northMeasured, azimuthShown);
final float newCacheHeadingShown = smoothUpdate(cacheHeadingMeasured, cacheHeadingShown);
if (Math.abs(AngleUtils.difference(azimuthShown, newAzimuthShown)) >= 2 ||
diff --git a/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java b/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java
index b2ce11a..67b8923 100644
--- a/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java
+++ b/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java
@@ -1,17 +1,18 @@
package cgeo.geocaching.ui.dialog;
import cgeo.geocaching.Geocache;
-import cgeo.geocaching.sensors.IGeoData;
import cgeo.geocaching.R;
import cgeo.geocaching.activity.AbstractActivity;
import cgeo.geocaching.activity.ActivityMixin;
import cgeo.geocaching.geopoint.Geopoint;
import cgeo.geocaching.geopoint.GeopointFormatter;
+import cgeo.geocaching.sensors.IGeoData;
import cgeo.geocaching.settings.Settings;
import cgeo.geocaching.settings.Settings.CoordInputFormatEnum;
import cgeo.geocaching.utils.EditUtils;
import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jdt.annotation.NonNull;
import android.os.Bundle;
import android.text.Editable;
@@ -25,9 +26,11 @@ import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
+import java.lang.ref.WeakReference;
+
public class CoordinatesInputDialog extends NoTitleDialog {
- final private AbstractActivity context;
+ final private WeakReference<AbstractActivity> context;
final private IGeoData geo;
final private Geocache cache;
private Geopoint gp;
@@ -43,9 +46,9 @@ public class CoordinatesInputDialog extends NoTitleDialog {
private CoordInputFormatEnum currentFormat = null;
- public CoordinatesInputDialog(final AbstractActivity context, final Geocache cache, final Geopoint gp, final IGeoData geo) {
+ public CoordinatesInputDialog(final @NonNull AbstractActivity context, final Geocache cache, final Geopoint gp, final IGeoData geo) {
super(context, ActivityMixin.getDialogTheme());
- this.context = context;
+ this.context = new WeakReference<AbstractActivity>(context);
this.geo = geo;
this.cache = cache;
@@ -66,7 +69,7 @@ public class CoordinatesInputDialog extends NoTitleDialog {
final Spinner spinner = (Spinner) findViewById(R.id.spinnerCoordinateFormats);
final ArrayAdapter<CharSequence> adapter =
- ArrayAdapter.createFromResource(context,
+ ArrayAdapter.createFromResource(context.get(),
R.array.waypoint_coordinate_formats,
android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
@@ -371,7 +374,8 @@ public class CoordinatesInputDialog extends NoTitleDialog {
// Signaled and returned below
}
if (signalError) {
- context.showToast(context.getResources().getString(R.string.err_parse_lat_lon));
+ final AbstractActivity activity = context.get();
+ activity.showToast(activity.getResources().getString(R.string.err_parse_lat_lon));
}
return false;
}
@@ -423,7 +427,8 @@ public class CoordinatesInputDialog extends NoTitleDialog {
@Override
public void onClick(View v) {
if (geo == null || geo.getCoords() == null) {
- context.showToast(context.getResources().getString(R.string.err_point_unknown_position));
+ final AbstractActivity activity = context.get();
+ activity.showToast(activity.getResources().getString(R.string.err_point_unknown_position));
return;
}
@@ -437,7 +442,8 @@ public class CoordinatesInputDialog extends NoTitleDialog {
@Override
public void onClick(View v) {
if (cache == null || cache.getCoords() == null) {
- context.showToast(context.getResources().getString(R.string.err_location_unknown));
+ final AbstractActivity activity = context.get();
+ activity.showToast(activity.getResources().getString(R.string.err_location_unknown));
return;
}
diff --git a/main/src/cgeo/geocaching/utils/RxUtils.java b/main/src/cgeo/geocaching/utils/RxUtils.java
index 8e7864c..deba573 100644
--- a/main/src/cgeo/geocaching/utils/RxUtils.java
+++ b/main/src/cgeo/geocaching/utils/RxUtils.java
@@ -3,16 +3,10 @@ package cgeo.geocaching.utils;
import rx.Scheduler;
import rx.schedulers.Schedulers;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
public class RxUtils {
// Utility class, not to be instanciated
private RxUtils() {}
- final static private int cores = Runtime.getRuntime().availableProcessors();
- public final static Scheduler computationScheduler = Schedulers.executor(new ThreadPoolExecutor(1, cores, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()));
-
+ public final static Scheduler computationScheduler = Schedulers.computation();
}