diff options
author | Bananeweizen <bananeweizen@gmx.de> | 2012-05-05 10:31:30 +0200 |
---|---|---|
committer | Bananeweizen <bananeweizen@gmx.de> | 2012-05-05 10:31:30 +0200 |
commit | 057f24b4f57dadeeba92a04c83e3bcd790e983b1 (patch) | |
tree | 23464d3fd6a16c6ede3206bc39ed4b2e16d0419c /main/src/cgeo/geocaching/connector | |
parent | 84c6bd6cc358b2cde2926a62990e4c151c47d6d1 (diff) | |
download | cgeo-057f24b4f57dadeeba92a04c83e3bcd790e983b1.zip cgeo-057f24b4f57dadeeba92a04c83e3bcd790e983b1.tar.gz cgeo-057f24b4f57dadeeba92a04c83e3bcd790e983b1.tar.bz2 |
refactoring: more cleanup in connector interfaces
* switch from method overrides to interface detection for connector
features
* remove GCBase and put code into other classes
* reduce GC specific code being used in general activities
Diffstat (limited to 'main/src/cgeo/geocaching/connector')
17 files changed, 558 insertions, 401 deletions
diff --git a/main/src/cgeo/geocaching/connector/AbstractConnector.java b/main/src/cgeo/geocaching/connector/AbstractConnector.java index 248ca23..f1d00fa 100644 --- a/main/src/cgeo/geocaching/connector/AbstractConnector.java +++ b/main/src/cgeo/geocaching/connector/AbstractConnector.java @@ -2,12 +2,7 @@ package cgeo.geocaching.connector; import cgeo.geocaching.SearchResult; import cgeo.geocaching.cgCache; -import cgeo.geocaching.cgeoapplication; -import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; -import cgeo.geocaching.utils.CancellableHandler; - -import java.util.Set; public abstract class AbstractConnector implements IConnector { @@ -17,11 +12,6 @@ public abstract class AbstractConnector implements IConnector { } @Override - public boolean supportsRefreshCache(cgCache cache) { - return false; - } - - @Override public boolean supportsWatchList() { return false; } @@ -41,26 +31,6 @@ public abstract class AbstractConnector implements IConnector { return false; } - @Override - public boolean supportsCachesAround() { - return false; - } - - @Override - public SearchResult searchByCoordinate(Geopoint center) { - return null; - } - - @Override - public SearchResult searchByGeocode(String geocode, String guid, cgeoapplication app, CancellableHandler handler) { - return null; - } - - @Override - public SearchResult searchByGeocodes(Set<String> geocodes) { - return null; - } - public SearchResult searchByViewport(Viewport viewport, String tokens[]) { return null; } diff --git a/main/src/cgeo/geocaching/connector/ConnectorFactory.java b/main/src/cgeo/geocaching/connector/ConnectorFactory.java index fc8ef45..784a54e 100644 --- a/main/src/cgeo/geocaching/connector/ConnectorFactory.java +++ b/main/src/cgeo/geocaching/connector/ConnectorFactory.java @@ -6,13 +6,10 @@ import cgeo.geocaching.connector.gc.GCConnector; import cgeo.geocaching.connector.oc.OCApiConnector; import cgeo.geocaching.connector.oc.OCConnector; import cgeo.geocaching.connector.ox.OXConnector; -import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; import org.apache.commons.lang3.StringUtils; -import java.util.Set; - public final class ConnectorFactory { private static final UnknownConnector UNKNOWN_CONNECTOR = new UnknownConnector(); private static final IConnector[] connectors = new IConnector[] { @@ -70,13 +67,6 @@ public final class ConnectorFactory { return StringUtils.isBlank(geocode) || !Character.isLetterOrDigit(geocode.charAt(0)); } - /** @see IConnector#searchByCoordinate */ - public static SearchResult searchByCoordinate(final Geopoint center) { - // We have only connector capable of doing a 'searchByCoordinate()' - // If there is a second connector the information has to be collected from all collectors - return GCConnector.getInstance().searchByCoordinate(center); - } - /** @see IConnector#searchByViewport */ public static SearchResult searchByViewport(final Viewport viewport, final String[] tokens) { // We have only connector capable of doing a 'searchByViewport()' @@ -84,11 +74,4 @@ public final class ConnectorFactory { return GCConnector.getInstance().searchByViewport(viewport, tokens); } - /** @see IConnector#searchByGeocodes */ - public static SearchResult searchByGeocodes(final Set<String> geocodes) { - // We have only connector capable of doing a 'searchByViewport()' - // If there is a second connector the information has to be collected from all collectors - return GCConnector.getInstance().searchByGeocodes(geocodes); - } - } diff --git a/main/src/cgeo/geocaching/connector/IConnector.java b/main/src/cgeo/geocaching/connector/IConnector.java index 28c21c8..4bc63fc 100644 --- a/main/src/cgeo/geocaching/connector/IConnector.java +++ b/main/src/cgeo/geocaching/connector/IConnector.java @@ -2,12 +2,7 @@ package cgeo.geocaching.connector; import cgeo.geocaching.SearchResult; import cgeo.geocaching.cgCache; -import cgeo.geocaching.cgeoapplication; -import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; -import cgeo.geocaching.utils.CancellableHandler; - -import java.util.Set; public interface IConnector { /** @@ -25,8 +20,6 @@ public interface IConnector { */ public boolean canHandle(final String geocode); - public boolean supportsRefreshCache(final cgCache cache); - /** * get browser URL for the given cache * @@ -72,25 +65,6 @@ public interface IConnector { public boolean supportsUserActions(); /** - * enable/disable "caches around" action in cache details - * - * @return - */ - public boolean supportsCachesAround(); - - public SearchResult searchByGeocode(final String geocode, final String guid, final cgeoapplication app, final CancellableHandler handler); - - public SearchResult searchByGeocodes(final Set<String> geocodes); - - /** - * search caches by coordinate. must be implemented if {@link supportsCachesAround} returns <code>true</true> - * - * @param center - * @return - */ - public SearchResult searchByCoordinate(final Geopoint center); - - /** * Search caches by viewport. * * @param viewport diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java b/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java new file mode 100644 index 0000000..62645c2 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java @@ -0,0 +1,13 @@ +package cgeo.geocaching.connector.capability; + +import cgeo.geocaching.SearchResult; +import cgeo.geocaching.geopoint.Geopoint; + +/** + * connector capability for online searching caches around a center coordinate, sorted by distance + * + */ +public interface ISearchByCenter { + public SearchResult searchByCenter(final Geopoint center); + +} diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByGeocode.java b/main/src/cgeo/geocaching/connector/capability/ISearchByGeocode.java new file mode 100644 index 0000000..c3d6bba --- /dev/null +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByGeocode.java @@ -0,0 +1,12 @@ +package cgeo.geocaching.connector.capability; + +import cgeo.geocaching.SearchResult; +import cgeo.geocaching.utils.CancellableHandler; + +/** + * connector capability of searching online for a cache by geocode + * + */ +public interface ISearchByGeocode { + public SearchResult searchByGeocode(final String geocode, final String guid, final CancellableHandler handler); +} diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java b/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java new file mode 100644 index 0000000..316cf00 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java @@ -0,0 +1,8 @@ +package cgeo.geocaching.connector.capability; + +import cgeo.geocaching.SearchResult; +import cgeo.geocaching.geopoint.Viewport; + +public interface ISearchByViewPort { + public SearchResult searchByViewport(final Viewport viewport, final String[] tokens); +} diff --git a/main/src/cgeo/geocaching/connector/gc/AbstractSearchThread.java b/main/src/cgeo/geocaching/connector/gc/AbstractSearchThread.java new file mode 100644 index 0000000..33c8a92 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/gc/AbstractSearchThread.java @@ -0,0 +1,68 @@ +package cgeo.geocaching.connector.gc; + +import cgeo.geocaching.utils.Log; + +import android.os.Handler; +import android.os.Message; + +abstract public class AbstractSearchThread extends Thread { + private Handler recaptchaHandler = null; + private String recaptchaChallenge = null; + private String recaptchaText = null; + private final Handler handler; + private static AbstractSearchThread currentInstance; + + public AbstractSearchThread(final Handler handler) { + this.handler = handler; + } + + public void setRecaptchaHandler(Handler recaptchaHandlerIn) { + recaptchaHandler = recaptchaHandlerIn; + } + + public void notifyNeed() { + if (recaptchaHandler != null) { + recaptchaHandler.sendEmptyMessage(1); + } + } + + public synchronized void waitForUser() { + try { + wait(); + } catch (InterruptedException e) { + Log.w("searchThread is not waiting for user..."); + } + } + + public void setChallenge(String challenge) { + recaptchaChallenge = challenge; + } + + public String getChallenge() { + return recaptchaChallenge; + } + + public synchronized void setText(String text) { + recaptchaText = text; + + notify(); + } + + public synchronized String getText() { + return recaptchaText; + } + + @Override + final public void run() { + super.run(); + currentInstance = this; + runSearch(); + handler.sendMessage(Message.obtain()); + } + + protected abstract void runSearch(); + + public static AbstractSearchThread getCurrentInstance() { + return currentInstance; + } +} diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java index b461cc2..fbda5cd 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java @@ -6,7 +6,10 @@ import cgeo.geocaching.Settings; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.connector.AbstractConnector; +import cgeo.geocaching.connector.capability.ISearchByCenter; +import cgeo.geocaching.connector.capability.ISearchByGeocode; import cgeo.geocaching.enumerations.StatusCode; +import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.utils.CancellableHandler; @@ -15,10 +18,9 @@ import cgeo.geocaching.utils.Log; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import java.util.Set; import java.util.regex.Pattern; -public class GCConnector extends AbstractConnector { +public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter { private static GCConnector instance; private static final Pattern gpxZipFilePattern = Pattern.compile("\\d{7,}(_.+)?\\.zip", Pattern.CASE_INSENSITIVE); @@ -43,11 +45,6 @@ public class GCConnector extends AbstractConnector { } @Override - public boolean supportsRefreshCache(cgCache cache) { - return true; - } - - @Override public String getCacheUrl(cgCache cache) { // it would also be possible to use "http://www.geocaching.com/seek/cache_details.aspx?wp=" + cache.getGeocode(); return "http://coord.info/" + cache.getGeocode(); @@ -79,18 +76,7 @@ public class GCConnector extends AbstractConnector { } @Override - public boolean supportsCachesAround() { - return true; - } - - @Override - public SearchResult searchByGeocode(final String geocode, final String guid, final cgeoapplication app, final CancellableHandler handler) { - - if (app == null) { - Log.e("cgeoBase.searchByGeocode: No application found"); - return null; - } - + public SearchResult searchByGeocode(final String geocode, final String guid, final CancellableHandler handler) { final Parameters params = new Parameters("decrypt", "y"); if (StringUtils.isNotBlank(geocode)) { params.put("wp", geocode); @@ -106,11 +92,11 @@ public class GCConnector extends AbstractConnector { if (StringUtils.isEmpty(page)) { final SearchResult search = new SearchResult(); - if (app.isThere(geocode, guid, true, false)) { + if (cgeoapplication.getInstance().isThere(geocode, guid, true, false)) { if (StringUtils.isBlank(geocode) && StringUtils.isNotBlank(guid)) { Log.i("Loading old cache from cache."); - search.addGeocode(app.getGeocode(guid)); + search.addGeocode(cgeoapplication.getInstance().getGeocode(guid)); } else { search.addGeocode(geocode); } @@ -134,13 +120,8 @@ public class GCConnector extends AbstractConnector { } @Override - public SearchResult searchByGeocodes(Set<String> geocodes) { - return GCBase.searchByGeocodes(geocodes); - } - - @Override public SearchResult searchByViewport(Viewport viewport, String[] tokens) { - return GCBase.searchByViewport(viewport, tokens); + return GCMap.searchByViewport(viewport, tokens); } @Override @@ -153,8 +134,18 @@ public class GCConnector extends AbstractConnector { return cacheHasReliableLatLon; } + public static int addToWatchlist(cgCache cache) { + return GCParser.addToWatchlist(cache); + } + + public static int removeFromWatchlist(cgCache cache) { + return GCParser.removeFromWatchlist(cache); + } + @Override - public String[] getTokens() { - return GCBase.getTokens(); + 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; } + } diff --git a/main/src/cgeo/geocaching/connector/gc/GCConstants.java b/main/src/cgeo/geocaching/connector/gc/GCConstants.java index 083626d..21dde03 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConstants.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConstants.java @@ -158,7 +158,37 @@ public final class GCConstants { /** Number of logs to retrieve from GC.com */ public final static int NUMBER_OF_LOGS = 35; + private final static String SEQUENCE_GCID = "0123456789ABCDEFGHJKMNPQRTVWXYZ"; + private final static long GC_BASE31 = 31; + private final static long GC_BASE16 = 16; + + /** + * Convert GCCode (geocode) to (old) GCIds + * + * Based on http://www.geoclub.de/viewtopic.php?f=111&t=54859&start=40 + * see http://support.groundspeak.com/index.php?pg=kb.printer.friendly&id=1#p221 + */ + public static long gccodeToGCId(final String gccode) { + long gcid = 0; + long base = GC_BASE31; + String geocodeWO = gccode.substring(2).toUpperCase(); + + if ((geocodeWO.length() < 4) || (geocodeWO.length() == 4 && SEQUENCE_GCID.indexOf(geocodeWO.charAt(0)) < 16)) { + base = GC_BASE16; + } + + for (int p = 0; p < geocodeWO.length(); p++) { + gcid = base * gcid + SEQUENCE_GCID.indexOf(geocodeWO.charAt(p)); + } + + if (base == GC_BASE31) { + gcid += Math.pow(16, 4) - 16 * Math.pow(31, 3); + } + return gcid; + } + private GCConstants() { // this class shall not have instances } + } diff --git a/main/src/cgeo/geocaching/connector/gc/GCBase.java b/main/src/cgeo/geocaching/connector/gc/GCMap.java index 64e5875..c7cb40f 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCBase.java +++ b/main/src/cgeo/geocaching/connector/gc/GCMap.java @@ -1,6 +1,5 @@ package cgeo.geocaching.connector.gc; -import cgeo.geocaching.ICoordinates; import cgeo.geocaching.SearchResult; import cgeo.geocaching.Settings; import cgeo.geocaching.cgCache; @@ -13,16 +12,13 @@ import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.IConversion; import cgeo.geocaching.geopoint.Viewport; -import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.ui.Formatter; -import cgeo.geocaching.utils.BaseUtils; import cgeo.geocaching.utils.LeastRecentlyUsedMap; import cgeo.geocaching.utils.Log; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.http.HttpResponse; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -31,172 +27,80 @@ import android.graphics.Bitmap; import java.text.ParseException; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -/** - * GC.com/Groundspeak (GS) specific stuff - * - * @author blafoo - * - */ -public class GCBase { - - protected final static String SEQUENCE_GCID = "0123456789ABCDEFGHJKMNPQRTVWXYZ"; - protected final static long GC_BASE31 = 31; - protected final static long GC_BASE16 = 16; - - private final static LeastRecentlyUsedMap<Integer, Tile> tileCache = new LeastRecentlyUsedMap.LruCache<Integer, Tile>(64); +public class GCMap { private static Viewport lastSearchViewport = null; - /** - * Searches the view port on the live map with Strategy.AUTO - * - * @param viewport - * Area to search - * @param tokens - * Live map tokens - * @return - */ - public static SearchResult searchByViewport(final Viewport viewport, final String[] tokens) { - Strategy strategy = Settings.getLiveMapStrategy(); - if (strategy == Strategy.AUTO) { - float speedNow = cgeoapplication.getInstance().currentGeo().getSpeed(); - strategy = speedNow >= 8 ? Strategy.FAST : Strategy.DETAILED; // 8 m/s = 30 km/h - } - // return searchByViewport(viewport, tokens, strategy); - - // testing purpose - { - SearchResult result = searchByViewport(viewport, tokens, strategy); - String text = Formatter.SEPARATOR + strategy.getL10n() + Formatter.SEPARATOR; - int speed = (int) cgeoapplication.getInstance().currentGeo().getSpeed(); - if (Settings.isUseMetricUnits()) { - text += speed + " km/h"; - } else { - text += speed / IConversion.MILES_TO_KILOMETER + " mph"; - } - result.setUrl(result.getUrl() + text); - return result; - } - } - - public static void removeFromTileCache(final ICoordinates point) { - if (point != null) { - Collection<Tile> tiles = new ArrayList<Tile>(tileCache.values()); - for (Tile tile : tiles) { - if (tile.containsPoint(point)) { - tileCache.remove(tile.hashCode()); - } - } - } - } - - /** - * Searches the view port on the live map for caches. - * The strategy dictates if only live map information is used or if an additional - * searchByCoordinates query is issued. - * - * @param viewport - * Area to search - * @param tokens - * Live map tokens - * @param strategy - * Strategy for data retrieval and parsing, @see Strategy - * @return - */ - private static SearchResult searchByViewport(final Viewport viewport, final String[] tokens, Strategy strategy) { - Log.d("GCBase.searchByViewport" + viewport.toString()); - - String referer = GCConstants.URL_LIVE_MAP; - - final SearchResult searchResult = new SearchResult(); - searchResult.setUrl(referer + "?ll=" + viewport.getCenter().getLatitude() + "," + viewport.getCenter().getLongitude()); + public static SearchResult searchByGeocodes(Set<String> geocodes) { + final SearchResult result = new SearchResult(); - if (strategy.flags.contains(StrategyFlag.LOAD_TILES)) { - final Set<Tile> tiles = getTilesForViewport(viewport); + final String geocodeList = StringUtils.join(geocodes.toArray(), "|"); + final String referer = GCConstants.URL_LIVE_MAP_DETAILS; - for (Tile tile : tiles) { + try { + final Parameters params = new Parameters("i", geocodeList, "_", String.valueOf(System.currentTimeMillis())); + final String data = StringUtils.defaultString(Tile.requestMapInfo(referer, params, referer)); - if (!tileCache.containsKey(tile.hashCode())) { - final Parameters params = new Parameters( - "x", String.valueOf(tile.getX()), - "y", String.valueOf(tile.getY()), - "z", String.valueOf(tile.getZoomlevel()), - "ep", "1"); - if (tokens != null) { - params.put("k", tokens[0], "st", tokens[1]); - } - if (Settings.isExcludeMyCaches()) { - params.put("hf", "1", "hh", "1"); // hide found, hide hidden - } - if (Settings.getCacheType() == CacheType.TRADITIONAL) { - params.put("ect", "9,5,3,6,453,13,1304,137,11,4,8,1858"); // 2 = tradi 3 = multi 8 = mystery - } else if (Settings.getCacheType() == CacheType.MULTI) { - params.put("ect", "9,5,2,6,453,13,1304,137,11,4,8,1858"); - } else if (Settings.getCacheType() == CacheType.MYSTERY) { - params.put("ect", "9,5,3,6,453,13,1304,137,11,4,2,1858"); - } - if (tile.getZoomlevel() != 14) { - params.put("_", String.valueOf(System.currentTimeMillis())); - } - // TODO: other types t.b.d + // Example JSON information + // {"status":"success", + // "data":[{"name":"Mission: Impossible","gc":"GC1234","g":"34c2e609-5246-4f91-9029-d6c02b0f2a82","available":true,"archived":false,"subrOnly":false,"li":false,"fp":"5","difficulty":{"text":3.5,"value":"3_5"},"terrain":{"text":1.0,"value":"1"},"hidden":"7/23/2001","container":{"text":"Regular","value":"regular.gif"},"type":{"text":"Unknown Cache","value":8},"owner":{"text":"Ca$h_Cacher","value":"2db18e69-6877-402a-848d-6362621424f6"}}, + // {"name":"HP: Hannover - Sahlkamp","gc":"GC2Q97X","g":"a09149ca-00e0-4aa2-b332-db2b4dfb18d2","available":true,"archived":false,"subrOnly":false,"li":false,"fp":"0","difficulty":{"text":1.0,"value":"1"},"terrain":{"text":1.5,"value":"1_5"},"hidden":"5/29/2011","container":{"text":"Small","value":"small.gif"},"type":{"text":"Traditional Cache","value":2},"owner":{"text":"GeoM@n","value":"1deaa69e-6bcc-421d-95a1-7d32b468cb82"}}] + // } - // The PNG must be requested first, otherwise the following request would always return with 204 - No Content - Bitmap bitmap = Tile.requestMapTile(GCConstants.URL_MAP_TILE, params, referer); + final JSONObject json = new JSONObject(data); + final String status = json.getString("status"); + if (StringUtils.isBlank(status)) { - // Check bitmap size - if (bitmap != null && (bitmap.getWidth() != Tile.TILE_SIZE || - bitmap.getHeight() != Tile.TILE_SIZE)) { - bitmap.recycle(); - bitmap = null; - } + throw new JSONException("No status inside JSON"); + } + if ("success".compareTo(status) != 0) { + throw new JSONException("Wrong status inside JSON"); + } + final JSONArray dataArray = json.getJSONArray("data"); + if (dataArray == null) { + throw new JSONException("No data inside JSON"); + } - String data = Tile.requestMapInfo(GCConstants.URL_MAP_INFO, params, referer); - if (StringUtils.isEmpty(data)) { - Log.e("GCBase.searchByViewport: No data from server for tile (" + tile.getX() + "/" + tile.getY() + ")"); - } else { - final SearchResult search = parseMapJSON(data, tile, bitmap, strategy); - if (search == null || CollectionUtils.isEmpty(search.getGeocodes())) { - Log.e("GCBase.searchByViewport: No cache parsed for viewport " + viewport); - } - else { - searchResult.addGeocodes(search.getGeocodes()); - } - tileCache.put(tile.hashCode(), tile); - } + for (int j = 0; j < dataArray.length(); j++) { + final cgCache cache = new cgCache(); - // release native bitmap memory - if (bitmap != null) { - bitmap.recycle(); - } + JSONObject dataObject = dataArray.getJSONObject(j); + cache.setName(dataObject.getString("name")); + cache.setGeocode(dataObject.getString("gc")); + cache.setGuid(dataObject.getString("g")); // 34c2e609-5246-4f91-9029-d6c02b0f2a82" + cache.setDisabled(!dataObject.getBoolean("available")); + cache.setArchived(dataObject.getBoolean("archived")); + cache.setPremiumMembersOnly(dataObject.getBoolean("subrOnly")); + // "li" seems to be "false" always + cache.setFavoritePoints(Integer.parseInt(dataObject.getString("fp"))); + JSONObject difficultyObj = dataObject.getJSONObject("difficulty"); + cache.setDifficulty(Float.parseFloat(difficultyObj.getString("text"))); // 3.5 + JSONObject terrainObj = dataObject.getJSONObject("terrain"); + cache.setTerrain(Float.parseFloat(terrainObj.getString("text"))); // 1.5 + cache.setHidden(Login.parseGcCustomDate(dataObject.getString("hidden"), "MM/dd/yyyy")); // 7/23/2001 + JSONObject containerObj = dataObject.getJSONObject("container"); + cache.setSize(CacheSize.getById(containerObj.getString("text"))); // Regular + JSONObject typeObj = dataObject.getJSONObject("type"); + cache.setType(CacheType.getByPattern(typeObj.getString("text"))); // Traditional Cache + JSONObject ownerObj = dataObject.getJSONObject("owner"); + cache.setOwner(ownerObj.getString("text")); - } - } - } + result.addCache(cache); - if (strategy.flags.contains(StrategyFlag.SEARCH_NEARBY)) { - final Geopoint center = viewport.getCenter(); - if ((lastSearchViewport == null) || !lastSearchViewport.contains(center)) { - SearchResult search = GCParser.searchByCoords(null, center, Settings.getCacheType(), false); - if (search != null && !search.isEmpty()) { - final Set<String> geocodes = search.getGeocodes(); - if (Settings.isPremiumMember()) { - lastSearchViewport = cgeoapplication.getInstance().getBounds(geocodes); - } else { - lastSearchViewport = new Viewport(center, 0.01, 0.01); - } - searchResult.addGeocodes(geocodes); - } } + } catch (JSONException e) { + result.setError(StatusCode.UNKNOWN_ERROR); + } catch (ParseException e) { + result.setError(StatusCode.UNKNOWN_ERROR); + } catch (NumberFormatException e) { + result.setError(StatusCode.UNKNOWN_ERROR); } - - return searchResult; + return result; } /** @@ -313,125 +217,135 @@ public class GCBase { return searchResult; } - /** - * Calculate needed tiles for the given viewport - * + * Searches the view port on the live map with Strategy.AUTO + * * @param viewport + * Area to search + * @param tokens + * Live map tokens * @return */ - protected static Set<Tile> getTilesForViewport(final Viewport viewport) { - Set<Tile> tiles = new HashSet<Tile>(); - int zoom = Math.min(Tile.calcZoomLon(viewport.bottomLeft, viewport.topRight), - Tile.calcZoomLat(viewport.bottomLeft, viewport.topRight)); - tiles.add(new Tile(viewport.bottomLeft, zoom)); - tiles.add(new Tile(new Geopoint(viewport.getLatitudeMin(), viewport.getLongitudeMax()), zoom)); - tiles.add(new Tile(new Geopoint(viewport.getLatitudeMax(), viewport.getLongitudeMin()), zoom)); - tiles.add(new Tile(viewport.topRight, zoom)); - return tiles; - } - - /** - * Convert GCCode (geocode) to (old) GCIds - * - * Based on http://www.geoclub.de/viewtopic.php?f=111&t=54859&start=40 - * see http://support.groundspeak.com/index.php?pg=kb.printer.friendly&id=1#p221 - */ - public static long gccodeToGCId(final String gccode) { - long gcid = 0; - long base = GC_BASE31; - String geocodeWO = gccode.substring(2).toUpperCase(); - - if ((geocodeWO.length() < 4) || (geocodeWO.length() == 4 && SEQUENCE_GCID.indexOf(geocodeWO.charAt(0)) < 16)) { - base = GC_BASE16; - } - - for (int p = 0; p < geocodeWO.length(); p++) { - gcid = base * gcid + SEQUENCE_GCID.indexOf(geocodeWO.charAt(p)); + public static SearchResult searchByViewport(final Viewport viewport, final String[] tokens) { + Strategy strategy = Settings.getLiveMapStrategy(); + if (strategy == Strategy.AUTO) { + float speedNow = cgeoapplication.getInstance().currentGeo().getSpeed(); + strategy = speedNow >= 8 ? Strategy.FAST : Strategy.DETAILED; // 8 m/s = 30 km/h } + // return searchByViewport(viewport, tokens, strategy); - if (base == GC_BASE31) { - gcid += Math.pow(16, 4) - 16 * Math.pow(31, 3); + // testing purpose + { + SearchResult result = searchByViewport(viewport, tokens, strategy); + String text = Formatter.SEPARATOR + strategy.getL10n() + Formatter.SEPARATOR; + int speed = (int) cgeoapplication.getInstance().currentGeo().getSpeed(); + if (Settings.isUseMetricUnits()) { + text += speed + " km/h"; + } else { + text += speed / IConversion.MILES_TO_KILOMETER + " mph"; + } + result.setUrl(result.getUrl() + text); + return result; } - return gcid; - } - - /** Get user session & session token from the Live Map. Needed for following requests */ - public static String[] getTokens() { - final HttpResponse response = Network.getRequest(GCConstants.URL_LIVE_MAP); - final String data = Network.getResponseData(response); - String userSession = BaseUtils.getMatch(data, GCConstants.PATTERN_USERSESSION, ""); - String sessionToken = BaseUtils.getMatch(data, GCConstants.PATTERN_SESSIONTOKEN, ""); - return new String[] { userSession, sessionToken }; } - public static SearchResult searchByGeocodes(final Set<String> geocodes) { - - final SearchResult result = new SearchResult(); + /** + * Searches the view port on the live map for caches. + * The strategy dictates if only live map information is used or if an additional + * searchByCoordinates query is issued. + * + * @param viewport + * Area to search + * @param tokens + * Live map tokens + * @param strategy + * Strategy for data retrieval and parsing, @see Strategy + * @return + */ + private static SearchResult searchByViewport(final Viewport viewport, final String[] tokens, Strategy strategy) { + Log.d("GCBase.searchByViewport" + viewport.toString()); - final String geocodeList = StringUtils.join(geocodes.toArray(), "|"); - final String referer = GCConstants.URL_LIVE_MAP_DETAILS; + final SearchResult searchResult = new SearchResult(); + searchResult.setUrl(GCConstants.URL_LIVE_MAP + "?ll=" + viewport.getCenter().getLatitude() + "," + viewport.getCenter().getLongitude()); - try { - final Parameters params = new Parameters("i", geocodeList, "_", String.valueOf(System.currentTimeMillis())); - final String data = StringUtils.defaultString(Tile.requestMapInfo(referer, params, referer)); + if (strategy.flags.contains(StrategyFlag.LOAD_TILES)) { + final Set<Tile> tiles = Tile.getTilesForViewport(viewport); - // Example JSON information - // {"status":"success", - // "data":[{"name":"Mission: Impossible","gc":"GC1234","g":"34c2e609-5246-4f91-9029-d6c02b0f2a82","available":true,"archived":false,"subrOnly":false,"li":false,"fp":"5","difficulty":{"text":3.5,"value":"3_5"},"terrain":{"text":1.0,"value":"1"},"hidden":"7/23/2001","container":{"text":"Regular","value":"regular.gif"},"type":{"text":"Unknown Cache","value":8},"owner":{"text":"Ca$h_Cacher","value":"2db18e69-6877-402a-848d-6362621424f6"}}, - // {"name":"HP: Hannover - Sahlkamp","gc":"GC2Q97X","g":"a09149ca-00e0-4aa2-b332-db2b4dfb18d2","available":true,"archived":false,"subrOnly":false,"li":false,"fp":"0","difficulty":{"text":1.0,"value":"1"},"terrain":{"text":1.5,"value":"1_5"},"hidden":"5/29/2011","container":{"text":"Small","value":"small.gif"},"type":{"text":"Traditional Cache","value":2},"owner":{"text":"GeoM@n","value":"1deaa69e-6bcc-421d-95a1-7d32b468cb82"}}] - // } + for (Tile tile : tiles) { - final JSONObject json = new JSONObject(data); - final String status = json.getString("status"); - if (StringUtils.isBlank(status)) { + if (!Tile.Cache.contains(tile)) { + final Parameters params = new Parameters( + "x", String.valueOf(tile.getX()), + "y", String.valueOf(tile.getY()), + "z", String.valueOf(tile.getZoomlevel()), + "ep", "1"); + if (tokens != null) { + params.put("k", tokens[0], "st", tokens[1]); + } + if (Settings.isExcludeMyCaches()) { + params.put("hf", "1", "hh", "1"); // hide found, hide hidden + } + if (Settings.getCacheType() == CacheType.TRADITIONAL) { + params.put("ect", "9,5,3,6,453,13,1304,137,11,4,8,1858"); // 2 = tradi 3 = multi 8 = mystery + } else if (Settings.getCacheType() == CacheType.MULTI) { + params.put("ect", "9,5,2,6,453,13,1304,137,11,4,8,1858"); + } else if (Settings.getCacheType() == CacheType.MYSTERY) { + params.put("ect", "9,5,3,6,453,13,1304,137,11,4,2,1858"); + } + if (tile.getZoomlevel() != 14) { + params.put("_", String.valueOf(System.currentTimeMillis())); + } + // TODO: other types t.b.d - throw new JSONException("No status inside JSON"); - } - if ("success".compareTo(status) != 0) { - throw new JSONException("Wrong status inside JSON"); - } - final JSONArray dataArray = json.getJSONArray("data"); - if (dataArray == null) { - throw new JSONException("No data inside JSON"); - } + // The PNG must be requested first, otherwise the following request would always return with 204 - No Content + Bitmap bitmap = Tile.requestMapTile(params); - for (int j = 0; j < dataArray.length(); j++) { + // Check bitmap size + if (bitmap != null && (bitmap.getWidth() != Tile.TILE_SIZE || + bitmap.getHeight() != Tile.TILE_SIZE)) { + bitmap.recycle(); + bitmap = null; + } - cgCache cache = new cgCache(); + String data = Tile.requestMapInfo(GCConstants.URL_MAP_INFO, params, GCConstants.URL_LIVE_MAP); + if (StringUtils.isEmpty(data)) { + Log.e("GCBase.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); + } + else { + searchResult.addGeocodes(search.getGeocodes()); + } + Tile.Cache.add(tile); + } - JSONObject dataObject = dataArray.getJSONObject(j); - cache.setName(dataObject.getString("name")); - cache.setGeocode(dataObject.getString("gc")); - cache.setGuid(dataObject.getString("g")); // 34c2e609-5246-4f91-9029-d6c02b0f2a82" - cache.setDisabled(!dataObject.getBoolean("available")); - cache.setArchived(dataObject.getBoolean("archived")); - cache.setPremiumMembersOnly(dataObject.getBoolean("subrOnly")); - // "li" seems to be "false" always - cache.setFavoritePoints(Integer.parseInt(dataObject.getString("fp"))); - JSONObject difficultyObj = dataObject.getJSONObject("difficulty"); - cache.setDifficulty(Float.parseFloat(difficultyObj.getString("text"))); // 3.5 - JSONObject terrainObj = dataObject.getJSONObject("terrain"); - cache.setTerrain(Float.parseFloat(terrainObj.getString("text"))); // 1.5 - cache.setHidden(Login.parseGcCustomDate(dataObject.getString("hidden"), "MM/dd/yyyy")); // 7/23/2001 - JSONObject containerObj = dataObject.getJSONObject("container"); - cache.setSize(CacheSize.getById(containerObj.getString("text"))); // Regular - JSONObject typeObj = dataObject.getJSONObject("type"); - cache.setType(CacheType.getByPattern(typeObj.getString("text"))); // Traditional Cache - JSONObject ownerObj = dataObject.getJSONObject("owner"); - cache.setOwner(ownerObj.getString("text")); + // release native bitmap memory + if (bitmap != null) { + bitmap.recycle(); + } - result.addCache(cache); + } + } + } + if (strategy.flags.contains(StrategyFlag.SEARCH_NEARBY)) { + final Geopoint center = viewport.getCenter(); + if ((lastSearchViewport == null) || !lastSearchViewport.contains(center)) { + SearchResult search = GCParser.searchByCoords(center, Settings.getCacheType(), false); + if (search != null && !search.isEmpty()) { + final Set<String> geocodes = search.getGeocodes(); + if (Settings.isPremiumMember()) { + lastSearchViewport = cgeoapplication.getInstance().getBounds(geocodes); + } else { + lastSearchViewport = new Viewport(center, 0.01, 0.01); + } + searchResult.addGeocodes(geocodes); + } } - } catch (JSONException e) { - result.setError(StatusCode.UNKNOWN_ERROR); - } catch (ParseException e) { - result.setError(StatusCode.UNKNOWN_ERROR); - } catch (NumberFormatException e) { - result.setError(StatusCode.UNKNOWN_ERROR); } - return result; - } + return searchResult; + } } diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java index 9b397c9..3d2e994 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCParser.java +++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java @@ -7,7 +7,6 @@ import cgeo.geocaching.Settings; import cgeo.geocaching.TrackableLog; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgImage; -import cgeo.geocaching.cgSearchThread; import cgeo.geocaching.cgTrackable; import cgeo.geocaching.cgWaypoint; import cgeo.geocaching.cgeoapplication; @@ -61,7 +60,7 @@ 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 static SearchResult parseSearch(final cgSearchThread thread, final String url, final String pageContent, final boolean showCaptcha) { + private static SearchResult parseSearch(final String url, final String pageContent, final boolean showCaptcha) { if (StringUtils.isBlank(pageContent)) { Log.e("cgeoBase.parseSearch: No page given"); return null; @@ -77,6 +76,7 @@ public abstract class GCParser { searchResult.viewstates = Login.getViewstates(page); // recaptcha + AbstractSearchThread thread = AbstractSearchThread.getCurrentInstance(); if (showCaptcha) { String recaptchaJsParam = BaseUtils.getMatch(page, GCConstants.PATTERN_SEARCH_RECAPTCHA, false, null); @@ -305,7 +305,7 @@ public abstract class GCParser { return searchResult; } - public static SearchResult parseCache(final String page, final CancellableHandler handler) { + static SearchResult parseCache(final String page, final CancellableHandler handler) { final SearchResult searchResult = parseCacheFromText(page, handler); if (searchResult != null && !searchResult.getGeocodes().isEmpty()) { final cgCache cache = searchResult.getFirstCacheFromResult(LoadFlags.LOAD_CACHE_OR_DB); @@ -710,7 +710,7 @@ public abstract class GCParser { return searchResult; } - public static SearchResult searchByNextPage(cgSearchThread thread, final SearchResult search, boolean showCaptcha) { + public static SearchResult searchByNextPage(final SearchResult search, boolean showCaptcha) { if (search == null) { return search; } @@ -747,7 +747,7 @@ public abstract class GCParser { return search; } - final SearchResult searchResult = parseSearch(thread, url, page, showCaptcha); + final SearchResult searchResult = parseSearch(url, page, showCaptcha); if (searchResult == null || CollectionUtils.isEmpty(searchResult.getGeocodes())) { Log.e("cgeoBase.searchByNextPage: No cache parsed"); return search; @@ -771,7 +771,7 @@ public abstract class GCParser { * @param addF * @return the original params if not null, maybe augmented with f=1, or a new Parameters with f=1 or null otherwise */ - public static Parameters addFToParams(final Parameters params, final boolean my, final boolean addF) { + private static Parameters addFToParams(final Parameters params, final boolean my, final boolean addF) { if (!my && Settings.isExcludeMyCaches() && addF) { if (params == null) { return new Parameters("f", "1"); @@ -784,8 +784,6 @@ public abstract class GCParser { } /** - * @param thread - * thread to run the captcha if needed * @param cacheType * @param listId * @param showCaptcha @@ -793,7 +791,7 @@ public abstract class GCParser { * the parameters to add to the request URI * @return */ - private static SearchResult searchByAny(final cgSearchThread thread, final CacheType cacheType, final boolean my, final boolean showCaptcha, final Parameters params) { + private static SearchResult searchByAny(final CacheType cacheType, final boolean my, final boolean showCaptcha, final Parameters params) { insertCacheType(params, cacheType); final String uri = "http://www.geocaching.com/seek/nearest.aspx"; @@ -805,7 +803,7 @@ public abstract class GCParser { return null; } - final SearchResult searchResult = parseSearch(thread, fullUri, page, showCaptcha); + final SearchResult searchResult = parseSearch(fullUri, page, showCaptcha); if (searchResult == null || CollectionUtils.isEmpty(searchResult.getGeocodes())) { Log.e("cgeoBase.searchByAny: No cache parsed"); return searchResult; @@ -818,22 +816,22 @@ public abstract class GCParser { return search; } - public static SearchResult searchByCoords(final cgSearchThread thread, final Geopoint coords, final CacheType cacheType, final boolean showCaptcha) { + public static SearchResult searchByCoords(final Geopoint coords, final CacheType cacheType, final boolean showCaptcha) { final Parameters params = new Parameters("lat", Double.toString(coords.getLatitude()), "lng", Double.toString(coords.getLongitude())); - return searchByAny(thread, cacheType, false, showCaptcha, params); + return searchByAny(cacheType, false, showCaptcha, params); } - public static SearchResult searchByKeyword(final cgSearchThread thread, final String keyword, final CacheType cacheType, final boolean showCaptcha) { + public static SearchResult searchByKeyword(final String keyword, final CacheType cacheType, final boolean showCaptcha) { if (StringUtils.isBlank(keyword)) { Log.e("cgeoBase.searchByKeyword: No keyword given"); return null; } final Parameters params = new Parameters("key", keyword); - return searchByAny(thread, cacheType, false, showCaptcha, params); + return searchByAny(cacheType, false, showCaptcha, params); } - public static SearchResult searchByUsername(final cgSearchThread thread, final String userName, final CacheType cacheType, final boolean showCaptcha) { + public static SearchResult searchByUsername(final String userName, final CacheType cacheType, final boolean showCaptcha) { if (StringUtils.isBlank(userName)) { Log.e("cgeoBase.searchByUsername: No user name given"); return null; @@ -847,17 +845,17 @@ public abstract class GCParser { Log.i("cgBase.searchByUsername: Overriding users choice, downloading all caches."); } - return searchByAny(thread, cacheType, my, showCaptcha, params); + return searchByAny(cacheType, my, showCaptcha, params); } - public static SearchResult searchByOwner(final cgSearchThread thread, final String userName, final CacheType cacheType, final boolean showCaptcha) { + public static SearchResult searchByOwner(final String userName, final CacheType cacheType, final boolean showCaptcha) { if (StringUtils.isBlank(userName)) { Log.e("cgeoBase.searchByOwner: No user name given"); return null; } final Parameters params = new Parameters("u", userName); - return searchByAny(thread, cacheType, false, showCaptcha, params); + return searchByAny(cacheType, false, showCaptcha, params); } public static cgTrackable searchTrackable(final String geocode, final String guid, final String id) { @@ -885,7 +883,7 @@ public abstract class GCParser { return trackable; } - trackable = parseTrackable(page, cgeoapplication.getInstance(), geocode); + trackable = parseTrackable(page, geocode); if (trackable == null) { Log.e("cgeoBase.searchTrackable: No trackable parsed"); return null; @@ -1108,7 +1106,7 @@ public abstract class GCParser { * the cache to add * @return -1: error occured */ - public static int addToWatchlist(final cgCache cache) { + static int addToWatchlist(final cgCache cache) { final String uri = "http://www.geocaching.com/my/watchlist.aspx?w=" + cache.getCacheId(); String page = Login.postRequestLogged(uri, null); @@ -1134,7 +1132,7 @@ public abstract class GCParser { * the cache to remove * @return -1: error occured */ - public static int removeFromWatchlist(final cgCache cache) { + static int removeFromWatchlist(final cgCache cache) { final String uri = "http://www.geocaching.com/my/watchlist.aspx?ds=1&action=rem&id=" + cache.getCacheId(); String page = Login.postRequestLogged(uri, null); @@ -1170,7 +1168,7 @@ public abstract class GCParser { * if not null, the application to use to save the trackable * @return the parsed trackable, or null if none could be parsed */ - public static cgTrackable parseTrackable(final String page, final cgeoapplication app, final String possibleTrackingcode) { + static cgTrackable parseTrackable(final String page, final String possibleTrackingcode) { if (StringUtils.isBlank(page)) { Log.e("cgeoBase.parseTrackable: No page given"); return null; @@ -1332,8 +1330,8 @@ public abstract class GCParser { trackable.setTrackingcode(possibleTrackingcode); } - if (app != null) { - app.saveTrackable(trackable); + if (cgeoapplication.getInstance() != null) { + cgeoapplication.getInstance().saveTrackable(trackable); } return trackable; diff --git a/main/src/cgeo/geocaching/connector/gc/Login.java b/main/src/cgeo/geocaching/connector/gc/Login.java index c39fcef..113f581 100644 --- a/main/src/cgeo/geocaching/connector/gc/Login.java +++ b/main/src/cgeo/geocaching/connector/gc/Login.java @@ -433,4 +433,12 @@ public abstract class Login { return data; } + /** 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, ""); + 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 new file mode 100644 index 0000000..45efff2 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/gc/SearchHandler.java @@ -0,0 +1,107 @@ +package cgeo.geocaching.connector.gc; + +import cgeo.geocaching.R; +import cgeo.geocaching.utils.Log; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Handler; +import android.os.Message; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; +import android.widget.ImageView; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; + +public class SearchHandler extends Handler { + private Activity activity = null; + private Resources res = null; + private AbstractSearchThread recaptchaThread = null; + private ImageView imageView = null; + private Bitmap img = null; + + private Handler imgHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + try { + if (img != null && imageView != null) { + imageView.setImageBitmap(img); + } + } catch (Exception e) { + // nothing + } + } + }; + + public SearchHandler(Activity activityIn, Resources resIn, AbstractSearchThread recaptchaThreadIn) { + activity = activityIn; + res = resIn; + recaptchaThread = recaptchaThreadIn; + } + + @Override + public void handleMessage(Message msg) { + try { + if (msg.what == 1) { + final AlertDialog.Builder dlg = new AlertDialog.Builder(activity); + final LayoutInflater inflater = activity.getLayoutInflater(); + final View view = inflater.inflate(R.layout.recaptcha_dialog, null); + + imageView = (ImageView) view.findViewById(R.id.image); + + (new getCaptcha(new URL("http://www.google.com/recaptcha/api/image?c=" + recaptchaThread.getChallenge()))).start(); + + dlg.setTitle(res.getString(R.string.caches_recaptcha_title)); + dlg.setView(view); + dlg.setNeutralButton(res.getString(R.string.caches_recaptcha_continue), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + final String text = ((EditText) view.findViewById(R.id.text)).getText().toString(); + + recaptchaThread.setText(text); + + dialog.cancel(); + } + }); + + dlg.create().show(); + } + } catch (Exception e) { + // nothing + } + } + + private class getCaptcha extends Thread { + private URL uri = null; + + public getCaptcha(URL uriIn) { + uri = uriIn; + } + + @Override + public void run() { + try { + HttpURLConnection connection = (HttpURLConnection) uri.openConnection(); + connection.setDoInput(true); + connection.connect(); + + InputStream is = connection.getInputStream(); + + img = BitmapFactory.decodeStream(is); + + is.close(); + + imgHandler.sendEmptyMessage(0); + } catch (IOException e) { + Log.e("Failed to download reCAPTCHA image"); + } + } + } +} diff --git a/main/src/cgeo/geocaching/connector/gc/Tile.java b/main/src/cgeo/geocaching/connector/gc/Tile.java index 692f28b..4747912 100644 --- a/main/src/cgeo/geocaching/connector/gc/Tile.java +++ b/main/src/cgeo/geocaching/connector/gc/Tile.java @@ -5,6 +5,7 @@ import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.utils.LeastRecentlyUsedMap; import cgeo.geocaching.utils.Log; import org.apache.http.HttpResponse; @@ -13,6 +14,10 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; /** * All about tiles. @@ -213,8 +218,8 @@ public class Tile { } /** Request .png image for a tile. */ - public static Bitmap requestMapTile(final String url, final Parameters params, final String referer) { - final HttpResponse response = Network.getRequest(url, params, new Parameters("Referer", referer)); + public static Bitmap requestMapTile(final Parameters params) { + final HttpResponse response = Network.getRequest(GCConstants.URL_MAP_TILE, params, new Parameters("Referer", GCConstants.URL_LIVE_MAP)); try { return response != null ? BitmapFactory.decodeStream(response.getEntity().getContent()) : null; } catch (IOException e) { @@ -226,4 +231,45 @@ public class Tile { public boolean containsPoint(final ICoordinates point) { return viewPort.contains(point); } + + /** + * Calculate needed tiles for the given viewport + * + * @param viewport + * @return + */ + protected static Set<Tile> getTilesForViewport(final Viewport viewport) { + Set<Tile> tiles = new HashSet<Tile>(); + int zoom = Math.min(Tile.calcZoomLon(viewport.bottomLeft, viewport.topRight), + Tile.calcZoomLat(viewport.bottomLeft, viewport.topRight)); + tiles.add(new Tile(viewport.bottomLeft, zoom)); + tiles.add(new Tile(new Geopoint(viewport.getLatitudeMin(), viewport.getLongitudeMax()), zoom)); + tiles.add(new Tile(new Geopoint(viewport.getLatitudeMax(), viewport.getLongitudeMin()), zoom)); + tiles.add(new Tile(viewport.topRight, zoom)); + return tiles; + } + + public static class Cache { + private final static LeastRecentlyUsedMap<Integer, Tile> tileCache = new LeastRecentlyUsedMap.LruCache<Integer, Tile>(64); + + public static void removeFromTileCache(final ICoordinates point) { + if (point != null) { + Collection<Tile> tiles = new ArrayList<Tile>(tileCache.values()); + for (Tile tile : tiles) { + if (tile.containsPoint(point)) { + tileCache.remove(tile.hashCode()); + } + } + } + } + + public static boolean contains(final Tile tile) { + return tileCache.containsKey(tile.hashCode()); + } + + public static void add(final Tile tile) { + tileCache.put(tile.hashCode(), tile); + } + } + } diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java index f291065..508aab4 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java @@ -3,12 +3,12 @@ package cgeo.geocaching.connector.oc; import cgeo.geocaching.SearchResult; import cgeo.geocaching.Settings; import cgeo.geocaching.cgCache; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.connector.capability.ISearchByGeocode; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.CryptUtils; -public class OCApiConnector extends OCConnector { +public class OCApiConnector extends OCConnector implements ISearchByGeocode { private final String cK; @@ -28,18 +28,12 @@ public class OCApiConnector extends OCConnector { } @Override - public boolean supportsRefreshCache(cgCache cache) { - return true; - } - - @Override - public SearchResult searchByGeocode(final String geocode, final String guid, final cgeoapplication app, final CancellableHandler handler) { + public SearchResult searchByGeocode(final String geocode, final String guid, final CancellableHandler handler) { final cgCache cache = OkapiClient.getCache(geocode); if (cache == null) { return null; } - final SearchResult searchResult = new SearchResult(); - searchResult.addCache(cache); + final SearchResult searchResult = new SearchResult(cache); return searchResult.filterSearchResults(false, false, Settings.getCacheType()); } } diff --git a/main/src/cgeo/geocaching/connector/ox/OXConnector.java b/main/src/cgeo/geocaching/connector/ox/OXConnector.java index c81011f..4c53361 100644 --- a/main/src/cgeo/geocaching/connector/ox/OXConnector.java +++ b/main/src/cgeo/geocaching/connector/ox/OXConnector.java @@ -3,17 +3,20 @@ package cgeo.geocaching.connector.ox; import cgeo.geocaching.SearchResult; import cgeo.geocaching.Settings; import cgeo.geocaching.cgCache; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.connector.AbstractConnector; +import cgeo.geocaching.connector.capability.ISearchByCenter; +import cgeo.geocaching.connector.capability.ISearchByGeocode; +import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.utils.CancellableHandler; +import java.util.Collection; import java.util.regex.Pattern; /** * connector for OpenCaching.com * */ -public class OXConnector extends AbstractConnector { +public class OXConnector extends AbstractConnector implements ISearchByCenter, ISearchByGeocode { private static final Pattern PATTERN_GEOCODE = Pattern.compile("OX[A-Z0-9]+", Pattern.CASE_INSENSITIVE); @@ -44,13 +47,22 @@ public class OXConnector extends AbstractConnector { } @Override - public SearchResult searchByGeocode(String geocode, String guid, cgeoapplication app, CancellableHandler handler) { + public SearchResult searchByGeocode(String geocode, String guid, CancellableHandler handler) { final cgCache cache = OpenCachingApi.searchByGeoCode(geocode); if (cache == null) { return null; } - final SearchResult searchResult = new SearchResult(); - searchResult.addCache(cache); + final SearchResult searchResult = new SearchResult(cache); + return searchResult.filterSearchResults(false, false, Settings.getCacheType()); + } + + @Override + public SearchResult searchByCenter(Geopoint center) { + Collection<cgCache> caches = OpenCachingApi.searchByCenter(center); + if (caches == null) { + return null; + } + final SearchResult searchResult = new SearchResult(caches); return searchResult.filterSearchResults(false, false, Settings.getCacheType()); } } diff --git a/main/src/cgeo/geocaching/connector/ox/OpenCachingApi.java b/main/src/cgeo/geocaching/connector/ox/OpenCachingApi.java index 304429f..f06230e 100644 --- a/main/src/cgeo/geocaching/connector/ox/OpenCachingApi.java +++ b/main/src/cgeo/geocaching/connector/ox/OpenCachingApi.java @@ -5,6 +5,8 @@ import cgeo.geocaching.cgCache; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.LoadFlags.SaveFlag; import cgeo.geocaching.files.GPX10Parser; +import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.geopoint.GeopointFormatter; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.utils.CryptUtils; @@ -14,6 +16,7 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.http.HttpResponse; import java.util.Collection; +import java.util.Collections; import java.util.EnumSet; public class OpenCachingApi { @@ -21,28 +24,54 @@ public class OpenCachingApi { private static final String DEV_KEY = CryptUtils.rot13("PtqQnHo9RUTht3Np"); public static cgCache searchByGeoCode(final String geocode) { - final HttpResponse response = Network.getRequest("http://www.opencaching.com/api/geocache/" + geocode + ".gpx", new Parameters("Authorization", DEV_KEY)); + final HttpResponse response = Network.getRequest("http://www.opencaching.com/api/geocache/" + geocode + ".gpx", + new Parameters( + "Authorization", DEV_KEY, + "log_limit", "30", + "hint", "true", + "description", "html")); + final Collection<cgCache> caches = importCachesFromResponse(response, true); + if (CollectionUtils.isNotEmpty(caches)) { + return caches.iterator().next(); + } + return null; + } + + private static Collection<cgCache> importCachesFromResponse(final HttpResponse response, final boolean isDetailed) { if (response == null) { - return null; + return Collections.emptyList(); } Collection<cgCache> caches = null; try { caches = new GPX10Parser(StoredList.STANDARD_LIST_ID).parse(response.getEntity().getContent(), null); } catch (Exception e) { Log.e("Error importing from OpenCaching.com", e); + return Collections.emptyList(); } - if (caches != null && CollectionUtils.isNotEmpty(caches)) { - final cgCache cache = caches.iterator().next(); + for (cgCache cache : caches) { cache.setUpdated(System.currentTimeMillis()); - cache.setDetailedUpdate(cache.getUpdated()); - cache.setDetailed(true); + if (isDetailed) { + cache.setDetailedUpdate(cache.getUpdated()); + cache.setDetailed(true); + } // save full detailed caches cgeoapplication.getInstance().saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); - return cache; } + return caches; + } - return null; + public static Collection<cgCache> searchByCenter(final Geopoint center) { + final HttpResponse response = Network.getRequest("http://www.opencaching.com/api/geocache/.gpx", + new Parameters( + "Authorization", DEV_KEY, + "log_limit", "0", + "hint", "false", + "description", "none", + "limit", "10", + "center", center.format(GeopointFormatter.Format.LAT_LON_DECDEGREE_COMMA))); + return importCachesFromResponse(response, false); } + } |