diff options
Diffstat (limited to 'main/src/cgeo/geocaching/connector')
31 files changed, 498 insertions, 274 deletions
diff --git a/main/src/cgeo/geocaching/connector/AbstractConnector.java b/main/src/cgeo/geocaching/connector/AbstractConnector.java index 28ad12b..3ec0580 100644 --- a/main/src/cgeo/geocaching/connector/AbstractConnector.java +++ b/main/src/cgeo/geocaching/connector/AbstractConnector.java @@ -1,12 +1,16 @@ package cgeo.geocaching.connector; import cgeo.geocaching.Geocache; +import cgeo.geocaching.LogCacheActivity; import cgeo.geocaching.R; +import cgeo.geocaching.enumerations.CacheType; +import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.geopoint.Geopoint; import org.apache.commons.lang3.StringUtils; -import android.app.Activity; +import java.util.ArrayList; +import java.util.List; public abstract class AbstractConnector implements IConnector { @@ -79,7 +83,7 @@ public abstract class AbstractConnector implements IConnector { } @Override - public ILoggingManager getLoggingManager(Activity activity, Geocache cache) { + public ILoggingManager getLoggingManager(final LogCacheActivity activity, final Geocache cache) { return new NoLoggingManager(); } @@ -147,4 +151,41 @@ public abstract class AbstractConnector implements IConnector { } return R.drawable.marker_other; } + + @Override + public List<LogType> getPossibleLogTypes(Geocache geocache) { + final List<LogType> logTypes = new ArrayList<LogType>(); + if (geocache.isEventCache()) { + logTypes.add(LogType.WILL_ATTEND); + logTypes.add(LogType.ATTENDED); + if (geocache.isOwner()) { + logTypes.add(LogType.ANNOUNCEMENT); + } + } else if (CacheType.WEBCAM == geocache.getType()) { + logTypes.add(LogType.WEBCAM_PHOTO_TAKEN); + } else { + logTypes.add(LogType.FOUND_IT); + } + if (!geocache.isEventCache()) { + logTypes.add(LogType.DIDNT_FIND_IT); + } + logTypes.add(LogType.NOTE); + if (!geocache.isEventCache()) { + logTypes.add(LogType.NEEDS_MAINTENANCE); + } + if (geocache.isOwner()) { + logTypes.add(LogType.OWNER_MAINTENANCE); + if (geocache.isDisabled()) { + logTypes.add(LogType.ENABLE_LISTING); + } + else { + logTypes.add(LogType.TEMP_DISABLE_LISTING); + } + logTypes.add(LogType.ARCHIVE); + } + if (!geocache.isArchived() && !geocache.isOwner()) { + logTypes.add(LogType.NEEDS_ARCHIVE); + } + return logTypes; + } } diff --git a/main/src/cgeo/geocaching/connector/ConnectorFactory.java b/main/src/cgeo/geocaching/connector/ConnectorFactory.java index 54e3447..3fdc11b 100644 --- a/main/src/cgeo/geocaching/connector/ConnectorFactory.java +++ b/main/src/cgeo/geocaching/connector/ConnectorFactory.java @@ -6,6 +6,7 @@ import cgeo.geocaching.SearchResult; import cgeo.geocaching.Trackable; import cgeo.geocaching.connector.capability.ILogin; import cgeo.geocaching.connector.capability.ISearchByCenter; +import cgeo.geocaching.connector.capability.ISearchByKeyword; import cgeo.geocaching.connector.capability.ISearchByViewPort; import cgeo.geocaching.connector.gc.GCConnector; import cgeo.geocaching.connector.oc.OCApiConnector; @@ -60,6 +61,8 @@ public final class ConnectorFactory { private static final ISearchByCenter[] searchByCenterConns; + private static final ISearchByKeyword[] searchByKeywordConns; + static { final List<ISearchByViewPort> vpConns = new ArrayList<ISearchByViewPort>(); for (final IConnector conn : CONNECTORS) { @@ -77,6 +80,15 @@ public final class ConnectorFactory { } } searchByCenterConns = centerConns.toArray(new ISearchByCenter[centerConns.size()]); + + final List<ISearchByKeyword> keywordConns = new ArrayList<ISearchByKeyword>(); + for (final IConnector conn : CONNECTORS) { + // GCConnector is handled specially, omit it here! + if (conn instanceof ISearchByKeyword && !(conn instanceof GCConnector)) { + keywordConns.add((ISearchByKeyword) conn); + } + } + searchByKeywordConns = keywordConns.toArray(new ISearchByKeyword[keywordConns.size()]); } public static IConnector[] getConnectors() { @@ -87,6 +99,10 @@ public final class ConnectorFactory { return searchByCenterConns; } + public static ISearchByKeyword[] getSearchByKeywordConnectors() { + return searchByKeywordConns; + } + public static ILogin[] getActiveLiveConnectors() { final List<ILogin> liveConns = new ArrayList<ILogin>(); for (final IConnector conn : CONNECTORS) { diff --git a/main/src/cgeo/geocaching/connector/IConnector.java b/main/src/cgeo/geocaching/connector/IConnector.java index 0c175cd..2262e47 100644 --- a/main/src/cgeo/geocaching/connector/IConnector.java +++ b/main/src/cgeo/geocaching/connector/IConnector.java @@ -2,9 +2,11 @@ package cgeo.geocaching.connector; import cgeo.geocaching.Geocache; import cgeo.geocaching.ICache; +import cgeo.geocaching.LogCacheActivity; +import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.geopoint.Geopoint; -import android.app.Activity; +import java.util.List; public interface IConnector { /** @@ -87,7 +89,7 @@ public interface IConnector { * * @return */ - public ILoggingManager getLoggingManager(Activity activity, Geocache cache); + public ILoggingManager getLoggingManager(final LogCacheActivity activity, final Geocache cache); /** * get host name of the connector server for dynamic loading of data @@ -209,4 +211,13 @@ public interface IConnector { * Whether to return the enabled or disabled marker type */ public int getCacheMapMarkerId(boolean disabled); + + /** + * Get the list of <b>potentially</b> possible log types for a cache. Those may still be filter further during the + * actual logging activity. + * + * @param geocache + * @return + */ + public List<LogType> getPossibleLogTypes(Geocache geocache); } diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java b/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java index 3fdd61f..91dd094 100644 --- a/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java @@ -1,14 +1,13 @@ package cgeo.geocaching.connector.capability; import cgeo.geocaching.SearchResult; +import cgeo.geocaching.connector.IConnector; import cgeo.geocaching.geopoint.Geopoint; /** * connector capability for online searching caches around a center coordinate, sorted by distance * */ -public interface ISearchByCenter { +public interface ISearchByCenter extends IConnector { public SearchResult searchByCenter(final Geopoint center); - - public boolean isActivated(); } diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByGeocode.java b/main/src/cgeo/geocaching/connector/capability/ISearchByGeocode.java index c3d6bba..4c16049 100644 --- a/main/src/cgeo/geocaching/connector/capability/ISearchByGeocode.java +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByGeocode.java @@ -1,12 +1,13 @@ package cgeo.geocaching.connector.capability; import cgeo.geocaching.SearchResult; +import cgeo.geocaching.connector.IConnector; import cgeo.geocaching.utils.CancellableHandler; /** * connector capability of searching online for a cache by geocode - * + * */ -public interface ISearchByGeocode { +public interface ISearchByGeocode extends IConnector { public SearchResult searchByGeocode(final String geocode, final String guid, final CancellableHandler handler); } diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByKeyword.java b/main/src/cgeo/geocaching/connector/capability/ISearchByKeyword.java new file mode 100644 index 0000000..09b2423 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByKeyword.java @@ -0,0 +1,12 @@ +package cgeo.geocaching.connector.capability; + +import cgeo.geocaching.SearchResult; +import cgeo.geocaching.connector.IConnector; + +/** + * connector capability of searching online for a cache by name + * + */ +public interface ISearchByKeyword extends IConnector { + public SearchResult searchByName(final String name); +} diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java b/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java index f1bd2ce..4954017 100644 --- a/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java @@ -1,10 +1,9 @@ package cgeo.geocaching.connector.capability; import cgeo.geocaching.SearchResult; +import cgeo.geocaching.connector.IConnector; import cgeo.geocaching.geopoint.Viewport; -public interface ISearchByViewPort { +public interface ISearchByViewPort extends IConnector { public SearchResult searchByViewport(final Viewport viewport, final String[] tokens); - - public boolean isActivated(); } diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java index 8888d6b..d5f7bf4 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java @@ -1,11 +1,12 @@ package cgeo.geocaching.connector.gc; +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.ICache; +import cgeo.geocaching.LogCacheActivity; import cgeo.geocaching.R; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.cgData; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.connector.AbstractConnector; import cgeo.geocaching.connector.ILoggingManager; import cgeo.geocaching.connector.capability.ILogin; @@ -20,10 +21,9 @@ import cgeo.geocaching.settings.SettingsActivity; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.Log; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import android.app.Activity; import android.content.Context; import android.os.Handler; @@ -104,7 +104,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public ILoggingManager getLoggingManager(Activity activity, Geocache cache) { + public ILoggingManager getLoggingManager(final LogCacheActivity activity, final Geocache cache) { return new GCLoggingManager(activity, cache); } @@ -137,10 +137,10 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, if (StringUtils.isEmpty(page)) { final SearchResult search = new SearchResult(); - if (cgData.isThere(geocode, guid, true, false)) { + if (DataStore.isThere(geocode, guid, true, false)) { if (StringUtils.isBlank(geocode) && StringUtils.isNotBlank(guid)) { Log.i("Loading old cache from cache."); - search.addGeocode(cgData.getGeocodeForGuid(guid)); + search.addGeocode(DataStore.getGeocodeForGuid(guid)); } else { search.addGeocode(geocode); } @@ -152,6 +152,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, search.setError(StatusCode.COMMUNICATION_ERROR); return search; } + assert page != null; final SearchResult searchResult = GCParser.parseCache(page, handler); @@ -189,7 +190,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, public boolean addToWatchlist(Geocache cache) { final boolean added = GCParser.addToWatchlist(cache); if (added) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return added; } @@ -198,7 +199,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, public boolean removeFromWatchlist(Geocache cache) { final boolean removed = GCParser.removeFromWatchlist(cache); if (removed) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return removed; } @@ -215,7 +216,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, public static boolean addToFavorites(Geocache cache) { final boolean added = GCParser.addToFavorites(cache); if (added) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return added; } @@ -232,7 +233,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, public static boolean removeFromFavorites(Geocache cache) { final boolean removed = GCParser.removeFromFavorites(cache); if (removed) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return removed; } @@ -241,7 +242,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, public boolean uploadModifiedCoordinates(Geocache cache, Geopoint wpt) { final boolean uploaded = GCParser.uploadModifiedCoordinates(cache, wpt); if (uploaded) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return uploaded; } @@ -250,7 +251,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, public boolean deleteModifiedCoordinates(Geocache cache) { final boolean deleted = GCParser.deleteModifiedCoordinates(cache); if (deleted) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return deleted; } @@ -259,7 +260,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, public boolean uploadPersonalNote(Geocache cache) { final boolean uploaded = GCParser.uploadPersonalNote(cache); if (uploaded) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return uploaded; } @@ -299,13 +300,13 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, final StatusCode status = Login.login(); if (status == StatusCode.NO_ERROR) { - cgeoapplication.getInstance().checkLogin = false; + CgeoApplication.getInstance().checkLogin = false; Login.detectGcCustomDate(); } - if (cgeoapplication.getInstance().showLoginToast && handler != null) { + if (CgeoApplication.getInstance().showLoginToast && handler != null) { handler.sendMessage(handler.obtainMessage(0, status)); - cgeoapplication.getInstance().showLoginToast = false; + CgeoApplication.getInstance().showLoginToast = false; // invoke settings activity to insert login details if (status == StatusCode.NO_LOGIN_INFO_STORED && fromActivity != null) { diff --git a/main/src/cgeo/geocaching/connector/gc/GCConstants.java b/main/src/cgeo/geocaching/connector/gc/GCConstants.java index 6299616..601bebe 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConstants.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConstants.java @@ -135,12 +135,12 @@ public final class GCConstants { public final static Pattern PATTERN_WPTYPE = Pattern.compile("\\/wpttypes\\/sm\\/(.+)\\.jpg"); public final static Pattern PATTERN_WPPREFIXORLOOKUPORLATLON = Pattern.compile(">([^<]*<[^>]+>)?([^<]+)(<[^>]+>[^<]*)?<\\/td>"); public final static Pattern PATTERN_WPNAME = Pattern.compile(">[^<]*<a[^>]+>([^<]*)<\\/a>"); - public final static Pattern PATTERN_WPNOTE = Pattern.compile("colspan=\"6\">(.*)<\\/td>"); + public final static Pattern PATTERN_WPNOTE = Pattern.compile("colspan=\"6\">(.*)" + Pattern.quote("</td>"), Pattern.DOTALL); /** * Patterns for different purposes */ - /** replace linebreak and paragraph tags */ + /** replace line break and paragraph tags */ public final static Pattern PATTERN_LINEBREAK = Pattern.compile("<(br|p)[^>]*>"); public final static Pattern PATTERN_TYPEBOX = Pattern.compile("<select name=\"ctl00\\$ContentBody\\$LogBookPanel1\\$ddLogType\" id=\"ctl00_ContentBody_LogBookPanel1_ddLogType\"[^>]*>" + "(([^<]*<option[^>]*>[^<]+</option>)+)[^<]*</select>", Pattern.CASE_INSENSITIVE); @@ -167,7 +167,6 @@ public final class GCConstants { public final static String STRING_PREMIUMONLY_2 = "Sorry, the owner of this listing has made it viewable to Premium Members only."; public final static String STRING_PREMIUMONLY_1 = "has chosen to make this cache listing visible to Premium Members only."; - public final static String STRING_UNPUBLISHED_OWNER = "cache has not been published yet"; public final static String STRING_UNPUBLISHED_OTHER = "you cannot view this cache listing until it has been published"; public final static String STRING_UNPUBLISHED_FROM_SEARCH = "UnpublishedCacheSearchWidget"; public final static String STRING_UNKNOWN_ERROR = "An Error Has Occurred"; diff --git a/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java index 2aa5c75..8bed2ea 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java @@ -3,7 +3,6 @@ package cgeo.geocaching.connector.gc; import cgeo.geocaching.Geocache; import cgeo.geocaching.LogCacheActivity; import cgeo.geocaching.R; -import cgeo.geocaching.settings.Settings; import cgeo.geocaching.TrackableLog; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.connector.ILoggingManager; @@ -13,12 +12,13 @@ import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.loaders.UrlLoader; import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.Nullable; -import android.app.Activity; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.LoaderManager; @@ -38,11 +38,12 @@ public class GCLoggingManager implements ILoggingManager, LoaderManager.LoaderCa private List<LogType> possibleLogTypes; private boolean hasLoaderError = true; - public GCLoggingManager(Activity activity, Geocache cache) { - this.activity = (LogCacheActivity) activity; + public GCLoggingManager(final LogCacheActivity activity, final Geocache cache) { + this.activity = activity; this.cache = cache; } + @Nullable @Override public Loader<String> onCreateLoader(int arg0, Bundle arg1) { if (!Settings.isLogin()) { // allow offline logging diff --git a/main/src/cgeo/geocaching/connector/gc/GCMap.java b/main/src/cgeo/geocaching/connector/gc/GCMap.java index 4fdde56..e2c7dfa 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCMap.java +++ b/main/src/cgeo/geocaching/connector/gc/GCMap.java @@ -1,9 +1,9 @@ package cgeo.geocaching.connector.gc; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.cgData; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LiveMapStrategy.Strategy; @@ -19,7 +19,7 @@ import cgeo.geocaching.ui.Formatter; import cgeo.geocaching.utils.LeastRecentlyUsedMap; import cgeo.geocaching.utils.Log; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.json.JSONArray; import org.json.JSONException; @@ -236,7 +236,9 @@ public class GCMap { } Log.d("Retrieved " + searchResult.getCount() + " caches for tile " + tile.toString()); - } catch (Exception e) { + } catch (RuntimeException e) { + Log.e("GCMap.parseMapJSON", e); + } catch (JSONException e) { Log.e("GCMap.parseMapJSON", e); } @@ -253,7 +255,7 @@ public class GCMap { * @return */ public static SearchResult searchByViewport(final Viewport viewport, final String[] tokens) { - int speed = (int) cgeoapplication.getInstance().currentGeo().getSpeed() * 60 * 60 / 1000; // in km/h + int speed = (int) CgeoApplication.getInstance().currentGeo().getSpeed() * 60 * 60 / 1000; // in km/h Strategy strategy = Settings.getLiveMapStrategy(); if (strategy == Strategy.AUTO) { strategy = speed >= 30 ? Strategy.FAST : Strategy.DETAILED; @@ -359,7 +361,7 @@ public class GCMap { // Check for vanished found caches if (tiles.iterator().next().getZoomLevel() >= Tile.ZOOMLEVEL_MIN_PERSONALIZED) { - searchResult.addFilteredGeocodes(cgData.getCachedMissingFromSearch(searchResult, tiles, GCConnector.getInstance(), Tile.ZOOMLEVEL_MIN_PERSONALIZED - 1)); + searchResult.addFilteredGeocodes(DataStore.getCachedMissingFromSearch(searchResult, tiles, GCConnector.getInstance(), Tile.ZOOMLEVEL_MIN_PERSONALIZED - 1)); } } @@ -370,7 +372,7 @@ public class GCMap { SearchResult search = GCParser.searchByCoords(center, Settings.getCacheType(), false, null); if (search != null && !search.isEmpty()) { final Set<String> geocodes = search.getGeocodes(); - lastSearchViewport = cgData.getBounds(geocodes); + lastSearchViewport = DataStore.getBounds(geocodes); searchResult.addGeocodes(geocodes); } } diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java index b373cb1..bfbcaa4 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCParser.java +++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java @@ -1,5 +1,7 @@ package cgeo.geocaching.connector.gc; +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.Image; import cgeo.geocaching.LogEntry; @@ -8,8 +10,6 @@ import cgeo.geocaching.SearchResult; import cgeo.geocaching.Trackable; import cgeo.geocaching.TrackableLog; import cgeo.geocaching.Waypoint; -import cgeo.geocaching.cgData; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LoadFlags; @@ -31,15 +31,18 @@ import cgeo.geocaching.ui.DirectionImage; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.MatcherWrapper; +import cgeo.geocaching.utils.SynchronizedDateFormat; import cgeo.geocaching.utils.TextUtils; import ch.boye.httpclientandroidlib.HttpResponse; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.collections.ListUtils; + +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -49,7 +52,6 @@ import android.text.Html; import java.io.File; import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; @@ -61,8 +63,8 @@ import java.util.Locale; import java.util.Set; public abstract class GCParser { - private final static SimpleDateFormat dateTbIn1 = new SimpleDateFormat("EEEEE, dd MMMMM yyyy", Locale.ENGLISH); // Saturday, 28 March 2009 - private final static SimpleDateFormat dateTbIn2 = new SimpleDateFormat("EEEEE, MMMMM dd, yyyy", Locale.ENGLISH); // Saturday, March 28, 2009 + private final static SynchronizedDateFormat dateTbIn1 = new SynchronizedDateFormat("EEEEE, dd MMMMM yyyy", Locale.ENGLISH); // Saturday, 28 March 2009 + private final static SynchronizedDateFormat dateTbIn2 = new SynchronizedDateFormat("EEEEE, MMMMM dd, yyyy", Locale.ENGLISH); // Saturday, March 28, 2009 private static SearchResult parseSearch(final String url, final String pageContent, final boolean showCaptcha, RecaptchaReceiver thread) { if (StringUtils.isBlank(pageContent)) { @@ -147,7 +149,7 @@ public abstract class GCParser { } } } - } catch (final Exception e) { + } catch (final RuntimeException e) { // failed to parse GUID and/or Disabled Log.w("GCParser.parseSearch: Failed to parse GUID and/or Disabled data"); } @@ -308,19 +310,15 @@ public abstract class GCParser { final String coordinates = Network.getResponseData(Network.postRequest("http://www.geocaching.com/seek/nearest.aspx", params), false); - if (StringUtils.isNotBlank(coordinates)) { - if (coordinates.contains("You have not agreed to the license agreement. The license agreement is required before you can start downloading GPX or LOC files from Geocaching.com")) { - Log.i("User has not agreed to the license agreement. Can\'t download .loc file."); - - searchResult.setError(StatusCode.UNAPPROVED_LICENSE); - - return searchResult; - } + if (StringUtils.contains(coordinates, "You have not agreed to the license agreement. The license agreement is required before you can start downloading GPX or LOC files from Geocaching.com")) { + Log.i("User has not agreed to the license agreement. Can\'t download .loc file."); + searchResult.setError(StatusCode.UNAPPROVED_LICENSE); + return searchResult; } LocParser.parseLoc(searchResult, coordinates); - } catch (final Exception e) { + } catch (final RuntimeException e) { Log.e("GCParser.parseSearch.CIDs", e); } } @@ -345,7 +343,7 @@ public abstract class GCParser { static SearchResult parseCache(final String page, final CancellableHandler handler) { final SearchResult searchResult = parseCacheFromText(page, handler); - // attention: parseCacheFromText already stores implicitely through searchResult.addCache + // attention: parseCacheFromText already stores implicitly through searchResult.addCache if (searchResult != null && !searchResult.getGeocodes().isEmpty()) { final Geocache cache = searchResult.getFirstCacheFromResult(LoadFlags.LOAD_CACHE_OR_DB); getExtraOnlineInfo(cache, page, handler); @@ -357,7 +355,7 @@ public abstract class GCParser { // save full detailed caches CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_cache); - cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); + 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 @@ -376,7 +374,7 @@ public abstract class GCParser { final SearchResult searchResult = new SearchResult(); - if (pageIn.contains(GCConstants.STRING_UNPUBLISHED_OTHER) || pageIn.contains(GCConstants.STRING_UNPUBLISHED_OWNER) || pageIn.contains(GCConstants.STRING_UNPUBLISHED_FROM_SEARCH)) { + if (pageIn.contains(GCConstants.STRING_UNPUBLISHED_OTHER) || pageIn.contains(GCConstants.STRING_UNPUBLISHED_FROM_SEARCH)) { searchResult.setError(StatusCode.UNPUBLISHED_CACHE); return searchResult; } @@ -569,7 +567,7 @@ public abstract class GCParser { } cache.setAttributes(attributes); } - } catch (final Exception e) { + } catch (final RuntimeException e) { // failed to parse cache attributes Log.w("GCParser.parseCache: Failed to parse cache attributes"); } @@ -598,7 +596,7 @@ public abstract class GCParser { } cache.addSpoiler(new Image(url, title, description)); } - } catch (final Exception e) { + } catch (final RuntimeException e) { // failed to parse cache spoilers Log.w("GCParser.parseCache: Failed to parse cache spoilers"); } @@ -632,7 +630,7 @@ public abstract class GCParser { } } } - } catch (final Exception e) { + } catch (final RuntimeException e) { // failed to parse cache inventory Log.w("GCParser.parseCache: Failed to parse cache inventory (2)"); } @@ -654,7 +652,7 @@ public abstract class GCParser { } } } - } catch (final Exception e) { + } catch (final NumberFormatException e) { // failed to parse logs Log.w("GCParser.parseCache: Failed to parse cache log count"); } @@ -667,7 +665,7 @@ public abstract class GCParser { final String originalCoords = TextUtils.getMatch(page, GCConstants.PATTERN_LATLON_ORIG, false, null); if (null != originalCoords) { - final Waypoint waypoint = new Waypoint(cgeoapplication.getInstance().getString(R.string.cache_coordinates_original), WaypointType.ORIGINAL, false); + final Waypoint waypoint = new Waypoint(CgeoApplication.getInstance().getString(R.string.cache_coordinates_original), WaypointType.ORIGINAL, false); waypoint.setCoords(new Geopoint(originalCoords)); cache.addOrChangeWaypoint(waypoint, false); cache.setUserModifiedCoords(true); @@ -706,7 +704,7 @@ public abstract class GCParser { // waypoint name // res is null during the unit tests - final String name = TextUtils.getMatch(wp[6], GCConstants.PATTERN_WPNAME, true, 1, cgeoapplication.getInstance().getString(R.string.waypoint), true); + final String name = TextUtils.getMatch(wp[6], GCConstants.PATTERN_WPNAME, true, 1, CgeoApplication.getInstance().getString(R.string.waypoint), true); // waypoint type final String resulttype = TextUtils.getMatch(wp[3], GCConstants.PATTERN_WPTYPE, null); @@ -719,7 +717,7 @@ public abstract class GCParser { // waypoint lookup waypoint.setLookup(TextUtils.getMatch(wp[5], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, true, 2, waypoint.getLookup(), false)); - // waypoint latitude and logitude + // waypoint latitude and longitude latlon = Html.fromHtml(TextUtils.getMatch(wp[7], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, false, 2, "", false)).toString().trim(); if (!StringUtils.startsWith(latlon, "???")) { waypoint.setLatlon(latlon); @@ -847,6 +845,7 @@ public abstract class GCParser { * @param recaptchaReceiver * @return */ + @Nullable private static SearchResult searchByAny(final CacheType cacheType, final boolean my, final boolean showCaptcha, final Parameters params, RecaptchaReceiver recaptchaReceiver) { insertCacheType(params, cacheType); @@ -858,6 +857,7 @@ public abstract class GCParser { Log.e("GCParser.searchByAny: No data from server"); return null; } + assert page != null; final SearchResult searchResult = parseSearch(fullUri, page, showCaptcha, recaptchaReceiver); if (searchResult == null || CollectionUtils.isEmpty(searchResult.getGeocodes())) { @@ -944,6 +944,7 @@ public abstract class GCParser { return null; } + @Nullable public static Trackable searchTrackable(final String geocode, final String guid, final String id) { if (StringUtils.isBlank(geocode) && StringUtils.isBlank(guid) && StringUtils.isBlank(id)) { Log.w("GCParser.searchTrackable: No geocode nor guid nor id given"); @@ -968,6 +969,7 @@ public abstract class GCParser { Log.e("GCParser.searchTrackable: No data from server"); return trackable; } + assert page != null; trackable = parseTrackable(page, geocode); if (trackable == null) { @@ -1083,7 +1085,7 @@ public abstract class GCParser { page = Network.getResponseData(Network.postRequest(uri, params)); } - } catch (final Exception e) { + } catch (final RuntimeException e) { Log.e("GCParser.postLog.confim", e); } @@ -1094,7 +1096,7 @@ public abstract class GCParser { Log.i("Log successfully posted to cache #" + cacheid); if (geocode != null) { - cgData.saveVisitDate(geocode); + DataStore.saveVisitDate(geocode); } Login.getLoginStatus(page); @@ -1136,6 +1138,7 @@ public abstract class GCParser { Log.e("GCParser.uploadLogImage: No data from server"); return new ImmutablePair<StatusCode, String>(StatusCode.UNKNOWN_ERROR, null); } + assert page != null; final String[] viewstates = Login.getViewstates(page); @@ -1293,7 +1296,8 @@ public abstract class GCParser { return !guidOnPage; // on watch list (=error) / not on watch list } - static String requestHtmlPage(final String geocode, final String guid, final String log, final String numlogs) { + @Nullable + static String requestHtmlPage(@Nullable final String geocode, @Nullable final String guid, final String log, final String numlogs) { final Parameters params = new Parameters("decrypt", "y"); if (StringUtils.isNotBlank(geocode)) { params.put("wp", geocode); @@ -1396,7 +1400,7 @@ public abstract class GCParser { trackable.setOwnerGuid(matcherOwner.group(1)); trackable.setOwner(matcherOwner.group(2).trim()); } - } catch (final Exception e) { + } catch (final RuntimeException e) { // failed to parse trackable owner name Log.w("GCParser.parseTrackable: Failed to parse trackable owner name"); } @@ -1427,7 +1431,7 @@ public abstract class GCParser { if (TextUtils.matches(page, GCConstants.PATTERN_TRACKABLE_SPOTTEDOWNER)) { trackable.setSpottedType(Trackable.SPOTTED_OWNER); } - } catch (final Exception e) { + } catch (final RuntimeException e) { // failed to parse trackable last known place Log.w("GCParser.parseTrackable: Failed to parse trackable last known place"); } @@ -1476,12 +1480,12 @@ public abstract class GCParser { trackable.setDetails(convertLinks(details)); } } - } catch (final Exception e) { + } catch (final RuntimeException e) { // failed to parse trackable details & image Log.w("GCParser.parseTrackable: Failed to parse trackable details & image"); } if (StringUtils.isEmpty(trackable.getDetails()) && page.contains(GCConstants.ERROR_TB_NOT_ACTIVATED)) { - trackable.setDetails(cgeoapplication.getInstance().getString(R.string.trackable_not_activated)); + trackable.setDetails(CgeoApplication.getInstance().getString(R.string.trackable_not_activated)); } // trackable logs @@ -1538,8 +1542,8 @@ public abstract class GCParser { trackable.setTrackingcode(possibleTrackingcode); } - if (cgeoapplication.getInstance() != null) { - cgData.saveTrackable(trackable); + if (CgeoApplication.getInstance() != null) { + DataStore.saveTrackable(trackable); } return trackable; @@ -1601,8 +1605,17 @@ public abstract class GCParser { rawResponse = TextUtils.getMatch(page, GCConstants.PATTERN_LOGBOOK, ""); } + return parseLogs(friends, rawResponse); + } + + 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; + } + try { final JSONObject resp = new JSONObject(rawResponse); if (!resp.getString("status").equals("success")) { @@ -1629,7 +1642,7 @@ public abstract class GCParser { // 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 as all. + // display them at all. final String latLon = entry.getString("LatLonString"); final LogEntry logDone = new LogEntry( entry.getString("UserName"), @@ -1658,9 +1671,10 @@ public abstract class GCParser { return logs; } + @NonNull public static List<LogType> parseTypes(String page) { if (StringUtils.isEmpty(page)) { - return ListUtils.EMPTY_LIST; + return Collections.emptyList(); } final List<LogType> types = new ArrayList<LogType>(); diff --git a/main/src/cgeo/geocaching/connector/gc/Login.java b/main/src/cgeo/geocaching/connector/gc/Login.java index 0d8fb05..beb49f1 100644 --- a/main/src/cgeo/geocaching/connector/gc/Login.java +++ b/main/src/cgeo/geocaching/connector/gc/Login.java @@ -1,7 +1,7 @@ package cgeo.geocaching.connector.gc; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.network.Cookies; import cgeo.geocaching.network.HtmlImage; @@ -13,10 +13,11 @@ import cgeo.geocaching.utils.MatcherWrapper; import cgeo.geocaching.utils.TextUtils; import ch.boye.httpclientandroidlib.HttpResponse; - import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import android.graphics.drawable.BitmapDrawable; @@ -76,7 +77,7 @@ public abstract class Login { return StatusCode.NO_LOGIN_INFO_STORED; } - Login.setActualStatus(cgeoapplication.getInstance().getString(R.string.init_login_popup_working)); + Login.setActualStatus(CgeoApplication.getInstance().getString(R.string.init_login_popup_working)); HttpResponse loginResponse = Network.getRequest("https://www.geocaching.com/login/default.aspx"); String loginData = Network.getResponseData(loginResponse); if (loginResponse != null && loginResponse.getStatusLine().getStatusCode() == 503 && TextUtils.matches(loginData, GCConstants.PATTERN_MAINTENANCE)) { @@ -119,6 +120,7 @@ public abstract class Login { // FIXME: should it be CONNECTION_FAILED to match the first attempt? return StatusCode.COMMUNICATION_ERROR; // no login page } + assert loginData != null; // Caught above if (Login.getLoginStatus(loginData)) { Log.i("Successfully logged in Geocaching.com as " + login.left + " (" + Settings.getMemberStatus() + ')'); @@ -171,7 +173,7 @@ public abstract class Login { resetLoginStatus(); setActualCachesFound(-1); - setActualStatus(cgeoapplication.getInstance().getString(R.string.err_login)); + setActualStatus(CgeoApplication.getInstance().getString(R.string.err_login)); } static void setActualCachesFound(final int found) { @@ -212,13 +214,14 @@ public abstract class Login { * @param page * @return <code>true</code> if user is logged in, <code>false</code> otherwise */ - public static boolean getLoginStatus(final String page) { + public static boolean getLoginStatus(@Nullable final String page) { if (StringUtils.isBlank(page)) { Log.e("Login.checkLogin: No page given"); return false; } + assert page != null; - setActualStatus(cgeoapplication.getInstance().getString(R.string.init_login_popup_ok)); + setActualStatus(CgeoApplication.getInstance().getString(R.string.init_login_popup_ok)); // on every page except login page setActualLoginStatus(TextUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME)); @@ -246,7 +249,7 @@ public abstract class Login { return true; } - setActualStatus(cgeoapplication.getInstance().getString(R.string.init_login_popup_failed)); + setActualStatus(CgeoApplication.getInstance().getString(R.string.init_login_popup_failed)); return false; } @@ -276,7 +279,8 @@ public abstract class Login { public static BitmapDrawable downloadAvatarAndGetMemberStatus() { try { - final String profile = TextUtils.replaceWhitespace(Network.getResponseData(Network.getRequest("http://www.geocaching.com/my/"))); + 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)); if (profile.contains(GCConstants.MEMBER_STATUS_RENEW)) { @@ -473,10 +477,13 @@ public abstract class Login { * @param params * @return */ - public static String getRequestLogged(final String uri, final Parameters params) { - final String data = Network.getResponseData(Network.getRequest(uri, params), canRemoveWhitespace(uri)); + @Nullable + public static String getRequestLogged(@NonNull final String uri, @Nullable final Parameters params) { + final HttpResponse response = Network.getRequest(uri, params); + final String data = Network.getResponseData(response, canRemoveWhitespace(uri)); - if (getLoginStatus(data)) { + // A page not found will not be found if the user logs in either + if (Network.isPageNotFound(response) || getLoginStatus(data)) { return data; } diff --git a/main/src/cgeo/geocaching/connector/gc/SearchHandler.java b/main/src/cgeo/geocaching/connector/gc/SearchHandler.java index 4358399..795ed2f 100644 --- a/main/src/cgeo/geocaching/connector/gc/SearchHandler.java +++ b/main/src/cgeo/geocaching/connector/gc/SearchHandler.java @@ -91,7 +91,7 @@ public class SearchHandler extends Handler { dlg.create().show(); } - } catch (Exception e) { + } catch (MalformedURLException e) { Log.e("Error in reCAPTCHA handler", e); } } diff --git a/main/src/cgeo/geocaching/connector/gc/Tile.java b/main/src/cgeo/geocaching/connector/gc/Tile.java index 4ed53c9..623730a 100644 --- a/main/src/cgeo/geocaching/connector/gc/Tile.java +++ b/main/src/cgeo/geocaching/connector/gc/Tile.java @@ -29,11 +29,6 @@ import java.util.Set; */ public class Tile { - public static final double LATITUDE_MIN = -85.05112878; - public static final double LATITUDE_MAX = 85.05112878; - public static final double LONGITUDE_MIN = -180; - public static final double LONGITUDE_MAX = 180; - public static final int TILE_SIZE = 256; public static final int ZOOMLEVEL_MAX = 18; public static final int ZOOMLEVEL_MIN = 0; diff --git a/main/src/cgeo/geocaching/connector/gc/UTFGrid.java b/main/src/cgeo/geocaching/connector/gc/UTFGrid.java index 6d20eb6..89a3de8 100644 --- a/main/src/cgeo/geocaching/connector/gc/UTFGrid.java +++ b/main/src/cgeo/geocaching/connector/gc/UTFGrid.java @@ -12,22 +12,6 @@ public final class UTFGrid { public static final int GRID_MAXX = 63; public static final int GRID_MAXY = 63; - /** - * Convert a value from a JSON grid object into an id that can be used as an index - * It's not used at the moment due to optimizations. - * But maybe we need it some day... - */ - public static short getUTFGridId(final char value) { - short result = (short) value; - if (result >= 93) { - result--; - } - if (result >= 35) { - result--; - } - return (short) (result - 32); - } - /** Calculate from a list of positions (x/y) the coords */ public static UTFGridPosition getPositionInGrid(List<UTFGridPosition> positions) { int minX = GRID_MAXX; diff --git a/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java b/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java index 5965fff..eff193a 100644 --- a/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java +++ b/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java @@ -15,9 +15,12 @@ public final class UTFGridPosition { private final static Pattern PATTERN_JSON_KEY = Pattern.compile("[^\\d]*" + "(\\d+),\\s*(\\d+)" + "[^\\d]*"); // (12, 34) public UTFGridPosition(final int x, final int y) { - assert x >= 0 && x <= UTFGrid.GRID_MAXX : "x outside bounds"; - assert y >= 0 && y <= UTFGrid.GRID_MAXY : "y outside bounds"; - + if (x < 0 || x > UTFGrid.GRID_MAXX) { + throw new IllegalArgumentException("x outside bounds"); + } + if (y < 0 || y > UTFGrid.GRID_MAXY) { + throw new IllegalArgumentException("y outside bounds"); + } this.x = x; this.y = y; } diff --git a/main/src/cgeo/geocaching/connector/oc/IOCAuthParams.java b/main/src/cgeo/geocaching/connector/oc/IOCAuthParams.java index 5a140a1..dacb626 100644 --- a/main/src/cgeo/geocaching/connector/oc/IOCAuthParams.java +++ b/main/src/cgeo/geocaching/connector/oc/IOCAuthParams.java @@ -3,58 +3,65 @@ package cgeo.geocaching.connector.oc; public interface IOCAuthParams { /** - * The site name: 'www.openaching...' - * + * The site name: 'www.opencaching...' + * * @return */ String getSite(); /** * ResId of the Consumer key - * + * * @return */ int getCKResId(); /** * ResId of the Consumer secret - * + * * @return */ int getCSResId(); /** - * ResId ot the Authorization title - * + * ResId of the Authorization title + * * @return */ - int getAuthTitelResId(); + int getAuthTitleResId(); /** * Preference key of the public token - * + * * @return */ int getTokenPublicPrefKey(); /** * Preference key of the secret token - * + * * @return */ int getTokenSecretPrefKey(); /** * Preference key of the temporary public token (OAuth) - * + * * @return */ int getTempTokenPublicPrefKey(); /** * Preference key of the temporary secret token (OAuth) - * + * * @return */ int getTempTokenSecretPrefKey(); + + /** + * The URI to use as a callback (OAuth) + * + * @return + */ + String getCallbackUri(); } diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java index d35e54b..b1b9088 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java @@ -1,12 +1,14 @@ package cgeo.geocaching.connector.oc; +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; +import cgeo.geocaching.LogCacheActivity; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.cgData; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.connector.ILoggingManager; import cgeo.geocaching.connector.capability.ILogin; import cgeo.geocaching.connector.capability.ISearchByCenter; +import cgeo.geocaching.connector.capability.ISearchByKeyword; import cgeo.geocaching.connector.capability.ISearchByViewPort; import cgeo.geocaching.connector.oc.UserInfo.UserInfoStatus; import cgeo.geocaching.geopoint.Geopoint; @@ -16,11 +18,10 @@ import cgeo.geocaching.utils.CryptUtils; import org.apache.commons.lang3.StringUtils; -import android.app.Activity; import android.content.Context; import android.os.Handler; -public class OCApiLiveConnector extends OCApiConnector implements ISearchByCenter, ISearchByViewPort, ILogin { +public class OCApiLiveConnector extends OCApiConnector implements ISearchByCenter, ISearchByViewPort, ILogin, ISearchByKeyword { private final String cS; private final int isActivePrefKeyId; @@ -29,9 +30,9 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente private UserInfo userInfo = new UserInfo(StringUtils.EMPTY, 0, UserInfoStatus.NOT_RETRIEVED); public OCApiLiveConnector(String name, String host, String prefix, String licenseString, int cKResId, int cSResId, int isActivePrefKeyId, int tokenPublicPrefKeyId, int tokenSecretPrefKeyId, ApiSupport apiSupport) { - super(name, host, prefix, CryptUtils.rot13(cgeoapplication.getInstance().getResources().getString(cKResId)), licenseString, apiSupport); + super(name, host, prefix, CryptUtils.rot13(CgeoApplication.getInstance().getResources().getString(cKResId)), licenseString, apiSupport); - cS = CryptUtils.rot13(cgeoapplication.getInstance().getResources().getString(cSResId)); + cS = CryptUtils.rot13(CgeoApplication.getInstance().getResources().getString(cSResId)); this.isActivePrefKeyId = isActivePrefKeyId; this.tokenPublicPrefKeyId = tokenPublicPrefKeyId; this.tokenSecretPrefKeyId = tokenSecretPrefKeyId; @@ -87,7 +88,7 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente final boolean added = OkapiClient.setWatchState(cache, true, this); if (added) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return added; @@ -98,7 +99,7 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente final boolean removed = OkapiClient.setWatchState(cache, false, this); if (removed) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return removed; @@ -110,7 +111,7 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente } @Override - public ILoggingManager getLoggingManager(Activity activity, Geocache cache) { + public ILoggingManager getLoggingManager(final LogCacheActivity activity, final Geocache cache) { return new OkapiLoggingManager(activity, this, cache); } @@ -145,11 +146,17 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente @Override public String getLoginStatusString() { - return cgeoapplication.getInstance().getString(userInfo.getStatus().resId); + return CgeoApplication.getInstance().getString(userInfo.getStatus().resId); } @Override public boolean isLoggedIn() { return userInfo.getStatus() == UserInfoStatus.SUCCESSFUL; } + + @Override + public SearchResult searchByName(final String name) { + final Geopoint currentPos = CgeoApplication.getInstance().currentGeo().getCoords(); + return new SearchResult(OkapiClient.getCachesNamed(currentPos, name, this)); + } } diff --git a/main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java b/main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java index 10d6a66..c082bac 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java +++ b/main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java @@ -1,13 +1,14 @@ package cgeo.geocaching.connector.oc; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.network.OAuthAuthorizationActivity; import cgeo.geocaching.settings.Settings; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.Nullable; -public class OCAuthorizationActivity extends OAuthAuthorizationActivity { +public abstract class OCAuthorizationActivity extends OAuthAuthorizationActivity { final IOCAuthParams authParams; @@ -17,8 +18,9 @@ public class OCAuthorizationActivity extends OAuthAuthorizationActivity { "/okapi/services/oauth/authorize", "/okapi/services/oauth/access_token", false, - cgeoapplication.getInstance().getResources().getString(authParams.getCKResId()), - cgeoapplication.getInstance().getResources().getString(authParams.getCSResId())); + CgeoApplication.getInstance().getResources().getString(authParams.getCKResId()), + CgeoApplication.getInstance().getResources().getString(authParams.getCSResId()), + authParams.getCallbackUri()); this.authParams = authParams; } @@ -28,12 +30,12 @@ public class OCAuthorizationActivity extends OAuthAuthorizationActivity { } @Override - protected void setTempTokens(String tokenPublic, String tokenSecret) { + protected void setTempTokens(@Nullable final String tokenPublic, @Nullable final String tokenSecret) { Settings.setTokens(authParams.getTempTokenPublicPrefKey(), tokenPublic, authParams.getTempTokenSecretPrefKey(), tokenSecret); } @Override - protected void setTokens(String tokenPublic, String tokenSecret, boolean enable) { + protected void setTokens(@Nullable final String tokenPublic, @Nullable final String tokenSecret, final boolean enable) { Settings.setTokens(authParams.getTokenPublicPrefKey(), tokenPublic, authParams.getTokenSecretPrefKey(), tokenSecret); if (tokenPublic != null) { Settings.setTokens(authParams.getTempTokenPublicPrefKey(), null, authParams.getTempTokenSecretPrefKey(), null); @@ -42,22 +44,7 @@ public class OCAuthorizationActivity extends OAuthAuthorizationActivity { @Override protected String getAuthTitle() { - return res.getString(authParams.getAuthTitelResId()); - } - - @Override - protected String getAuthAgain() { - return res.getString(R.string.auth_again_oc); - } - - @Override - protected String getErrAuthInitialize() { - return res.getString(R.string.err_auth_initialize); - } - - @Override - protected String getAuthStart() { - return res.getString(R.string.auth_start_oc); + return res.getString(authParams.getAuthTitleResId()); } @Override @@ -65,48 +52,4 @@ public class OCAuthorizationActivity extends OAuthAuthorizationActivity { return res.getString(R.string.auth_dialog_completed_oc, getAuthTitle()); } - @Override - protected String getErrAuthProcess() { - return res.getString(R.string.err_auth_process); - } - - @Override - protected String getAuthDialogWait() { - return res.getString(R.string.auth_dialog_wait_oc, getAuthTitle()); - } - - @Override - protected String getAuthDialogPinTitle() { - return res.getString(R.string.auth_dialog_pin_title_oc); - } - - @Override - protected String getAuthDialogPinMessage() { - return res.getString(R.string.auth_dialog_pin_message_oc, getAuthTitle()); - } - - @Override - protected String getAboutAuth1() { - return res.getString(R.string.about_auth_1_oc, getAuthTitle()); - } - - @Override - protected String getAboutAuth2() { - return res.getString(R.string.about_auth_2_oc, getAuthTitle(), getAuthTitle()); - } - - @Override - protected String getAuthAuthorize() { - return res.getString(R.string.auth_authorize_oc); - } - - @Override - protected String getAuthPinHint() { - return res.getString(R.string.auth_pin_hint_oc, getAuthTitle()); - } - - @Override - protected String getAuthFinish() { - return res.getString(R.string.auth_finish_oc); - } } diff --git a/main/src/cgeo/geocaching/connector/oc/OCConnector.java b/main/src/cgeo/geocaching/connector/oc/OCConnector.java index 29cdd10..b5c62ea 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCConnector.java @@ -4,7 +4,10 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.ICache; import cgeo.geocaching.R; import cgeo.geocaching.connector.AbstractConnector; +import cgeo.geocaching.enumerations.LogType; +import java.util.Arrays; +import java.util.List; import java.util.regex.Pattern; public class OCConnector extends AbstractConnector { @@ -14,6 +17,9 @@ public class OCConnector extends AbstractConnector { private final Pattern codePattern; private static final Pattern GPX_ZIP_FILE_PATTERN = Pattern.compile("oc[a-z]{2,3}\\d{5,}\\.zip", Pattern.CASE_INSENSITIVE); + private static final List<LogType> STANDARD_LOG_TYPES = Arrays.asList(LogType.FOUND_IT, LogType.DIDNT_FIND_IT, LogType.NOTE); + private static final List<LogType> EVENT_LOG_TYPES = Arrays.asList(LogType.WILL_ATTEND, LogType.ATTENDED, LogType.NOTE); + public OCConnector(final String name, final String host, final String prefix) { this.name = name; this.host = host; @@ -66,4 +72,12 @@ public class OCConnector extends AbstractConnector { return R.drawable.marker_oc; } + @Override + public final List<LogType> getPossibleLogTypes(Geocache cache) { + if (cache.isEventCache()) { + return EVENT_LOG_TYPES; + } + + return STANDARD_LOG_TYPES; + } } diff --git a/main/src/cgeo/geocaching/connector/oc/OCDEAuthParams.java b/main/src/cgeo/geocaching/connector/oc/OCDEAuthParams.java index 734b383..17c1cb8 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCDEAuthParams.java +++ b/main/src/cgeo/geocaching/connector/oc/OCDEAuthParams.java @@ -20,7 +20,7 @@ public class OCDEAuthParams implements IOCAuthParams { } @Override - public int getAuthTitelResId() { + public int getAuthTitleResId() { return R.string.auth_ocde; } @@ -43,4 +43,9 @@ public class OCDEAuthParams implements IOCAuthParams { public int getTempTokenSecretPrefKey() { return R.string.pref_temp_ocde_token_secret; } + + @Override + public String getCallbackUri() { + return "callback://www.cgeo.org/opencaching.de/"; + } } diff --git a/main/src/cgeo/geocaching/connector/oc/OCDEAuthorizationActivity.java b/main/src/cgeo/geocaching/connector/oc/OCDEAuthorizationActivity.java index caf114c..1d2aa49 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCDEAuthorizationActivity.java +++ b/main/src/cgeo/geocaching/connector/oc/OCDEAuthorizationActivity.java @@ -1,6 +1,6 @@ package cgeo.geocaching.connector.oc; -public class OCDEAuthorizationActivity extends OCAuthorizationActivity { +public final class OCDEAuthorizationActivity extends OCAuthorizationActivity { public OCDEAuthorizationActivity() { super(new OCDEAuthParams()); diff --git a/main/src/cgeo/geocaching/connector/oc/OCPLAuthParams.java b/main/src/cgeo/geocaching/connector/oc/OCPLAuthParams.java index 117e990..dfe03e5 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCPLAuthParams.java +++ b/main/src/cgeo/geocaching/connector/oc/OCPLAuthParams.java @@ -20,7 +20,7 @@ public class OCPLAuthParams implements IOCAuthParams { } @Override - public int getAuthTitelResId() { + public int getAuthTitleResId() { return R.string.auth_ocpl; } @@ -43,4 +43,9 @@ public class OCPLAuthParams implements IOCAuthParams { public int getTempTokenSecretPrefKey() { return R.string.pref_temp_ocpl_token_secret; } + + @Override + public String getCallbackUri() { + return "callback://www.cgeo.org/opencaching.pl/"; + } } diff --git a/main/src/cgeo/geocaching/connector/oc/OCPLAuthorizationActivity.java b/main/src/cgeo/geocaching/connector/oc/OCPLAuthorizationActivity.java index 4f9ef96..30ea150 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCPLAuthorizationActivity.java +++ b/main/src/cgeo/geocaching/connector/oc/OCPLAuthorizationActivity.java @@ -1,6 +1,6 @@ package cgeo.geocaching.connector.oc; -public class OCPLAuthorizationActivity extends OCAuthorizationActivity { +public final class OCPLAuthorizationActivity extends OCAuthorizationActivity { public OCPLAuthorizationActivity() { super(new OCPLAuthParams()); diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java index 43ea8ad..686e314 100644 --- a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java +++ b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java @@ -1,12 +1,12 @@ package cgeo.geocaching.connector.oc; +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.Image; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; import cgeo.geocaching.Waypoint; -import cgeo.geocaching.cgData; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.IConnector; import cgeo.geocaching.connector.LogResult; @@ -29,9 +29,15 @@ import cgeo.geocaching.network.OAuth; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.SynchronizedDateFormat; + +import ch.boye.httpclientandroidlib.HttpResponse; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.FastDateFormat; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -39,7 +45,6 @@ import org.json.JSONObject; import android.net.Uri; import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; @@ -56,12 +61,8 @@ final class OkapiClient { private static final char SEPARATOR = '|'; private static final String SEPARATOR_STRING = Character.toString(SEPARATOR); - private static final SimpleDateFormat LOG_DATE_FORMAT; - static { - LOG_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ", Locale.US); - LOG_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); - } - private static final SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); + private static final FastDateFormat LOG_DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSSZ", TimeZone.getTimeZone("UTC"), Locale.US); + private static final SynchronizedDateFormat ISO8601DATEFORMAT = new SynchronizedDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); private static final String CACHE_ATTRNAMES = "attrnames"; private static final String WPT_LOCATION = "location"; @@ -81,7 +82,6 @@ final class OkapiClient { private static final String CACHE_LATEST_LOGS = "latest_logs"; private static final String CACHE_IMAGE_URL = "url"; private static final String CACHE_IMAGE_CAPTION = "caption"; - private static final String CACHE_IMAGE_IS_SPOILER = "is_spoiler"; private static final String CACHE_IMAGES = "images"; private static final String CACHE_HINT = "hint"; private static final String CACHE_DESCRIPTION = "description"; @@ -110,14 +110,15 @@ final class OkapiClient { // the several realms of possible fields for cache retrieval: // Core: for livemap requests (L3 - only with level 3 auth) // Additional: additional fields for full cache (L3 - only for level 3 auth, current - only for connectors with current api) - private static final String SERVICE_CACHE_CORE_FIELDS = "code|name|location|type|status|difficulty|terrain|size"; + private static final String SERVICE_CACHE_CORE_FIELDS = "code|name|location|type|status|difficulty|terrain|size|date_hidden"; private static final String SERVICE_CACHE_CORE_L3_FIELDS = "is_found"; - private static final String SERVICE_CACHE_ADDITIONAL_FIELDS = "owner|founds|notfounds|rating|rating_votes|recommendations|description|hint|images|latest_logs|date_hidden|alt_wpts|attrnames|req_passwd"; + private static final String SERVICE_CACHE_ADDITIONAL_FIELDS = "owner|founds|notfounds|rating|rating_votes|recommendations|description|hint|images|latest_logs|alt_wpts|attrnames|req_passwd"; private static final String SERVICE_CACHE_ADDITIONAL_CURRENT_FIELDS = "gc_code|attribution_note"; private static final String SERVICE_CACHE_ADDITIONAL_L3_FIELDS = "is_watched|my_notes"; - private static final String METHOD_SEARCH_NEAREST = "services/caches/search/nearest"; + private static final String METHOD_SEARCH_ALL = "services/caches/search/all"; private static final String METHOD_SEARCH_BBOX = "services/caches/search/bbox"; + private static final String METHOD_SEARCH_NEAREST = "services/caches/search/nearest"; private static final String METHOD_RETRIEVE_CACHES = "services/caches/geocaches"; public static Geocache getCache(final String geoCode) { @@ -132,13 +133,9 @@ final class OkapiClient { params.add("fields", getFullFields(ocapiConn)); params.add("attribution_append", "none"); - final JSONObject data = request(ocapiConn, OkapiService.SERVICE_CACHE, params); - - if (data == null) { - return null; - } + final JSONResult result = request(ocapiConn, OkapiService.SERVICE_CACHE, params); - return parseCache(data); + return result.isSuccess ? parseCache(result.data) : null; } public static List<Geocache> getCachesAround(final Geopoint center, final OCApiConnector connector) { @@ -152,12 +149,38 @@ final class OkapiClient { return requestCaches(connector, params, valueMap); } + public static List<Geocache> getCachesNamed(final Geopoint center, final String namePart, final OCApiConnector connector) { + final Map<String, String> valueMap = new LinkedHashMap<String, String>(); + final Parameters params; + + // search around current position, if there is a position + if (center != null) { + final String centerString = GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, center) + SEPARATOR + GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, center); + params = new Parameters("search_method", METHOD_SEARCH_NEAREST); + valueMap.put("center", centerString); + valueMap.put("limit", "20"); + } + else { + params = new Parameters("search_method", METHOD_SEARCH_ALL); + valueMap.put("limit", "20"); + } + + // full wildcard search, maybe we need to change this after some testing and evaluation + valueMap.put("name", "*" + namePart + "*"); + return requestCaches(connector, params, valueMap); + } + private static List<Geocache> requestCaches(final OCApiConnector connector, final Parameters params, final Map<String, String> valueMap) { + // if a global type filter is set, and OKAPI does not know that type, then return an empty list instead of all caches + if (Settings.getCacheType() != CacheType.ALL && StringUtils.isBlank(getFilterFromType())) { + return Collections.emptyList(); + } + addFilterParams(valueMap, connector); params.add("search_params", new JSONObject(valueMap).toString()); addRetrieveParams(params, connector); - final JSONObject data = request(connector, OkapiService.SERVICE_SEARCH_AND_RETRIEVE, params); + final JSONObject data = request(connector, OkapiService.SERVICE_SEARCH_AND_RETRIEVE, params).data; if (data == null) { return Collections.emptyList(); @@ -166,7 +189,9 @@ final class OkapiClient { return parseCaches(data); } - // Assumes level 3 OAuth + /** + * Assumes level 3 OAuth. + */ public static List<Geocache> getCachesBBox(final Viewport viewport, final OCApiConnector connector) { if (viewport.getLatitudeSpan() == 0 || viewport.getLongitudeSpan() == 0) { @@ -188,7 +213,7 @@ final class OkapiClient { final Parameters params = new Parameters("cache_code", cache.getGeocode()); params.add("watched", watched ? "true" : "false"); - final JSONObject data = request(connector, OkapiService.SERVICE_MARK_CACHE, params); + final JSONObject data = request(connector, OkapiService.SERVICE_MARK_CACHE, params).data; if (data == null) { return false; @@ -212,7 +237,7 @@ final class OkapiClient { params.add("password", logPassword); } - final JSONObject data = request(connector, OkapiService.SERVICE_SUBMIT_LOG, params); + final JSONObject data = request(connector, OkapiService.SERVICE_SUBMIT_LOG, params).data; if (data == null) { return new LogResult(StatusCode.LOG_POST_ERROR, ""); @@ -265,7 +290,7 @@ final class OkapiClient { parseCoreCache(response, cache); - cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_CACHE)); + DataStore.saveCache(cache, EnumSet.of(SaveFlag.SAVE_CACHE)); } catch (final JSONException e) { Log.e("OkapiClient.parseSmallCache", e); } @@ -297,7 +322,7 @@ final class OkapiClient { final StringBuilder description = new StringBuilder(500); if (!response.isNull("gc_code")) { final String gccode = response.getString("gc_code"); - description.append(cgeoapplication.getInstance().getResources() + description.append(CgeoApplication.getInstance().getResources() .getString(R.string.cache_listed_on, GCConnector.getInstance().getName())) .append(": <a href=\"http://coord.info/") .append(gccode) @@ -318,13 +343,13 @@ final class OkapiClient { final JSONObject imageResponse = images.getJSONObject(i); final String title = imageResponse.getString(CACHE_IMAGE_CAPTION); final String url = absoluteUrl(imageResponse.getString(CACHE_IMAGE_URL), cache.getGeocode()); + // all images are added as spoiler images, although OKAPI has spoiler and non spoiler images cache.addSpoiler(new Image(url, title)); } } cache.setAttributes(parseAttributes(response.getJSONArray(CACHE_ATTRNAMES))); cache.setLogs(parseLogs(response.getJSONArray(CACHE_LATEST_LOGS))); - cache.setHidden(parseDate(response.getString(CACHE_HIDDEN))); //TODO: Store license per cache //cache.setLicense(response.getString("attribution_note")); cache.setWaypoints(parseWaypoints(response.getJSONArray(CACHE_WPTS)), false); @@ -338,7 +363,7 @@ final class OkapiClient { cache.setDetailedUpdatedNow(); // save full detailed caches - cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); + DataStore.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); } catch (final JSONException e) { Log.e("OkapiClient.parseCache", e); } @@ -363,6 +388,7 @@ final class OkapiClient { if (!response.isNull(CACHE_IS_FOUND)) { cache.setFound(response.getBoolean(CACHE_IS_FOUND)); } + cache.setHidden(parseDate(response.getString(CACHE_HIDDEN))); } private static String absoluteUrl(final String url, final String geocode) { @@ -597,14 +623,15 @@ final class OkapiClient { return res.toString(); } - private static JSONObject request(final OCApiConnector connector, final OkapiService service, final Parameters params) { + @NonNull + private static JSONResult request(final OCApiConnector connector, final OkapiService service, final Parameters params) { if (connector == null) { - return null; + return new JSONResult(null); } final String host = connector.getHost(); if (StringUtils.isBlank(host)) { - return null; + return new JSONResult(null); } params.add("langpref", getPreferredLanguage()); @@ -617,7 +644,7 @@ final class OkapiClient { } final String uri = "http://" + host + service.methodName; - return Network.requestJSON(uri, params); + return new JSONResult(Network.getRequest(uri, params)); } private static String getPreferredLanguage() { @@ -637,7 +664,7 @@ final class OkapiClient { valueMap.put("found_status", "notfound_only"); } if (Settings.getCacheType() != CacheType.ALL) { - valueMap.put("type", getFilterFromType(Settings.getCacheType())); + valueMap.put("type", getFilterFromType()); } } @@ -647,8 +674,8 @@ final class OkapiClient { params.add("wrap", "true"); } - private static String getFilterFromType(final CacheType cacheType) { - switch (cacheType) { + private static String getFilterFromType() { + switch (Settings.getCacheType()) { case EVENT: return "Event"; case MULTI: @@ -669,12 +696,16 @@ final class OkapiClient { public static UserInfo getUserInfo(final OCApiLiveConnector connector) { final Parameters params = new Parameters("fields", USER_INFO_FIELDS); - final JSONObject data = request(connector, OkapiService.SERVICE_USER, params); + final JSONResult result = request(connector, OkapiService.SERVICE_USER, params); - if (data == null) { - return new UserInfo(StringUtils.EMPTY, 0, UserInfoStatus.FAILED); + if (!result.isSuccess) { + final OkapiError error = new OkapiError(result.data); + Log.e("OkapiClient.getUserInfo: error getting user info: '" + error.getMessage() + "'"); + return new UserInfo(StringUtils.EMPTY, 0, UserInfoStatus.getFromOkapiError(error.getResult())); } + JSONObject data = result.data; + String name = StringUtils.EMPTY; boolean successUserName = false; @@ -702,4 +733,28 @@ final class OkapiClient { return new UserInfo(name, finds, successUserName && successFinds ? UserInfoStatus.SUCCESSFUL : UserInfoStatus.FAILED); } + /** + * Encapsulates response state and content of an HTTP-request that expects a JSON result. <code>isSuccess</code> is + * only true, if the response state was success and <code>data</code> is not null. + */ + private static class JSONResult { + + public final boolean isSuccess; + public final JSONObject data; + + public JSONResult(final @Nullable HttpResponse response) { + boolean isSuccess = Network.isSuccess(response); + final String responseData = Network.getResponseDataAlways(response); + JSONObject data = null; + if (responseData != null) { + try { + data = new JSONObject(responseData); + } catch (final JSONException e) { + Log.w("JSONResult", e); + } + } + this.data = data; + this.isSuccess = isSuccess && data != null; + } + } } diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiError.java b/main/src/cgeo/geocaching/connector/oc/OkapiError.java new file mode 100644 index 0000000..7faf2c7 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/oc/OkapiError.java @@ -0,0 +1,87 @@ +package cgeo.geocaching.connector.oc; + +import cgeo.geocaching.utils.Log; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Handles the JSON error response from OKAPI + */ +public class OkapiError { + + /** + * List of detected errors OKAPI might return + */ + public enum OkapiErrors { + NO_ERROR, + UNSPECIFIED, + INVALID_TIMESTAMP, + INVALID_TOKEN; + } + + @NonNull private final OkapiErrors state; + @NonNull private final String message; + + public OkapiError(@Nullable JSONObject data) { + + // A null-response is by definition an error (some exception occurred somewhere in the flow) + if (data == null) { + state = OkapiErrors.UNSPECIFIED; + message = StringUtils.EMPTY; + return; + } + // Second possibility: we get an error object as return (@see http://opencaching.pl/okapi/introduction.html#errors) + if (data.has("error")) { + String localmessage = null; + OkapiErrors localstate = OkapiErrors.UNSPECIFIED; + try { + JSONObject error = data.getJSONObject("error"); + // Check reason_stack element to look for the specific oauth problems we want to report back + if (error.has("reason_stack")) { + String reason = error.getString("reason_stack"); + if (StringUtils.contains(reason, "invalid_oauth_request")) { + if (StringUtils.contains(reason, "invalid_timestamp")) { + localstate = OkapiErrors.INVALID_TIMESTAMP; + } else if (StringUtils.contains(reason, "invalid_token")) { + localstate = OkapiErrors.INVALID_TOKEN; + } + } + } + // Check if we can extract a message as well + if (error.has("developer_message")) { + localmessage = error.getString("developer_message"); + assert localmessage != null; // by virtue of defaultString + } + } catch (JSONException ex) { + Log.d("OkapiError: Failed to parse JSON", ex); + localstate = OkapiErrors.UNSPECIFIED; + } + state = localstate; + message = StringUtils.defaultString(localmessage); + return; + } + + // Third possibility: some other response, everything is fine! + state = OkapiErrors.NO_ERROR; + message = StringUtils.EMPTY; + } + + public boolean isError() { + return state != OkapiErrors.NO_ERROR; + } + + @NonNull + public OkapiErrors getResult() { + return state; + } + + @NonNull + public String getMessage() { + return message; + } + +} diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java b/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java index c995975..c6be3cb 100644 --- a/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java @@ -9,10 +9,8 @@ import cgeo.geocaching.connector.LogResult; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.StatusCode; -import android.app.Activity; import android.net.Uri; -import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.List; @@ -23,48 +21,42 @@ public class OkapiLoggingManager implements ILoggingManager { private final Geocache cache; private LogCacheActivity activity; - private final static List<LogType> standardLogTypes = Arrays.asList(LogType.FOUND_IT, LogType.DIDNT_FIND_IT, LogType.NOTE); - private final static List<LogType> eventLogTypes = Arrays.asList(LogType.WILL_ATTEND, LogType.ATTENDED, LogType.NOTE); - - public OkapiLoggingManager(Activity activity, OCApiLiveConnector connector, Geocache cache) { + public OkapiLoggingManager(final LogCacheActivity activity, final OCApiLiveConnector connector, final Geocache cache) { this.connector = connector; this.cache = cache; - this.activity = (LogCacheActivity) activity; + this.activity = activity; } @Override - public void init() { + public final void init() { activity.onLoadFinished(); } @Override - public LogResult postLog(Geocache cache, LogType logType, Calendar date, String log, String logPassword, List<TrackableLog> trackableLogs) { + public final LogResult postLog(final Geocache cache, final LogType logType, final Calendar date, final String log, final String logPassword, final List<TrackableLog> trackableLogs) { final LogResult result = OkapiClient.postLog(cache, logType, date, log, logPassword, connector); connector.login(null, null); return result; } @Override - public ImageResult postLogImage(String logId, String imageCaption, String imageDescription, Uri imageUri) { + public final ImageResult postLogImage(final String logId, final String imageCaption, final String imageDescription, final Uri imageUri) { return new ImageResult(StatusCode.LOG_POST_ERROR, ""); } @Override - public boolean hasLoaderError() { + public final boolean hasLoaderError() { return false; } @Override - public List<TrackableLog> getTrackables() { + public final List<TrackableLog> getTrackables() { return Collections.emptyList(); } @Override public List<LogType> getPossibleLogTypes() { - if (cache.isEventCache()) { - return eventLogTypes; - } - - return standardLogTypes; + return connector.getPossibleLogTypes(cache); } + } diff --git a/main/src/cgeo/geocaching/connector/oc/UserInfo.java b/main/src/cgeo/geocaching/connector/oc/UserInfo.java index 0dc0440..c8b37cd 100644 --- a/main/src/cgeo/geocaching/connector/oc/UserInfo.java +++ b/main/src/cgeo/geocaching/connector/oc/UserInfo.java @@ -1,6 +1,7 @@ package cgeo.geocaching.connector.oc; import cgeo.geocaching.R; +import cgeo.geocaching.connector.oc.OkapiError.OkapiErrors; public class UserInfo { @@ -8,13 +9,28 @@ public class UserInfo { NOT_RETRIEVED(R.string.init_login_popup_working), SUCCESSFUL(R.string.init_login_popup_ok), FAILED(R.string.init_login_popup_failed), - NOT_SUPPORTED(R.string.init_login_popup_not_authorized); + NOT_SUPPORTED(R.string.init_login_popup_not_authorized), + INVALID_TIMESTAMP(R.string.init_login_popup_invalid_timestamp), + INVALID_TOKEN(R.string.init_login_popup_invalid_token); public final int resId; UserInfoStatus(int resId) { this.resId = resId; } + + public static UserInfoStatus getFromOkapiError(OkapiErrors result) { + switch (result) { + case NO_ERROR: + return SUCCESSFUL; + case INVALID_TIMESTAMP: + return INVALID_TIMESTAMP; + case INVALID_TOKEN: + return INVALID_TOKEN; + default: + return FAILED; + } + } } private final String name; diff --git a/main/src/cgeo/geocaching/connector/ox/OpenCachingApi.java b/main/src/cgeo/geocaching/connector/ox/OpenCachingApi.java index f25e289..9d1dfc7 100644 --- a/main/src/cgeo/geocaching/connector/ox/OpenCachingApi.java +++ b/main/src/cgeo/geocaching/connector/ox/OpenCachingApi.java @@ -1,9 +1,9 @@ package cgeo.geocaching.connector.ox; import cgeo.geocaching.Geocache; -import cgeo.geocaching.StoredList; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter; +import cgeo.geocaching.list.StoredList; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.utils.CryptUtils; @@ -11,7 +11,7 @@ import cgeo.geocaching.utils.Log; import ch.boye.httpclientandroidlib.HttpResponse; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import java.util.Collection; import java.util.Collections; diff --git a/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java b/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java index 66ca5f7..67180b3 100644 --- a/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java +++ b/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java @@ -1,8 +1,8 @@ package cgeo.geocaching.connector.trackable; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; import cgeo.geocaching.Trackable; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.utils.Log; import org.xml.sax.Attributes; @@ -35,15 +35,23 @@ public class GeokretyParser { @Override public void start(Attributes attributes) { try { - if (attributes.getIndex("id") > -1) { + final int indexId = attributes.getIndex("id"); + if (indexId > -1) { trackable.setGeocode(geocode(Integer.valueOf(attributes.getValue("id")))); } - if (attributes.getIndex("dist") > -1) { + final int indexDist = attributes.getIndex("dist"); + if (indexDist > -1) { trackable.setDistance(Float.valueOf(attributes.getValue("dist"))); } - if (attributes.getIndex("type") > -1) { + final int indexType = attributes.getIndex("type"); + if (indexType > -1) { trackable.setType(getType(Integer.valueOf(attributes.getValue("type")))); } + final int indexWaypoint = attributes.getIndex("waypoint"); + if (indexWaypoint > -1) { + trackable.setSpottedName(attributes.getValue(indexWaypoint)); + trackable.setSpottedType(Trackable.SPOTTED_CACHE); + } } catch (final NumberFormatException e) { Log.e("Parsing geokret", e); } @@ -63,15 +71,15 @@ public class GeokretyParser { protected static String getType(int type) { switch (type) { case 0: - return cgeoapplication.getInstance().getString(R.string.geokret_type_traditional); + return CgeoApplication.getInstance().getString(R.string.geokret_type_traditional); case 1: - return cgeoapplication.getInstance().getString(R.string.geokret_type_book_or_media); + return CgeoApplication.getInstance().getString(R.string.geokret_type_book_or_media); case 2: - return cgeoapplication.getInstance().getString(R.string.geokret_type_human); + return CgeoApplication.getInstance().getString(R.string.geokret_type_human); case 3: - return cgeoapplication.getInstance().getString(R.string.geokret_type_coin); + return CgeoApplication.getInstance().getString(R.string.geokret_type_coin); case 4: - return cgeoapplication.getInstance().getString(R.string.geokret_type_post); + return CgeoApplication.getInstance().getString(R.string.geokret_type_post); } return null; } |
