diff options
Diffstat (limited to 'main/src/cgeo/geocaching/connector/gc')
9 files changed, 544 insertions, 263 deletions
diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java index 50bf096..44d6e8f 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java @@ -4,30 +4,46 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.ICache; import cgeo.geocaching.R; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; import cgeo.geocaching.cgData; +import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.connector.AbstractConnector; +import cgeo.geocaching.connector.ILoggingManager; +import cgeo.geocaching.connector.capability.ILogin; import cgeo.geocaching.connector.capability.ISearchByCenter; import cgeo.geocaching.connector.capability.ISearchByGeocode; import cgeo.geocaching.connector.capability.ISearchByViewPort; -import cgeo.geocaching.enumerations.CacheRealm; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; +import cgeo.geocaching.settings.SettingsActivity; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.Log; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import android.app.Activity; +import android.content.Context; +import android.os.Handler; + import java.util.regex.Pattern; -public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort { +public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort, ILogin { private static final String CACHE_URL_SHORT = "http://coord.info/"; // Double slash is used to force open in browser private static final String CACHE_URL_LONG = "http://www.geocaching.com//seek/cache_details.aspx?wp="; - private static final Pattern gpxZipFilePattern = Pattern.compile("\\d{7,}(_.+)?\\.zip", Pattern.CASE_INSENSITIVE); + /** + * Pocket queries downloaded from the website use a numeric prefix. The pocket query creator Android app adds a + * verbatim "pocketquery" prefix. + */ + private static final Pattern GPX_ZIP_FILE_PATTERN = Pattern.compile("((\\d{7,})|(pocketquery))" + "(_.+)?" + "\\.zip", Pattern.CASE_INSENSITIVE); + + /** + * Pattern for GC codes + */ + private final static Pattern PATTERN_GC_CODE = Pattern.compile("GC[0-9A-Z]+", Pattern.CASE_INSENSITIVE); private GCConnector() { // singleton @@ -49,7 +65,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, if (geocode == null) { return false; } - return GCConstants.PATTERN_GC_CODE.matcher(geocode).matches() || GCConstants.PATTERN_TB_CODE.matcher(geocode).matches(); + return GCConnector.PATTERN_GC_CODE.matcher(geocode).matches(); } @Override @@ -63,6 +79,11 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override + public boolean supportsPersonalNote() { + return true; + } + + @Override public boolean supportsOwnCoordinates() { return true; } @@ -78,8 +99,23 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override + public boolean supportsLogImages() { + return true; + } + + @Override + public ILoggingManager getLoggingManager(Activity activity, Geocache cache) { + return new GCLoggingManager(activity, cache); + } + + @Override + public boolean canLog(Geocache cache) { + return StringUtils.isNotBlank(cache.getCacheId()); + } + + @Override public String getName() { - return "GeoCaching.com"; + return "geocaching.com"; } @Override @@ -135,7 +171,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, @Override public boolean isZippedGPXFile(final String fileName) { - return gpxZipFilePattern.matcher(fileName).matches(); + return GPX_ZIP_FILE_PATTERN.matcher(fileName).matches(); } @Override @@ -149,7 +185,8 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } - public static boolean addToWatchlist(Geocache cache) { + @Override + public boolean addToWatchlist(Geocache cache) { final boolean added = GCParser.addToWatchlist(cache); if (added) { cgData.saveChangedCache(cache); @@ -157,7 +194,8 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, return added; } - public static boolean removeFromWatchlist(Geocache cache) { + @Override + public boolean removeFromWatchlist(Geocache cache) { final boolean removed = GCParser.removeFromWatchlist(cache); if (removed) { cgData.saveChangedCache(cache); @@ -218,6 +256,15 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override + public boolean uploadPersonalNote(Geocache cache) { + final boolean uploaded = GCParser.uploadPersonalNote(cache); + if (uploaded) { + cgData.saveChangedCache(cache); + } + return uploaded; + } + + @Override public SearchResult searchByCenter(Geopoint center) { // TODO make search by coordinate use this method. currently it is just a marker that this connector supports search by center return null; @@ -234,12 +281,57 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public CacheRealm getCacheRealm() { - return CacheRealm.GC; + public boolean isActivated() { + return Settings.isGCConnectorActive(); } @Override - public boolean isActivated() { - return true; + public int getCacheMapMarkerId(boolean disabled) { + if (disabled) { + return R.drawable.marker_disabled; + } + return R.drawable.marker; + } + + @Override + public boolean login(Handler handler, Context fromActivity) { + // login + final StatusCode status = Login.login(); + + if (status == StatusCode.NO_ERROR) { + cgeoapplication.getInstance().firstRun = false; + Login.detectGcCustomDate(); + } + + if (cgeoapplication.getInstance().showLoginToast && handler != null) { + handler.sendMessage(handler.obtainMessage(0, status)); + cgeoapplication.getInstance().showLoginToast = false; + + // invoke settings activity to insert login details + if (status == StatusCode.NO_LOGIN_INFO_STORED && fromActivity != null) { + SettingsActivity.startWithServicesPage(fromActivity); + } + } + return status == StatusCode.NO_ERROR; + } + + @Override + public String getUserName() { + return Login.getActualUserName(); + } + + @Override + public int getCachesFound() { + return Login.getActualCachesFound(); + } + + @Override + public String getLoginStatusString() { + return Login.getActualStatus(); + } + + @Override + public boolean isLoggedIn() { + return Login.isActualLoginStatus(); } } diff --git a/main/src/cgeo/geocaching/connector/gc/GCConstants.java b/main/src/cgeo/geocaching/connector/gc/GCConstants.java index b66acb7..d908b47 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConstants.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConstants.java @@ -36,7 +36,7 @@ public final class GCConstants { public final static Pattern PATTERN_LATLON = Pattern.compile("<span id=\"uxLatLon\"[^>]*>(.*?)</span>"); public final static Pattern PATTERN_LATLON_ORIG = Pattern.compile("\\{\"isUserDefined\":true[^}]+?\"oldLatLngDisplay\":\"([^\"]+)\"\\}"); public final static Pattern PATTERN_LOCATION = Pattern.compile(Pattern.quote("<span id=\"ctl00_ContentBody_Location\">In ") + "(?:<a href=[^>]*>)?(.*?)<"); - public final static Pattern PATTERN_PERSONALNOTE = Pattern.compile("<p id=\"cache_note\"[^>]*>(.*?)</p>"); + public final static Pattern PATTERN_PERSONALNOTE = Pattern.compile("<p id=\"cache_note\"[^>]*>(.*?)</p>", Pattern.DOTALL); public final static Pattern PATTERN_NAME = Pattern.compile("<span id=\"ctl00_ContentBody_CacheName\">(.*?)</span>"); public final static Pattern PATTERN_DIFFICULTY = Pattern.compile("<span id=\"ctl00_ContentBody_uxLegendScale\"[^>]*>[^<]*<img src=\"[^\"]*/images/stars/stars([0-9_]+)\\.gif\""); public final static Pattern PATTERN_TERRAIN = Pattern.compile("<span id=\"ctl00_ContentBody_Localize[\\d]+\"[^>]*>[^<]*<img src=\"[^\"]*/images/stars/stars([0-9_]+)\\.gif\""); @@ -69,7 +69,7 @@ public final class GCConstants { public static final String MEMBER_STATUS_RENEW = "<a id=\"ctl00_hlRenew"; public static final String MEMBER_STATUS_PM = "Premium Member"; /** Use replaceAll("[,.]","") on the resulting string before converting to an int */ - public static final Pattern PATTERN_CACHES_FOUND = Pattern.compile("<strong[^>]*>.*?([\\d,.]+) Caches Found"); + public static final Pattern PATTERN_CACHES_FOUND = Pattern.compile("<strong[^>]*>.*?([\\d,.]+) Caches Found", Pattern.DOTALL); public static final Pattern PATTERN_AVATAR_IMAGE_PROFILE_PAGE = Pattern.compile("<img src=\"(http://img.geocaching.com/user/avatar/[0-9a-f-]+\\.jpg)\"[^>]*\\salt=\"Avatar\""); public static final Pattern PATTERN_LOGIN_NAME_LOGIN_PAGE = Pattern.compile("ctl00_ContentBody_lbUsername\">.*<strong>(.*)</strong>"); public static final Pattern PATTERN_CUSTOMDATE = Pattern.compile("<option selected=\"selected\" value=\"([ /Mdy-]+)\">"); @@ -84,6 +84,7 @@ public final class GCConstants { public final static String ERROR_TB_DOES_NOT_EXIST = "does not exist in the system"; public final static String ERROR_TB_ELEMENT_EXCEPTION = "ElementNotFound Exception"; public final static String ERROR_TB_ARITHMETIC_OVERFLOW = "operation resulted in an overflow"; + public final static String ERROR_TB_NOT_ACTIVATED = "hasn't been activated"; /** * some parts of the webpage don't correctly encode the name, therefore this pattern must be checked with a * trackable name that needs HTML encoding @@ -121,9 +122,9 @@ public final class GCConstants { public final static Pattern PATTERN_SEARCH_DIFFICULTY_TERRAIN = Pattern.compile("<span class=\"small\">([0-5]([\\.,]5)?)/([0-5]([\\.,]5)?)</span><br />"); public final static Pattern PATTERN_SEARCH_CONTAINER = Pattern.compile("<img src=\"/images/icons/container/([^\\.]+)\\.gif\""); public final static Pattern PATTERN_SEARCH_GEOCODE = Pattern.compile("\\|\\W*(GC[0-9A-Z]+)[^\\|]*\\|"); - public final static Pattern PATTERN_SEARCH_ID = Pattern.compile("name=\"CID\"[^v]*value=\"([0-9]+)\""); - public final static Pattern PATTERN_SEARCH_FAVORITE = Pattern.compile("<span id=\"ctl00_ContentBody_dlResults_ctl[0-9]+_uxFavoritesValue\" title=\"[^\"]*\" class=\"favorite-rank\">([0-9]+)</span>"); - public final static Pattern PATTERN_SEARCH_TOTALCOUNT = Pattern.compile("<td class=\"PageBuilderWidget\"><span>Total Records[^<]*<b>(\\d+)<\\/b>"); + public final static Pattern PATTERN_SEARCH_ID = Pattern.compile("name=\"CID\"[^v]*value=\"(\\d+)\""); + public final static Pattern PATTERN_SEARCH_FAVORITE = Pattern.compile("favorite-rank\">([0-9,.]+)</span>"); + public final static Pattern PATTERN_SEARCH_TOTALCOUNT = Pattern.compile("<span>Total Records\\D*(\\d+)<"); public final static Pattern PATTERN_SEARCH_RECAPTCHA = Pattern.compile("<script[^>]*src=\"[^\"]*/recaptcha/api/challenge\\?k=([^\"]+)\"[^>]*>"); public final static Pattern PATTERN_SEARCH_RECAPTCHACHALLENGE = Pattern.compile("challenge : '([^']+)'"); @@ -152,17 +153,11 @@ public final class GCConstants { public final static Pattern PATTERN_MAINTENANCE = Pattern.compile("<span id=\"ctl00_ContentBody_LogBookPanel1_lbConfirm\"[^>]*>([^<]*<font[^>]*>)?([^<]+)(</font>[^<]*)?</span>", Pattern.CASE_INSENSITIVE); public final static Pattern PATTERN_OK1 = Pattern.compile("<h2[^>]*>[^<]*<span id=\"ctl00_ContentBody_lbHeading\"[^>]*>[^<]*</span>[^<]*</h2>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); public final static Pattern PATTERN_OK2 = Pattern.compile("<div id=[\"|']ctl00_ContentBody_LogBookPanel1_ViewLogPanel[\"|']>", Pattern.CASE_INSENSITIVE); - public final static Pattern PATTERN_OK_IMAGEUPLOAD = Pattern.compile("<div id=[\"|']ctl00_ContentBody_ImageUploadControl1_uxUploadDonePanel[\"|']>", Pattern.CASE_INSENSITIVE); + public final static Pattern PATTERN_IMAGE_UPLOAD_URL = Pattern.compile("title=\"Click for Larger Image\"\\s*src=\"(.*?)\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); public final static Pattern PATTERN_VIEWSTATEFIELDCOUNT = Pattern.compile("id=\"__VIEWSTATEFIELDCOUNT\"[^(value)]+value=\"(\\d+)\"[^>]+>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); public final static Pattern PATTERN_VIEWSTATES = Pattern.compile("id=\"__VIEWSTATE(\\d*)\"[^(value)]+value=\"([^\"]+)\"[^>]+>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); public final static Pattern PATTERN_USERTOKEN = Pattern.compile("userToken\\s*=\\s*'([^']+)'"); - /** - * Patterns for GC and TB codes - */ - public final static Pattern PATTERN_GC_CODE = Pattern.compile("GC[0-9A-Z]+", Pattern.CASE_INSENSITIVE); - public final static Pattern PATTERN_TB_CODE = Pattern.compile("TB[0-9A-Z]+", Pattern.CASE_INSENSITIVE); - /** Live Map since 14.02.2012 */ public final static Pattern PATTERN_USERSESSION = Pattern.compile("UserSession\\('([^']+)'"); public final static Pattern PATTERN_SESSIONTOKEN = Pattern.compile("sessionToken:'([^']+)'"); @@ -194,7 +189,7 @@ public final class GCConstants { */ public static long gccodeToGCId(final String gccode) { long base = GC_BASE31; - String geocodeWO = gccode.substring(2).toUpperCase(Locale.US); + final String geocodeWO = gccode.substring(2).toUpperCase(Locale.US); if ((geocodeWO.length() < 4) || (geocodeWO.length() == 4 && SEQUENCE_GCID.indexOf(geocodeWO.charAt(0)) < 16)) { base = GC_BASE16; diff --git a/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java new file mode 100644 index 0000000..2aa5c75 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java @@ -0,0 +1,132 @@ +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; +import cgeo.geocaching.connector.ImageResult; +import cgeo.geocaching.connector.LogResult; +import cgeo.geocaching.enumerations.LogType; +import cgeo.geocaching.enumerations.StatusCode; +import cgeo.geocaching.loaders.UrlLoader; +import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.utils.Log; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; + +import android.app.Activity; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.Loader; + +import java.util.Calendar; +import java.util.Collections; +import java.util.List; + +public class GCLoggingManager implements ILoggingManager, LoaderManager.LoaderCallbacks<String> { + + private final LogCacheActivity activity; + private final Geocache cache; + + private String[] viewstates; + private List<TrackableLog> trackables; + private List<LogType> possibleLogTypes; + private boolean hasLoaderError = true; + + public GCLoggingManager(Activity activity, Geocache cache) { + this.activity = (LogCacheActivity) activity; + this.cache = cache; + } + + @Override + public Loader<String> onCreateLoader(int arg0, Bundle arg1) { + if (!Settings.isLogin()) { // allow offline logging + ActivityMixin.showToast(activity, activity.getResources().getString(R.string.err_login)); + return null; + } + return new UrlLoader(activity.getBaseContext(), "http://www.geocaching.com/seek/log.aspx", new Parameters("ID", cache.getCacheId())); + } + + @Override + public void onLoadFinished(Loader<String> arg0, String page) { + + if (page == null) { + hasLoaderError = true; + } else { + + viewstates = Login.getViewstates(page); + trackables = GCParser.parseTrackableLog(page); + possibleLogTypes = GCParser.parseTypes(page); + + hasLoaderError = possibleLogTypes.isEmpty(); + } + + activity.onLoadFinished(); + } + + @Override + public void onLoaderReset(Loader<String> arg0) { + // nothing to do + } + + @Override + public void init() { + activity.getSupportLoaderManager().initLoader(0, null, this); + } + + @Override + public LogResult postLog(Geocache cache, LogType logType, Calendar date, String log, String logPassword, List<TrackableLog> trackableLogs) { + + try { + final ImmutablePair<StatusCode, String> postResult = GCParser.postLog(cache.getGeocode(), cache.getCacheId(), viewstates, logType, + date.get(Calendar.YEAR), (date.get(Calendar.MONTH) + 1), date.get(Calendar.DATE), + log, trackableLogs); + + return new LogResult(postResult.left, postResult.right); + } catch (Exception e) { + Log.e("GCLoggingManager.postLog", e); + } + + return new LogResult(StatusCode.LOG_POST_ERROR, ""); + } + + @Override + public ImageResult postLogImage(String logId, String imageCaption, String imageDescription, Uri imageUri) { + + if (StringUtils.isNotBlank(imageUri.getPath())) { + + ImmutablePair<StatusCode, String> imageResult = GCParser.uploadLogImage(logId, imageCaption, imageDescription, imageUri); + + return new ImageResult(imageResult.left, imageResult.right); + } + + return new ImageResult(StatusCode.LOGIMAGE_POST_ERROR, ""); + } + + @Override + public boolean hasLoaderError() { + return hasLoaderError; + } + + @Override + public List<TrackableLog> getTrackables() { + if (hasLoaderError) { + return Collections.emptyList(); + } + return trackables; + } + + @Override + public List<LogType> getPossibleLogTypes() { + if (hasLoaderError) { + return Collections.emptyList(); + } + return possibleLogTypes; + } + +} diff --git a/main/src/cgeo/geocaching/connector/gc/GCMap.java b/main/src/cgeo/geocaching/connector/gc/GCMap.java index 49f61ef..4bc55fe 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCMap.java +++ b/main/src/cgeo/geocaching/connector/gc/GCMap.java @@ -2,7 +2,7 @@ package cgeo.geocaching.connector.gc; import cgeo.geocaching.Geocache; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.cgData; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.CacheSize; @@ -92,7 +92,7 @@ public class GCMap { JSONObject ownerObj = dataObject.getJSONObject("owner"); cache.setOwnerDisplayName(ownerObj.getString("text")); - result.addCache(cache); + result.addAndPutInCache(cache); } } catch (JSONException e) { @@ -231,13 +231,13 @@ public class GCMap { exclude = true; } if (!exclude) { - searchResult.addCache(cache); + searchResult.addAndPutInCache(cache); } } Log.d("Retrieved " + searchResult.getCount() + " caches for tile " + tile.toString()); } catch (Exception e) { - Log.e("GCBase.parseMapJSON", e); + Log.e("GCMap.parseMapJSON", e); } return searchResult; @@ -283,7 +283,7 @@ public class GCMap { * @return */ private static SearchResult searchByViewport(final Viewport viewport, final String[] tokens, Strategy strategy) { - Log.d("GCBase.searchByViewport" + viewport.toString()); + Log.d("GCMap.searchByViewport" + viewport.toString()); final SearchResult searchResult = new SearchResult(); searchResult.setUrl(GCConstants.URL_LIVE_MAP + "?ll=" + viewport.getCenter().getLatitude() + "," + viewport.getCenter().getLongitude()); @@ -330,14 +330,14 @@ public class GCMap { String data = Tile.requestMapInfo(GCConstants.URL_MAP_INFO, params, GCConstants.URL_LIVE_MAP); if (StringUtils.isEmpty(data)) { - Log.w("GCBase.searchByViewport: No data from server for tile (" + tile.getX() + "/" + tile.getY() + ")"); + Log.w("GCMap.searchByViewport: No data from server for tile (" + tile.getX() + "/" + tile.getY() + ")"); } else { final SearchResult search = GCMap.parseMapJSON(data, tile, bitmap, strategy); if (search == null || CollectionUtils.isEmpty(search.getGeocodes())) { - Log.e("GCBase.searchByViewport: No cache parsed for viewport " + viewport); + Log.e("GCMap.searchByViewport: No cache parsed for viewport " + viewport); } else { - searchResult.addGeocodes(search.getGeocodes()); + searchResult.addSearchResult(search); } Tile.Cache.add(tile); } @@ -349,20 +349,21 @@ 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)); + } } - if (strategy.flags.contains(StrategyFlag.SEARCH_NEARBY)) { + if (strategy.flags.contains(StrategyFlag.SEARCH_NEARBY) && Settings.isPremiumMember()) { final Geopoint center = viewport.getCenter(); if ((lastSearchViewport == null) || !lastSearchViewport.contains(center)) { //FIXME We don't have a RecaptchaReceiver!? SearchResult search = GCParser.searchByCoords(center, Settings.getCacheType(), false, null); if (search != null && !search.isEmpty()) { final Set<String> geocodes = search.getGeocodes(); - if (Settings.isPremiumMember()) { - lastSearchViewport = cgData.getBounds(geocodes); - } else { - lastSearchViewport = new Viewport(center, center); - } + lastSearchViewport = cgData.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 6b456fd..9ecb51b 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCParser.java +++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java @@ -5,7 +5,7 @@ import cgeo.geocaching.Image; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.Trackable; import cgeo.geocaching.TrackableLog; import cgeo.geocaching.Waypoint; @@ -28,10 +28,10 @@ import cgeo.geocaching.loaders.RecaptchaReceiver; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.ui.DirectionImage; -import cgeo.geocaching.utils.BaseUtils; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.MatcherWrapper; +import cgeo.geocaching.utils.TextUtils; import ch.boye.httpclientandroidlib.HttpResponse; @@ -79,14 +79,14 @@ public abstract class GCParser { // recaptcha String recaptchaChallenge = null; if (showCaptcha) { - String recaptchaJsParam = BaseUtils.getMatch(page, GCConstants.PATTERN_SEARCH_RECAPTCHA, false, null); + final String recaptchaJsParam = TextUtils.getMatch(page, GCConstants.PATTERN_SEARCH_RECAPTCHA, false, null); if (recaptchaJsParam != null) { final Parameters params = new Parameters("k", recaptchaJsParam.trim()); final String recaptchaJs = Network.getResponseData(Network.getRequest("http://www.google.com/recaptcha/api/challenge", params)); if (StringUtils.isNotBlank(recaptchaJs)) { - recaptchaChallenge = BaseUtils.getMatch(recaptchaJs, GCConstants.PATTERN_SEARCH_RECAPTCHACHALLENGE, true, 1, null, true); + recaptchaChallenge = TextUtils.getMatch(recaptchaJs, GCConstants.PATTERN_SEARCH_RECAPTCHACHALLENGE, true, 1, null, true); } } if (thread != null && StringUtils.isNotBlank(recaptchaChallenge)) { @@ -109,7 +109,7 @@ public abstract class GCParser { page = page.substring(startPos); // cut on <table startPos = page.indexOf('>'); - int endPos = page.indexOf("ctl00_ContentBody_UnitTxt"); + final int endPos = page.indexOf("ctl00_ContentBody_UnitTxt"); if (startPos == -1 || endPos == -1) { Log.e("GCParser.parseSearch: ID \"ctl00_ContentBody_UnitTxt\" not found on page"); return null; @@ -120,9 +120,10 @@ public abstract class GCParser { final String[] rows = page.split("<tr class="); final int rows_count = rows.length; + int excludedCaches = 0; for (int z = 1; z < rows_count; z++) { final Geocache cache = new Geocache(); - String row = rows[z]; + final String row = rows[z]; // check for cache type presence if (!row.contains("images/wpttypes")) { @@ -150,33 +151,35 @@ public abstract class GCParser { } } } - } catch (Exception e) { + } catch (final Exception e) { // failed to parse GUID and/or Disabled Log.w("GCParser.parseSearch: Failed to parse GUID and/or Disabled data"); } if (Settings.isExcludeDisabledCaches() && (cache.isDisabled() || cache.isArchived())) { // skip disabled and archived caches + excludedCaches++; continue; } - cache.setGeocode(BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_GEOCODE, true, 1, cache.getGeocode(), true)); + cache.setGeocode(TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_GEOCODE, true, 1, cache.getGeocode(), true)); // cache type - cache.setType(CacheType.getByPattern(BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_TYPE, true, 1, null, true))); + cache.setType(CacheType.getByPattern(TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_TYPE, true, 1, null, true))); // cache direction - image if (Settings.getLoadDirImg()) { - final String direction = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION_DISTANCE, false, 1, null, false); + final String direction = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION_DISTANCE, false, 1, null, false); if (direction != null) { cache.setDirectionImg(direction); } } // cache distance - estimated distance for basic members - final String distance = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION_DISTANCE, false, 2, null, false); + final String distance = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION_DISTANCE, false, 2, null, false); if (distance != null) { - cache.setDistance(DistanceParser.parseDistance(distance, Settings.isUseMetricUnits())); + cache.setDistance(DistanceParser.parseDistance(distance, + !Settings.isUseImperialUnits())); } // difficulty/terrain @@ -193,7 +196,7 @@ public abstract class GCParser { } // size - final String container = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_CONTAINER, false, 1, null, false); + final String container = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_CONTAINER, false, 1, null, false); cache.setSize(CacheSize.getById(container)); // cache inventory @@ -203,7 +206,7 @@ public abstract class GCParser { if (matcherTbs.groupCount() > 0) { try { cache.setInventoryItems(Integer.parseInt(matcherTbs.group(1))); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("Error parsing trackables count", e); } inventoryPre = matcherTbs.group(2); @@ -229,7 +232,7 @@ public abstract class GCParser { cache.setFound(row.contains("/images/icons/16/found.png")); // id - String result = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_ID, null); + String result = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_ID, null); if (null != result) { cache.setCacheId(result); cids.add(cache.getCacheId()); @@ -237,24 +240,24 @@ public abstract class GCParser { // favorite count try { - result = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_FAVORITE, false, 1, null, true); + result = getNumberString(TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_FAVORITE, false, 1, null, true)); if (null != result) { cache.setFavoritePoints(Integer.parseInt(result)); } - } catch (NumberFormatException e) { - Log.w("GCParser.parseSearch: Failed to parse favourite count"); + } catch (final NumberFormatException e) { + Log.w("GCParser.parseSearch: Failed to parse favorite count"); } - searchResult.addCache(cache); + searchResult.addAndPutInCache(cache); } // total caches found try { - String result = BaseUtils.getMatch(page, GCConstants.PATTERN_SEARCH_TOTALCOUNT, false, 1, null, true); + final String result = TextUtils.getMatch(page, GCConstants.PATTERN_SEARCH_TOTALCOUNT, false, 1, null, true); if (null != result) { - searchResult.setTotal(Integer.parseInt(result)); + searchResult.setTotal(Integer.parseInt(result) - excludedCaches); } - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.w("GCParser.parseSearch: Failed to parse cache count"); } @@ -284,7 +287,7 @@ public abstract class GCParser { params.put("__VIEWSTATEFIELDCOUNT", String.valueOf(searchResult.viewstates.length)); } } - for (String cid : cids) { + for (final String cid : cids) { params.put("CID", cid); } @@ -308,7 +311,7 @@ public abstract class GCParser { LocParser.parseLoc(searchResult, coordinates); - } catch (Exception e) { + } catch (final Exception e) { Log.e("GCParser.parseSearch.CIDs", e); } } @@ -316,7 +319,7 @@ public abstract class GCParser { // get direction images if (Settings.getLoadDirImg()) { final Set<Geocache> caches = searchResult.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB); - for (Geocache cache : caches) { + for (final Geocache cache : caches) { if (cache.getCoords() == null && StringUtils.isNotEmpty(cache.getDirectionImg())) { DirectionImage.getDrawable(cache.getDirectionImg()); } @@ -327,15 +330,17 @@ public abstract class GCParser { } private static Float parseStars(final String value) { - float floatValue = Float.parseFloat(StringUtils.replaceChars(value, ',', '.')); + final float floatValue = Float.parseFloat(StringUtils.replaceChars(value, ',', '.')); return floatValue >= 0.5 && floatValue <= 5.0 ? floatValue : null; } static SearchResult parseCache(final String page, final CancellableHandler handler) { final SearchResult searchResult = parseCacheFromText(page, handler); + // attention: parseCacheFromText already stores implicitely through searchResult.addCache if (searchResult != null && !searchResult.getGeocodes().isEmpty()) { final Geocache cache = searchResult.getFirstCacheFromResult(LoadFlags.LOAD_CACHE_OR_DB); getExtraOnlineInfo(cache, page, handler); + // too late: it is already stored through parseCacheFromText cache.setDetailedUpdatedNow(); if (CancellableHandler.isCancelled(handler)) { return null; @@ -352,61 +357,70 @@ public abstract class GCParser { return searchResult; } - static SearchResult parseCacheFromText(final String page, final CancellableHandler handler) { + static SearchResult parseCacheFromText(final String pageIn, final CancellableHandler handler) { CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_details); - if (StringUtils.isBlank(page)) { + if (StringUtils.isBlank(pageIn)) { Log.e("GCParser.parseCache: No page given"); return null; } final SearchResult searchResult = new SearchResult(); - if (page.contains(GCConstants.STRING_UNPUBLISHED_OTHER) || page.contains(GCConstants.STRING_UNPUBLISHED_OWNER) || page.contains(GCConstants.STRING_UNPUBLISHED_FROM_SEARCH)) { + if (pageIn.contains(GCConstants.STRING_UNPUBLISHED_OTHER) || pageIn.contains(GCConstants.STRING_UNPUBLISHED_OWNER) || pageIn.contains(GCConstants.STRING_UNPUBLISHED_FROM_SEARCH)) { searchResult.setError(StatusCode.UNPUBLISHED_CACHE); return searchResult; } - if (page.contains(GCConstants.STRING_PREMIUMONLY_1) || page.contains(GCConstants.STRING_PREMIUMONLY_2)) { + if (pageIn.contains(GCConstants.STRING_PREMIUMONLY_1) || pageIn.contains(GCConstants.STRING_PREMIUMONLY_2)) { searchResult.setError(StatusCode.PREMIUM_ONLY); return searchResult; } - final String cacheName = Html.fromHtml(BaseUtils.getMatch(page, GCConstants.PATTERN_NAME, true, "")).toString(); + 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; } + // first handle the content with line breaks, then trim everything for easier matching and reduced memory consumption in parsed fields + String personalNoteWithLineBreaks = ""; + MatcherWrapper matcher = new MatcherWrapper(GCConstants.PATTERN_PERSONALNOTE, pageIn); + if (matcher.find()) { + personalNoteWithLineBreaks = matcher.group(1).trim(); + } + + final String page = TextUtils.replaceWhitespace(pageIn); + final Geocache cache = new Geocache(); cache.setDisabled(page.contains(GCConstants.STRING_DISABLED)); cache.setArchived(page.contains(GCConstants.STRING_ARCHIVED)); - cache.setPremiumMembersOnly(BaseUtils.matches(page, GCConstants.PATTERN_PREMIUMMEMBERS)); + cache.setPremiumMembersOnly(TextUtils.matches(page, GCConstants.PATTERN_PREMIUMMEMBERS)); - cache.setFavorite(BaseUtils.matches(page, GCConstants.PATTERN_FAVORITE)); + cache.setFavorite(TextUtils.matches(page, GCConstants.PATTERN_FAVORITE)); // cache geocode - cache.setGeocode(BaseUtils.getMatch(page, GCConstants.PATTERN_GEOCODE, true, cache.getGeocode())); + cache.setGeocode(TextUtils.getMatch(page, GCConstants.PATTERN_GEOCODE, true, cache.getGeocode())); // cache id - cache.setCacheId(BaseUtils.getMatch(page, GCConstants.PATTERN_CACHEID, true, cache.getCacheId())); + cache.setCacheId(TextUtils.getMatch(page, GCConstants.PATTERN_CACHEID, true, cache.getCacheId())); // cache guid - cache.setGuid(BaseUtils.getMatch(page, GCConstants.PATTERN_GUID, true, cache.getGuid())); + cache.setGuid(TextUtils.getMatch(page, GCConstants.PATTERN_GUID, true, cache.getGuid())); // name cache.setName(cacheName); // owner real name - cache.setOwnerUserId(Network.decode(BaseUtils.getMatch(page, GCConstants.PATTERN_OWNER_USERID, true, cache.getOwnerUserId()))); + cache.setOwnerUserId(Network.decode(TextUtils.getMatch(page, GCConstants.PATTERN_OWNER_USERID, true, cache.getOwnerUserId()))); cache.setUserModifiedCoords(false); String tableInside = page; - int pos = tableInside.indexOf(GCConstants.STRING_CACHEDETAILS); + final int pos = tableInside.indexOf(GCConstants.STRING_CACHEDETAILS); if (pos == -1) { Log.e("GCParser.parseCache: ID \"cacheDetails\" not found on page"); return null; @@ -416,96 +430,96 @@ public abstract class GCParser { if (StringUtils.isNotBlank(tableInside)) { // cache terrain - String result = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_TERRAIN, true, null); + String result = TextUtils.getMatch(tableInside, GCConstants.PATTERN_TERRAIN, true, null); if (result != null) { try { cache.setTerrain(Float.parseFloat(StringUtils.replaceChars(result, '_', '.'))); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("Error parsing terrain value", e); } } // cache difficulty - result = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_DIFFICULTY, true, null); + result = TextUtils.getMatch(tableInside, GCConstants.PATTERN_DIFFICULTY, true, null); if (result != null) { try { cache.setDifficulty(Float.parseFloat(StringUtils.replaceChars(result, '_', '.'))); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("Error parsing difficulty value", e); } } // owner - cache.setOwnerDisplayName(StringEscapeUtils.unescapeHtml4(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_OWNER_DISPLAYNAME, true, cache.getOwnerDisplayName()))); + cache.setOwnerDisplayName(StringEscapeUtils.unescapeHtml4(TextUtils.getMatch(tableInside, GCConstants.PATTERN_OWNER_DISPLAYNAME, true, cache.getOwnerDisplayName()))); // hidden try { - String hiddenString = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDEN, true, null); + String hiddenString = TextUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDEN, true, null); if (StringUtils.isNotBlank(hiddenString)) { cache.setHidden(Login.parseGcCustomDate(hiddenString)); } if (cache.getHiddenDate() == null) { // event date - hiddenString = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDENEVENT, true, null); + hiddenString = TextUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDENEVENT, true, null); if (StringUtils.isNotBlank(hiddenString)) { cache.setHidden(Login.parseGcCustomDate(hiddenString)); } } - } catch (ParseException e) { + } catch (final ParseException e) { // failed to parse cache hidden date Log.w("GCParser.parseCache: Failed to parse cache hidden (event) date"); } - // favourite + // favorite try { - cache.setFavoritePoints(Integer.parseInt(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_FAVORITECOUNT, true, "0"))); - } catch (NumberFormatException e) { - Log.e("Error parsing favourite count", e); + cache.setFavoritePoints(Integer.parseInt(TextUtils.getMatch(tableInside, GCConstants.PATTERN_FAVORITECOUNT, true, "0"))); + } catch (final NumberFormatException e) { + Log.e("Error parsing favorite count", e); } // cache size - cache.setSize(CacheSize.getById(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_SIZE, true, CacheSize.NOT_CHOSEN.id))); + cache.setSize(CacheSize.getById(TextUtils.getMatch(tableInside, GCConstants.PATTERN_SIZE, true, CacheSize.NOT_CHOSEN.id))); } // cache found - cache.setFound(BaseUtils.matches(page, GCConstants.PATTERN_FOUND) || BaseUtils.matches(page, GCConstants.PATTERN_FOUND_ALTERNATIVE)); + cache.setFound(TextUtils.matches(page, GCConstants.PATTERN_FOUND) || TextUtils.matches(page, GCConstants.PATTERN_FOUND_ALTERNATIVE)); // cache found date try { - final String foundDateString = BaseUtils.getMatch(page, GCConstants.PATTERN_FOUND_DATE, true, null); + final String foundDateString = TextUtils.getMatch(page, GCConstants.PATTERN_FOUND_DATE, true, null); if (StringUtils.isNotBlank(foundDateString)) { cache.setVisitedDate(Login.parseGcCustomDate(foundDateString).getTime()); } - } catch (ParseException e) { + } catch (final ParseException e) { // failed to parse cache found date Log.w("GCParser.parseCache: Failed to parse cache found date"); } // cache type - cache.setType(CacheType.getByPattern(BaseUtils.getMatch(page, GCConstants.PATTERN_TYPE, true, cache.getType().id))); + cache.setType(CacheType.getByPattern(TextUtils.getMatch(page, GCConstants.PATTERN_TYPE, true, cache.getType().id))); // on watchlist - cache.setOnWatchlist(BaseUtils.matches(page, GCConstants.PATTERN_WATCHLIST)); + cache.setOnWatchlist(TextUtils.matches(page, GCConstants.PATTERN_WATCHLIST)); // latitude and longitude. Can only be retrieved if user is logged in - String latlon = BaseUtils.getMatch(page, GCConstants.PATTERN_LATLON, true, ""); + String latlon = TextUtils.getMatch(page, GCConstants.PATTERN_LATLON, true, ""); if (StringUtils.isNotEmpty(latlon)) { try { cache.setCoords(new Geopoint(latlon)); cache.setReliableLatLon(true); - } catch (Geopoint.GeopointException e) { + } catch (final Geopoint.GeopointException e) { Log.w("GCParser.parseCache: Failed to parse cache coordinates", e); } } // cache location - cache.setLocation(BaseUtils.getMatch(page, GCConstants.PATTERN_LOCATION, true, "")); + cache.setLocation(TextUtils.getMatch(page, GCConstants.PATTERN_LOCATION, true, "")); // cache hint - String result = BaseUtils.getMatch(page, GCConstants.PATTERN_HINT, false, null); + final String result = TextUtils.getMatch(page, GCConstants.PATTERN_HINT, false, null); if (result != null) { // replace linebreak and paragraph tags - String hint = GCConstants.PATTERN_LINEBREAK.matcher(result).replaceAll("\n"); + final String hint = GCConstants.PATTERN_LINEBREAK.matcher(result).replaceAll("\n"); if (hint != null) { cache.setHint(StringUtils.replace(hint, "</p>", "").trim()); } @@ -514,17 +528,17 @@ public abstract class GCParser { cache.checkFields(); // cache personal note - cache.setPersonalNote(BaseUtils.getMatch(page, GCConstants.PATTERN_PERSONALNOTE, true, cache.getPersonalNote())); + cache.setPersonalNote(personalNoteWithLineBreaks); // cache short description - cache.setShortDescription(BaseUtils.getMatch(page, GCConstants.PATTERN_SHORTDESC, true, "")); + cache.setShortDescription(TextUtils.getMatch(page, GCConstants.PATTERN_SHORTDESC, true, "")); // cache description - cache.setDescription(BaseUtils.getMatch(page, GCConstants.PATTERN_DESC, true, "")); + cache.setDescription(TextUtils.getMatch(page, GCConstants.PATTERN_DESC, true, "")); // cache attributes try { - final String attributesPre = BaseUtils.getMatch(page, GCConstants.PATTERN_ATTRIBUTES, true, null); + final String attributesPre = TextUtils.getMatch(page, GCConstants.PATTERN_ATTRIBUTES, true, null); if (null != attributesPre) { final MatcherWrapper matcherAttributesInside = new MatcherWrapper(GCConstants.PATTERN_ATTRIBUTESINSIDE, attributesPre); @@ -537,8 +551,8 @@ public abstract class GCParser { // if the image name can be recognized, use the image name as attribute final String imageName = matcherAttributesInside.group(1).trim(); if (StringUtils.isNotEmpty(imageName)) { - int start = imageName.lastIndexOf('/'); - int end = imageName.lastIndexOf('.'); + final int start = imageName.lastIndexOf('/'); + final int end = imageName.lastIndexOf('.'); if (start >= 0 && end >= 0) { attribute = imageName.substring(start + 1, end).replace('-', '_').toLowerCase(Locale.US); } @@ -548,7 +562,7 @@ public abstract class GCParser { } cache.setAttributes(attributes); } - } catch (Exception e) { + } catch (final Exception e) { // failed to parse cache attributes Log.w("GCParser.parseCache: Failed to parse cache attributes"); } @@ -565,7 +579,7 @@ public abstract class GCParser { while (matcherSpoilersInside.find()) { // the original spoiler URL (include .../display/... contains a low-resolution image // if we shorten the URL we get the original-resolution image - String url = matcherSpoilersInside.group(1).replace("/display", ""); + final String url = matcherSpoilersInside.group(1).replace("/display", ""); String title = null; if (matcherSpoilersInside.group(3) != null) { @@ -577,7 +591,7 @@ public abstract class GCParser { } cache.addSpoiler(new Image(url, title, description)); } - } catch (Exception e) { + } catch (final Exception e) { // failed to parse cache spoilers Log.w("GCParser.parseCache: Failed to parse cache spoilers"); } @@ -611,20 +625,20 @@ public abstract class GCParser { } } } - } catch (Exception e) { + } catch (final Exception e) { // failed to parse cache inventory Log.w("GCParser.parseCache: Failed to parse cache inventory (2)"); } // cache logs counts try { - final String countlogs = BaseUtils.getMatch(page, GCConstants.PATTERN_COUNTLOGS, true, null); + final String countlogs = TextUtils.getMatch(page, GCConstants.PATTERN_COUNTLOGS, true, null); if (null != countlogs) { final MatcherWrapper matcherLog = new MatcherWrapper(GCConstants.PATTERN_COUNTLOG, countlogs); while (matcherLog.find()) { - String typeStr = matcherLog.group(1); - String countStr = matcherLog.group(2).replaceAll("[.,]", ""); + final String typeStr = matcherLog.group(1); + final String countStr = getNumberString(matcherLog.group(2)); if (StringUtils.isNotBlank(typeStr) && LogType.UNKNOWN != LogType.getByIconName(typeStr) @@ -633,7 +647,7 @@ public abstract class GCParser { } } } - } catch (Exception e) { + } catch (final Exception e) { // failed to parse logs Log.w("GCParser.parseCache: Failed to parse cache log count"); } @@ -643,7 +657,7 @@ public abstract class GCParser { // add waypoint for original coordinates in case of user-modified listing-coordinates try { - final String originalCoords = BaseUtils.getMatch(page, GCConstants.PATTERN_LATLON_ORIG, false, null); + 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); @@ -651,7 +665,7 @@ public abstract class GCParser { cache.addOrChangeWaypoint(waypoint, false); cache.setUserModifiedCoords(true); } - } catch (Geopoint.GeopointException e) { + } catch (final Geopoint.GeopointException e) { } int wpBegin = page.indexOf("<table class=\"Table\" id=\"ctl00_ContentBody_Waypoints\">"); @@ -685,21 +699,21 @@ public abstract class GCParser { // waypoint name // res is null during the unit tests - final String name = BaseUtils.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 = BaseUtils.getMatch(wp[3], GCConstants.PATTERN_WPTYPE, null); + final String resulttype = TextUtils.getMatch(wp[3], GCConstants.PATTERN_WPTYPE, null); final Waypoint waypoint = new Waypoint(name, WaypointType.findById(resulttype), false); // waypoint prefix - waypoint.setPrefix(BaseUtils.getMatch(wp[4], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, true, 2, waypoint.getPrefix(), false)); + waypoint.setPrefix(TextUtils.getMatch(wp[4], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, true, 2, waypoint.getPrefix(), false)); // waypoint lookup - waypoint.setLookup(BaseUtils.getMatch(wp[5], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, true, 2, waypoint.getLookup(), false)); + waypoint.setLookup(TextUtils.getMatch(wp[5], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, true, 2, waypoint.getLookup(), false)); // waypoint latitude and logitude - latlon = Html.fromHtml(BaseUtils.getMatch(wp[7], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, false, 2, "", false)).toString().trim(); + latlon = Html.fromHtml(TextUtils.getMatch(wp[7], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, false, 2, "", false)).toString().trim(); if (!StringUtils.startsWith(latlon, "???")) { waypoint.setLatlon(latlon); waypoint.setCoords(new Geopoint(latlon)); @@ -711,7 +725,7 @@ public abstract class GCParser { } // waypoint note - waypoint.setNote(BaseUtils.getMatch(wp[3], GCConstants.PATTERN_WPNOTE, waypoint.getNote())); + waypoint.setNote(TextUtils.getMatch(wp[3], GCConstants.PATTERN_WPNOTE, waypoint.getNote())); cache.addOrChangeWaypoint(waypoint, false); } @@ -729,10 +743,18 @@ public abstract class GCParser { return searchResult; } - searchResult.addCache(cache); + cache.setDetailedUpdatedNow(); + searchResult.addAndPutInCache(cache); return searchResult; } + private static String getNumberString(final String numberWithPunctuation) { + if (numberWithPunctuation == null) { + return null; + } + return numberWithPunctuation.replaceAll("[.,]", ""); + } + public static SearchResult searchByNextPage(final SearchResult search, boolean showCaptcha, RecaptchaReceiver recaptchaReceiver) { if (search == null) { return search; @@ -782,7 +804,7 @@ public abstract class GCParser { // save to application search.setError(searchResult.getError()); search.setViewstates(searchResult.viewstates); - for (String geocode : searchResult.getGeocodes()) { + for (final String geocode : searchResult.getGeocodes()) { search.addGeocode(geocode); } return search; @@ -859,7 +881,7 @@ public abstract class GCParser { } private static boolean isSearchForMyCaches(final String userName) { - if (userName.equalsIgnoreCase(Settings.getLogin().left)) { + if (userName.equalsIgnoreCase(Settings.getGcLogin().left)) { Log.i("Overriding users choice because of self search, downloading all caches."); return true; } @@ -893,7 +915,7 @@ public abstract class GCParser { return null; } try { - JSONObject response = Network.requestJSON("http://www.geocaching.com/api/geocode", new Parameters("q", address)); + final JSONObject response = Network.requestJSON("http://www.geocaching.com/api/geocode", new Parameters("q", address)); if (response == null) { return null; } @@ -903,12 +925,12 @@ public abstract class GCParser { if (!response.has("data")) { return null; } - JSONObject data = response.getJSONObject("data"); + final JSONObject data = response.getJSONObject("data"); if (data == null) { return null; } return searchByCoords(new Geopoint(data.getDouble("lat"), data.getDouble("lng")), cacheType, showCaptcha, recaptchaReceiver); - } catch (JSONException e) { + } catch (final JSONException e) { Log.w("GCParser.searchByAddress", e); } @@ -1005,7 +1027,7 @@ public abstract class GCParser { final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/seek/log.aspx").encodedQuery("ID=" + cacheid).build().toString(); String page = Login.postRequestLogged(uri, params); if (!Login.getLoginStatus(page)) { - Log.e("GCParser.postLogTrackable: Can not log in geocaching"); + Log.e("GCParser.postLog: Cannot log in geocaching"); return new ImmutablePair<StatusCode, String>(StatusCode.NOT_LOGGED_IN, ""); } @@ -1033,7 +1055,7 @@ public abstract class GCParser { if (trackables != null && !trackables.isEmpty()) { // we have some trackables to proceed final StringBuilder hdnSelected = new StringBuilder(); - for (TrackableLog tb : trackables) { + for (final TrackableLog tb : trackables) { final String action = Integer.toString(tb.id) + tb.action.action; final StringBuilder paramText = new StringBuilder("ctl00$ContentBody$LogBookPanel1$uxTrackables$repTravelBugs$ctl"); @@ -1054,7 +1076,7 @@ public abstract class GCParser { page = Network.getResponseData(Network.postRequest(uri, params)); } - } catch (Exception e) { + } catch (final Exception e) { Log.e("GCParser.postLog.confim", e); } @@ -1074,11 +1096,11 @@ public abstract class GCParser { Login.setActualCachesFound(Login.getActualCachesFound() + 1); } - final String logID = BaseUtils.getMatch(page, GCConstants.PATTERN_LOG_IMAGE_UPLOAD, ""); + final String logID = TextUtils.getMatch(page, GCConstants.PATTERN_LOG_IMAGE_UPLOAD, ""); return new ImmutablePair<StatusCode, String>(StatusCode.NO_ERROR, logID); } - } catch (Exception e) { + } catch (final Exception e) { Log.e("GCParser.postLog.check", e); } @@ -1099,20 +1121,13 @@ public abstract class GCParser { * the URI for the image to be uploaded * @return status code to indicate success or failure */ - public static StatusCode uploadLogImage(final String logId, final String caption, final String description, final Uri imageUri) { - final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/seek/upload.aspx").encodedQuery("LID=" + logId).build().toString(); + public static ImmutablePair<StatusCode, String> uploadLogImage(final String logId, final String caption, final String description, final Uri imageUri) { + final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/seek/upload.aspx").build().toString(); - String page = Network.getResponseData(Network.getRequest(uri)); - - if (!Login.getLoginStatus(page)) { - // Login.isActualLoginStatus() was wrong, we are not logged in - final StatusCode loginState = Login.login(); - if (loginState == StatusCode.NO_ERROR) { - page = Network.getResponseData(Network.getRequest(uri)); - } else { - Log.e("Image upload: No login (error: " + loginState + ')'); - return StatusCode.NOT_LOGGED_IN; - } + final String page = Login.getRequestLogged(uri, new Parameters("LID=", logId)); + if (StringUtils.isBlank(page)) { + Log.e("GCParser.uploadLogImage: No data from server"); + return new ImmutablePair<StatusCode, String>(StatusCode.UNKNOWN_ERROR, null); } final String[] viewstates = Login.getViewstates(page); @@ -1128,18 +1143,23 @@ public abstract class GCParser { final File image = new File(imageUri.getPath()); final String response = Network.getResponseData(Network.postRequest(uri, uploadParams, "ctl00$ContentBody$ImageUploadControl1$uxFileUpload", "image/jpeg", image)); - MatcherWrapper matcherOK = new MatcherWrapper(GCConstants.PATTERN_OK_IMAGEUPLOAD, response); + final MatcherWrapper matcherUrl = new MatcherWrapper(GCConstants.PATTERN_IMAGE_UPLOAD_URL, response); - if (matcherOK.find()) { + if (matcherUrl.find()) { Log.i("Logimage successfully uploaded."); - - return StatusCode.NO_ERROR; + final String uploadedImageUrl = matcherUrl.group(1); + return ImmutablePair.of(StatusCode.NO_ERROR, uploadedImageUrl); } Log.e("GCParser.uploadLogIMage: Failed to upload image because of unknown error"); - return StatusCode.LOGIMAGE_POST_ERROR; + return ImmutablePair.of(StatusCode.LOGIMAGE_POST_ERROR, null); } + /** + * Post a log to GC.com. + * + * @return status code of the upload and ID of the log + */ public static StatusCode postLogTrackable(final String tbid, final String trackingCode, final String[] viewstates, final LogType logType, final int year, final int month, final int day, final String log) { if (Login.isEmpty(viewstates)) { @@ -1187,7 +1207,7 @@ public abstract class GCParser { final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/track/log.aspx").encodedQuery("wid=" + tbid).build().toString(); final String page = Login.postRequestLogged(uri, params); if (!Login.getLoginStatus(page)) { - Log.e("GCParser.postLogTrackable: Can not log in geocaching"); + Log.e("GCParser.postLogTrackable: Cannot log in geocaching"); return StatusCode.NOT_LOGGED_IN; } @@ -1198,7 +1218,7 @@ public abstract class GCParser { Log.i("Log successfully posted to trackable #" + trackingCode); return StatusCode.NO_ERROR; } - } catch (Exception e) { + } catch (final Exception e) { Log.e("GCParser.postLogTrackable.check", e); } @@ -1215,14 +1235,14 @@ public abstract class GCParser { */ static boolean addToWatchlist(final Geocache cache) { final String uri = "http://www.geocaching.com/my/watchlist.aspx?w=" + cache.getCacheId(); - String page = Login.postRequestLogged(uri, null); + final String page = Login.postRequestLogged(uri, null); if (StringUtils.isBlank(page)) { Log.e("GCParser.addToWatchlist: No data from server"); return false; // error } - boolean guidOnPage = cache.isGuidContainedInPage(page); + final boolean guidOnPage = cache.isGuidContainedInPage(page); if (guidOnPage) { Log.i("GCParser.addToWatchlist: cache is on watchlist"); cache.setOnWatchlist(true); @@ -1256,7 +1276,7 @@ public abstract class GCParser { Login.transferViewstates(page, params); page = Network.getResponseData(Network.postRequest(uri, params)); - boolean guidOnPage = cache.isGuidContainedInPage(page); + final boolean guidOnPage = cache.isGuidContainedInPage(page); if (!guidOnPage) { Log.i("GCParser.removeFromWatchlist: cache removed from watchlist"); cache.setOnWatchlist(false); @@ -1294,14 +1314,14 @@ public abstract class GCParser { private static boolean changeFavorite(final Geocache cache, final boolean add) { final String page = requestHtmlPage(cache.getGeocode(), null, "n", "0"); - final String userToken = BaseUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, ""); + final String userToken = TextUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, ""); if (StringUtils.isEmpty(userToken)) { return false; } final String uri = "http://www.geocaching.com/datastore/favorites.svc/update?u=" + userToken + "&f=" + Boolean.toString(add); - HttpResponse response = Network.postRequest(uri, null); + final HttpResponse response = Network.postRequest(uri, null); if (response != null && response.getStatusLine().getStatusCode() == 200) { Log.i("GCParser.changeFavorite: cache added/removed to/from favorites"); @@ -1330,7 +1350,7 @@ public abstract class GCParser { * Parse a trackable HTML description into a Trackable object * * @param page - * the HTML page to parse, already processed through {@link BaseUtils#replaceWhitespace} + * the HTML page to parse, already processed through {@link TextUtils#replaceWhitespace} * @return the parsed trackable, or null if none could be parsed */ static Trackable parseTrackable(final String page, final String possibleTrackingcode) { @@ -1346,20 +1366,20 @@ public abstract class GCParser { final Trackable trackable = new Trackable(); // trackable geocode - trackable.setGeocode(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GEOCODE, true, trackable.getGeocode())); + trackable.setGeocode(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GEOCODE, true, StringUtils.upperCase(possibleTrackingcode))); // trackable id - trackable.setGuid(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GUID, true, trackable.getGuid())); + trackable.setGuid(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GUID, true, trackable.getGuid())); // trackable icon - trackable.setIconUrl(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_ICON, true, trackable.getIconUrl())); + trackable.setIconUrl(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_ICON, true, trackable.getIconUrl())); // trackable name - trackable.setName(Html.fromHtml(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_NAME, true, "")).toString()); + trackable.setName(Html.fromHtml(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_NAME, true, "")).toString()); // trackable type if (StringUtils.isNotBlank(trackable.getName())) { - trackable.setType(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_TYPE, true, trackable.getType())); + trackable.setType(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_TYPE, true, trackable.getType())); } // trackable owner name @@ -1369,13 +1389,13 @@ public abstract class GCParser { trackable.setOwnerGuid(matcherOwner.group(1)); trackable.setOwner(matcherOwner.group(2).trim()); } - } catch (Exception e) { + } catch (final Exception e) { // failed to parse trackable owner name Log.w("GCParser.parseTrackable: Failed to parse trackable owner name"); } // trackable origin - trackable.setOrigin(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_ORIGIN, true, trackable.getOrigin())); + trackable.setOrigin(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_ORIGIN, true, trackable.getOrigin())); // trackable spotted try { @@ -1393,39 +1413,44 @@ public abstract class GCParser { trackable.setSpottedType(Trackable.SPOTTED_USER); } - if (BaseUtils.matches(page, GCConstants.PATTERN_TRACKABLE_SPOTTEDUNKNOWN)) { + if (TextUtils.matches(page, GCConstants.PATTERN_TRACKABLE_SPOTTEDUNKNOWN)) { trackable.setSpottedType(Trackable.SPOTTED_UNKNOWN); } - if (BaseUtils.matches(page, GCConstants.PATTERN_TRACKABLE_SPOTTEDOWNER)) { + if (TextUtils.matches(page, GCConstants.PATTERN_TRACKABLE_SPOTTEDOWNER)) { trackable.setSpottedType(Trackable.SPOTTED_OWNER); } - } catch (Exception e) { + } catch (final Exception e) { // failed to parse trackable last known place Log.w("GCParser.parseTrackable: Failed to parse trackable last known place"); } // released date - can be missing on the page try { - String releaseString = BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_RELEASES, false, null); + final String releaseString = TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_RELEASES, false, null); if (releaseString != null) { trackable.setReleased(dateTbIn1.parse(releaseString)); if (trackable.getReleased() == null) { trackable.setReleased(dateTbIn2.parse(releaseString)); } } - } catch (ParseException e1) { + } catch (final ParseException e1) { trackable.setReleased(null); } // trackable distance - final String distance = BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_DISTANCE, false, null); + final String distance = TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_DISTANCE, false, null); if (null != distance) { - trackable.setDistance(DistanceParser.parseDistance(distance, Settings.isUseMetricUnits())); + try { + trackable.setDistance(DistanceParser.parseDistance(distance, + !Settings.isUseImperialUnits())); + } catch (final NumberFormatException e) { + Log.e("GCParser.parseTrackable: Failed to parse distance", e); + } } // trackable goal - trackable.setGoal(convertLinks(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GOAL, true, trackable.getGoal()))); + trackable.setGoal(convertLinks(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GOAL, true, trackable.getGoal()))); // trackable details & image try { @@ -1441,10 +1466,13 @@ public abstract class GCParser { trackable.setDetails(convertLinks(details)); } } - } catch (Exception e) { + } catch (final Exception 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 logs try { @@ -1462,7 +1490,7 @@ public abstract class GCParser { long date = 0; try { date = Login.parseGcCustomDate(matcherLogs.group(2)).getTime(); - } catch (ParseException e) { + } catch (final ParseException e) { } final LogEntry logDone = new LogEntry( @@ -1490,7 +1518,7 @@ public abstract class GCParser { trackable.getLogs().add(logDone); } - } catch (Exception e) { + } catch (final Exception e) { // failed to parse logs Log.w("GCParser.parseCache: Failed to parse cache logs", e); } @@ -1560,10 +1588,10 @@ public abstract class GCParser { } } else { // extract embedded JSON data from page - rawResponse = BaseUtils.getMatch(page, GCConstants.PATTERN_LOGBOOK, ""); + rawResponse = TextUtils.getMatch(page, GCConstants.PATTERN_LOGBOOK, ""); } - List<LogEntry> logs = new ArrayList<LogEntry>(); + final List<LogEntry> logs = new ArrayList<LogEntry>(); try { final JSONObject resp = new JSONObject(rawResponse); @@ -1584,7 +1612,7 @@ public abstract class GCParser { long date = 0; try { date = Login.parseGcCustomDate(entry.getString("Visited")).getTime(); - } catch (ParseException e) { + } catch (final ParseException e) { Log.e("GCParser.loadLogsFromDetails: failed to parse log date."); } @@ -1612,7 +1640,7 @@ public abstract class GCParser { logs.add(logDone); } - } catch (JSONException e) { + } catch (final JSONException e) { // failed to parse logs Log.w("GCParser.loadLogsFromDetails: Failed to parse cache logs", e); } @@ -1628,30 +1656,26 @@ public abstract class GCParser { final List<LogType> types = new ArrayList<LogType>(); final MatcherWrapper typeBoxMatcher = new MatcherWrapper(GCConstants.PATTERN_TYPEBOX, page); - String typesText = null; - if (typeBoxMatcher.find()) { - if (typeBoxMatcher.groupCount() > 0) { - typesText = typeBoxMatcher.group(1); - } - } - - if (typesText != null) { - + if (typeBoxMatcher.find() && typeBoxMatcher.groupCount() > 0) { + final String typesText = typeBoxMatcher.group(1); final MatcherWrapper typeMatcher = new MatcherWrapper(GCConstants.PATTERN_TYPE2, typesText); while (typeMatcher.find()) { if (typeMatcher.groupCount() > 1) { try { - int type = Integer.parseInt(typeMatcher.group(2)); + final int type = Integer.parseInt(typeMatcher.group(2)); if (type > 0) { types.add(LogType.getById(type)); } - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("Error parsing log types", e); } } } } + // we don't support this log type + types.remove(LogType.UPDATE_COORDINATES); + return types; } @@ -1690,7 +1714,7 @@ public abstract class GCParser { Log.i("Trackable in inventory (#" + entry.ctl + "/" + entry.id + "): " + entry.trackCode + " - " + entry.name); trackableLogs.add(entry); } - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("GCParser.parseTrackableLog", e); } } @@ -1719,10 +1743,10 @@ public abstract class GCParser { //cache.setLogs(loadLogsFromDetails(page, cache, false)); if (Settings.isFriendLogsWanted()) { CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_logs); - List<LogEntry> allLogs = cache.getLogs(); - List<LogEntry> friendLogs = loadLogsFromDetails(page, cache, true, false); + final List<LogEntry> allLogs = cache.getLogs(); + final List<LogEntry> friendLogs = loadLogsFromDetails(page, cache, true, false); if (friendLogs != null) { - for (LogEntry log : friendLogs) { + for (final LogEntry log : friendLogs) { if (allLogs.contains(log)) { allLogs.get(allLogs.indexOf(log)).friend = true; } else { @@ -1732,16 +1756,6 @@ public abstract class GCParser { } } - if (Settings.isElevationWanted()) { - if (CancellableHandler.isCancelled(handler)) { - return; - } - CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_elevation); - if (cache.getCoords() != null) { - cache.setElevation(cache.getCoords().getElevation()); - } - } - if (Settings.isRatingWanted()) { if (CancellableHandler.isCancelled(handler)) { return; @@ -1766,7 +1780,7 @@ public abstract class GCParser { public static boolean editModifiedCoordinates(Geocache cache, Geopoint wpt) { final String page = requestHtmlPage(cache.getGeocode(), null, "n", "0"); - final String userToken = BaseUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, ""); + final String userToken = TextUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, ""); if (StringUtils.isEmpty(userToken)) { return false; } @@ -1785,7 +1799,7 @@ public abstract class GCParser { final String uriSuffix = wpt != null ? "SetUserCoordinate" : "ResetUserCoordinate"; final String uriPrefix = "http://www.geocaching.com/seek/cache_details.aspx/"; - HttpResponse response = Network.postJsonRequest(uriPrefix + uriSuffix, jo); + final HttpResponse response = Network.postJsonRequest(uriPrefix + uriSuffix, jo); Log.i("Sending to " + uriPrefix + uriSuffix + " :" + jo.toString()); if (response != null && response.getStatusLine().getStatusCode() == 200) { @@ -1793,11 +1807,42 @@ public abstract class GCParser { return true; } - } catch (JSONException e) { + } catch (final JSONException e) { Log.e("Unknown exception with json wrap code", e); } Log.e("GCParser.deleteModifiedCoordinates - cannot delete modified coords"); return false; } + public static boolean uploadPersonalNote(Geocache cache) { + final String page = requestHtmlPage(cache.getGeocode(), null, "n", "0"); + final String userToken = TextUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, ""); + if (StringUtils.isEmpty(userToken)) { + return false; + } + + try { + final JSONObject jo = new JSONObject() + .put("dto", (new JSONObject() + .put("et", cache.getPersonalNote()) + .put("ut", userToken))); + + final String uriSuffix = "SetUserCacheNote"; + + final String uriPrefix = "http://www.geocaching.com/seek/cache_details.aspx/"; + final HttpResponse response = Network.postJsonRequest(uriPrefix + uriSuffix, jo); + Log.i("Sending to " + uriPrefix + uriSuffix + " :" + jo.toString()); + + if (response != null && response.getStatusLine().getStatusCode() == 200) { + Log.i("GCParser.uploadPersonalNote - uploaded to GC.com"); + return true; + } + + } catch (final JSONException e) { + Log.e("Unknown exception with json wrap code", e); + } + Log.e("GCParser.uploadPersonalNote - cannot upload personal note"); + return false; + } + } diff --git a/main/src/cgeo/geocaching/connector/gc/IconDecoder.java b/main/src/cgeo/geocaching/connector/gc/IconDecoder.java index d3a2959..ed44392 100644 --- a/main/src/cgeo/geocaching/connector/gc/IconDecoder.java +++ b/main/src/cgeo/geocaching/connector/gc/IconDecoder.java @@ -1,7 +1,7 @@ package cgeo.geocaching.connector.gc; import cgeo.geocaching.Geocache; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.enumerations.CacheType; import android.graphics.Bitmap; @@ -21,7 +21,7 @@ public abstract class IconDecoder { private static final int CT_MEGAEVENT = 7; private static final int CT_CITO = 8; private static final int CT_WEBCAM = 9; - private static final int CT_WHEREIGO = 10; + private static final int CT_WHERIGO = 10; private static final int CT_VIRTUAL = 11; private static final int CT_LETTERBOX = 12; @@ -116,7 +116,7 @@ public abstract class IconDecoder { case CT_WEBCAM: cache.setType(CacheType.WEBCAM); return true; - case CT_WHEREIGO: + case CT_WHERIGO: cache.setType(CacheType.WHERIGO); return true; case CT_VIRTUAL: @@ -395,12 +395,12 @@ public abstract class IconDecoder { if (g < 71) { return CT_MYSTERY; } - return r < 153 ? CT_WHEREIGO : CT_WEBCAM; + return r < 153 ? CT_WHERIGO : CT_WEBCAM; } if (b < 167) { return r < 157 ? CT_TRADITIONAL : CT_WEBCAM; } - return CT_WHEREIGO; + return CT_WHERIGO; } if (g < 199) { if (r < 142) { @@ -450,7 +450,7 @@ public abstract class IconDecoder { if (b < 252) { if (r < 243) { if (r < 225) { - return CT_WHEREIGO; + return CT_WHERIGO; } if (b < 232) { if (g < 228) { @@ -459,14 +459,14 @@ public abstract class IconDecoder { return r < 231 ? CT_VIRTUAL : CT_TRADITIONAL; } if (r < 236) { - return CT_WHEREIGO; + return CT_WHERIGO; } - return r < 240 ? CT_WEBCAM : CT_WHEREIGO; + return r < 240 ? CT_WEBCAM : CT_WHERIGO; } if (g < 247) { return r < 245 ? CT_WEBCAM : CT_FOUND; } - return CT_WHEREIGO; + return CT_WHERIGO; } return CT_LETTERBOX; } diff --git a/main/src/cgeo/geocaching/connector/gc/Login.java b/main/src/cgeo/geocaching/connector/gc/Login.java index 7351311..4b4c93f 100644 --- a/main/src/cgeo/geocaching/connector/gc/Login.java +++ b/main/src/cgeo/geocaching/connector/gc/Login.java @@ -1,16 +1,16 @@ package cgeo.geocaching.connector.gc; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.network.Cookies; import cgeo.geocaching.network.HtmlImage; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; -import cgeo.geocaching.utils.BaseUtils; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.MatcherWrapper; +import cgeo.geocaching.utils.TextUtils; import ch.boye.httpclientandroidlib.HttpResponse; @@ -52,9 +52,9 @@ public abstract class Login { "dd/MM/yyyy" }; - Map<String, SimpleDateFormat> map = new HashMap<String, SimpleDateFormat>(); + final Map<String, SimpleDateFormat> map = new HashMap<String, SimpleDateFormat>(); - for (String format : formats) { + for (final String format : formats) { map.put(format, new SimpleDateFormat(format, Locale.ENGLISH)); } @@ -66,7 +66,7 @@ public abstract class Login { } private static StatusCode login(boolean retry) { - final ImmutablePair<String, String> login = Settings.getLogin(); + final ImmutablePair<String, String> login = Settings.getGcLogin(); if (login == null || StringUtils.isEmpty(login.left) || StringUtils.isEmpty(login.right)) { Login.setActualStatus(cgeoapplication.getInstance().getString(R.string.err_login)); @@ -77,7 +77,7 @@ public abstract class Login { 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 && BaseUtils.matches(loginData, GCConstants.PATTERN_MAINTENANCE)) { + if (loginResponse != null && loginResponse.getStatusLine().getStatusCode() == 503 && TextUtils.matches(loginData, GCConstants.PATTERN_MAINTENANCE)) { return StatusCode.MAINTENANCE; } @@ -147,9 +147,9 @@ public abstract class Login { } public static StatusCode logout() { - HttpResponse logoutResponse = Network.getRequest("https://www.geocaching.com/login/default.aspx?RESET=Y&redir=http%3a%2f%2fwww.geocaching.com%2fdefault.aspx%3f"); - String logoutData = Network.getResponseData(logoutResponse); - if (logoutResponse != null && logoutResponse.getStatusLine().getStatusCode() == 503 && BaseUtils.matches(logoutData, GCConstants.PATTERN_MAINTENANCE)) { + final HttpResponse logoutResponse = Network.getRequest("https://www.geocaching.com/login/default.aspx?RESET=Y&redir=http%3a%2f%2fwww.geocaching.com%2fdefault.aspx%3f"); + final String logoutData = Network.getResponseData(logoutResponse); + if (logoutResponse != null && logoutResponse.getStatusLine().getStatusCode() == 503 && TextUtils.matches(logoutData, GCConstants.PATTERN_MAINTENANCE)) { return StatusCode.MAINTENANCE; } @@ -205,17 +205,17 @@ public abstract class Login { setActualStatus(cgeoapplication.getInstance().getString(R.string.init_login_popup_ok)); // on every page except login page - setActualLoginStatus(BaseUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME)); + setActualLoginStatus(TextUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME)); if (isActualLoginStatus()) { - setActualUserName(BaseUtils.getMatch(page, GCConstants.PATTERN_LOGIN_NAME, true, "???")); + setActualUserName(TextUtils.getMatch(page, GCConstants.PATTERN_LOGIN_NAME, true, "???")); int cachesCount = 0; try { - cachesCount = Integer.parseInt(BaseUtils.getMatch(page, GCConstants.PATTERN_CACHES_FOUND, true, "0").replaceAll("[,.]", "")); - } catch (NumberFormatException e) { + cachesCount = Integer.parseInt(TextUtils.getMatch(page, GCConstants.PATTERN_CACHES_FOUND, true, "0").replaceAll("[,.]", "")); + } catch (final NumberFormatException e) { Log.e("getLoginStatus: bad cache count", e); } setActualCachesFound(cachesCount); - Settings.setMemberStatus(BaseUtils.getMatch(page, GCConstants.PATTERN_MEMBER_STATUS, true, null)); + Settings.setMemberStatus(TextUtils.getMatch(page, GCConstants.PATTERN_MEMBER_STATUS, true, null)); if ( page.contains(GCConstants.MEMBER_STATUS_RENEW) ) { Settings.setMemberStatus(GCConstants.MEMBER_STATUS_PM); } @@ -223,7 +223,7 @@ public abstract class Login { } // login page - setActualLoginStatus(BaseUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME_LOGIN_PAGE)); + setActualLoginStatus(TextUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME_LOGIN_PAGE)); if (isActualLoginStatus()) { setActualUserName(Settings.getUsername()); // number of caches found is not part of this page @@ -260,23 +260,23 @@ public abstract class Login { public static BitmapDrawable downloadAvatarAndGetMemberStatus() { try { - final String profile = BaseUtils.replaceWhitespace(Network.getResponseData(Network.getRequest("http://www.geocaching.com/my/"))); + final String profile = TextUtils.replaceWhitespace(Network.getResponseData(Network.getRequest("http://www.geocaching.com/my/"))); - Settings.setMemberStatus(BaseUtils.getMatch(profile, GCConstants.PATTERN_MEMBER_STATUS, true, null)); + Settings.setMemberStatus(TextUtils.getMatch(profile, GCConstants.PATTERN_MEMBER_STATUS, true, null)); if (profile.contains(GCConstants.MEMBER_STATUS_RENEW)) { Settings.setMemberStatus(GCConstants.MEMBER_STATUS_PM); } - setActualCachesFound(Integer.parseInt(BaseUtils.getMatch(profile, GCConstants.PATTERN_CACHES_FOUND, true, "-1").replaceAll("[,.]", ""))); + setActualCachesFound(Integer.parseInt(TextUtils.getMatch(profile, GCConstants.PATTERN_CACHES_FOUND, true, "-1").replaceAll("[,.]", ""))); - final String avatarURL = BaseUtils.getMatch(profile, GCConstants.PATTERN_AVATAR_IMAGE_PROFILE_PAGE, false, null); + final String avatarURL = TextUtils.getMatch(profile, GCConstants.PATTERN_AVATAR_IMAGE_PROFILE_PAGE, false, null); if (null != avatarURL) { final HtmlImage imgGetter = new HtmlImage("", false, 0, false); return imgGetter.getDrawable(avatarURL); } // No match? There may be no avatar set by user. Log.d("No avatar set for user"); - } catch (Exception e) { + } catch (final Exception e) { Log.w("Error when retrieving user avatar", e); } return null; @@ -294,7 +294,7 @@ public abstract class Login { return; } - String customDate = BaseUtils.getMatch(result, GCConstants.PATTERN_CUSTOMDATE, true, null); + final String customDate = TextUtils.getMatch(result, GCConstants.PATTERN_CUSTOMDATE, true, null); if (null != customDate) { Settings.setGcCustomDate(customDate); } @@ -310,14 +310,14 @@ public abstract class Login { if (gcCustomDateFormats.containsKey(format)) { try { return gcCustomDateFormats.get(format).parse(trimmed); - } catch (ParseException e) { + } catch (final ParseException e) { } } - for (SimpleDateFormat sdf : gcCustomDateFormats.values()) { + for (final SimpleDateFormat sdf : gcCustomDateFormats.values()) { try { return sdf.parse(trimmed); - } catch (ParseException e) { + } catch (final ParseException e) { } } @@ -347,7 +347,7 @@ public abstract class Login { return true; } - for (String s : a) { + for (final String s : a) { if (StringUtils.isNotEmpty(s)) { return false; } @@ -373,24 +373,24 @@ public abstract class Login { if (matcherViewstateCount.find()) { try { count = Integer.parseInt(matcherViewstateCount.group(1)); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("getViewStates", e); } } - String[] viewstates = new String[count]; + final String[] viewstates = new String[count]; // Get the viewstates final MatcherWrapper matcherViewstates = new MatcherWrapper(GCConstants.PATTERN_VIEWSTATES, page); while (matcherViewstates.find()) { - String sno = matcherViewstates.group(1); // number of viewstate + final String sno = matcherViewstates.group(1); // number of viewstate int no; if (StringUtils.isEmpty(sno)) { no = 0; } else { try { no = Integer.parseInt(sno); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("getViewStates", e); no = 0; } @@ -436,17 +436,17 @@ public abstract class Login { * @return */ public static String postRequestLogged(final String uri, final Parameters params) { - HttpResponse response = Network.postRequest(uri, params); - String data = Network.getResponseData(response); + final String data = Network.getResponseData(Network.postRequest(uri, params)); - if (!getLoginStatus(data)) { - if (login() == StatusCode.NO_ERROR) { - response = Network.postRequest(uri, params); - data = Network.getResponseData(response); - } else { - Log.i("Working as guest."); - } + if (getLoginStatus(data)) { + return data; + } + + if (login() == StatusCode.NO_ERROR) { + return Network.getResponseData(Network.postRequest(uri, params)); } + + Log.i("Working as guest."); return data; } @@ -458,26 +458,37 @@ public abstract class Login { * @return */ public static String getRequestLogged(final String uri, final Parameters params) { - final String data = Network.getResponseData(Network.getRequest(uri, params)); + final String data = Network.getResponseData(Network.getRequest(uri, params), canRemoveWhitespace(uri)); if (getLoginStatus(data)) { return data; } if (login() == StatusCode.NO_ERROR) { - return Network.getResponseData(Network.getRequest(uri, params)); + return Network.getResponseData(Network.getRequest(uri, params), canRemoveWhitespace(uri)); } Log.w("Working as guest."); return data; } + /** + * Unfortunately the cache details page contains user generated whitespace in the personal note, therefore we cannot + * remove the white space from cache details pages. + * + * @param uri + * @return + */ + private static boolean canRemoveWhitespace(final String uri) { + return !StringUtils.contains(uri, "cache_details"); + } + /** Get user session & session token from the Live Map. Needed for following requests */ public static String[] getMapTokens() { final HttpResponse response = Network.getRequest(GCConstants.URL_LIVE_MAP); final String data = Network.getResponseData(response); - final String userSession = BaseUtils.getMatch(data, GCConstants.PATTERN_USERSESSION, ""); - final String sessionToken = BaseUtils.getMatch(data, GCConstants.PATTERN_SESSIONTOKEN, ""); + final String userSession = TextUtils.getMatch(data, GCConstants.PATTERN_USERSESSION, ""); + final String sessionToken = TextUtils.getMatch(data, GCConstants.PATTERN_SESSIONTOKEN, ""); return new String[] { userSession, sessionToken }; } } diff --git a/main/src/cgeo/geocaching/connector/gc/SearchHandler.java b/main/src/cgeo/geocaching/connector/gc/SearchHandler.java index 840cad1..45832e4 100644 --- a/main/src/cgeo/geocaching/connector/gc/SearchHandler.java +++ b/main/src/cgeo/geocaching/connector/gc/SearchHandler.java @@ -102,7 +102,7 @@ public class SearchHandler extends Handler { imgHandler.sendEmptyMessage(0); } catch (IOException e) { - Log.e("Failed to download reCAPTCHA image"); + Log.e("Failed to download reCAPTCHA image", e); } } } diff --git a/main/src/cgeo/geocaching/connector/gc/Tile.java b/main/src/cgeo/geocaching/connector/gc/Tile.java index 0e5ffe7..ec90036 100644 --- a/main/src/cgeo/geocaching/connector/gc/Tile.java +++ b/main/src/cgeo/geocaching/connector/gc/Tile.java @@ -37,6 +37,7 @@ public class Tile { public static final int TILE_SIZE = 256; public static final int ZOOMLEVEL_MAX = 18; public static final int ZOOMLEVEL_MIN = 0; + public static final int ZOOMLEVEL_MIN_PERSONALIZED = 12; static final int[] NUMBER_OF_TILES = new int[ZOOMLEVEL_MAX - ZOOMLEVEL_MIN + 1]; static final int[] NUMBER_OF_PIXELS = new int[ZOOMLEVEL_MAX - ZOOMLEVEL_MIN + 1]; @@ -243,7 +244,7 @@ public class Tile { try { return response != null ? BitmapFactory.decodeStream(response.getEntity().getContent()) : null; } catch (IOException e) { - Log.e("cgBase.requestMapTile() " + e.getMessage()); + Log.e("Tile.requestMapTile() ", e); } return null; } @@ -252,6 +253,10 @@ public class Tile { return viewPort.contains(point); } + public Viewport getViewport() { + return viewPort; + } + /** * Calculate needed tiles for the given viewport to cover it with * max 2x2 tiles |
