package cgeo.geocaching.connector.gc; 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.connector.AbstractConnector; 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.utils.CancellableHandler; import cgeo.geocaching.utils.Log; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import java.util.regex.Pattern; public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort { 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); private GCConnector() { // singleton } /** * initialization on demand holder pattern */ private static class Holder { private static final GCConnector INSTANCE = new GCConnector(); } public static GCConnector getInstance() { return Holder.INSTANCE; } @Override public boolean canHandle(String geocode) { if (geocode == null) { return false; } return GCConstants.PATTERN_GC_CODE.matcher(geocode).matches() || GCConstants.PATTERN_TB_CODE.matcher(geocode).matches(); } @Override public String getLongCacheUrl(Geocache cache) { return CACHE_URL_LONG + cache.getGeocode(); } @Override public String getCacheUrl(Geocache cache) { return CACHE_URL_SHORT + cache.getGeocode(); } @Override public boolean supportsOwnCoordinates() { return true; } @Override public boolean supportsWatchList() { return true; } @Override public boolean supportsLogging() { return true; } @Override public String getName() { return "GeoCaching.com"; } @Override public String getHost() { return "www.geocaching.com"; } @Override public boolean supportsUserActions() { return true; } @Override public SearchResult searchByGeocode(final String geocode, final String guid, final CancellableHandler handler) { CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_loadpage); final String page = GCParser.requestHtmlPage(geocode, guid, "y", String.valueOf(GCConstants.NUMBER_OF_LOGS)); if (StringUtils.isEmpty(page)) { final SearchResult search = new SearchResult(); if (cgData.isThere(geocode, guid, true, false)) { if (StringUtils.isBlank(geocode) && StringUtils.isNotBlank(guid)) { Log.i("Loading old cache from cache."); search.addGeocode(cgData.getGeocodeForGuid(guid)); } else { search.addGeocode(geocode); } search.setError(StatusCode.NO_ERROR); return search; } Log.e("GCConnector.searchByGeocode: No data from server"); search.setError(StatusCode.COMMUNICATION_ERROR); return search; } final SearchResult searchResult = GCParser.parseCache(page, handler); if (searchResult == null || CollectionUtils.isEmpty(searchResult.getGeocodes())) { Log.w("GCConnector.searchByGeocode: No cache parsed"); return searchResult; } // do not filter when searching for one specific cache return searchResult; } @Override public SearchResult searchByViewport(Viewport viewport, String[] tokens) { return GCMap.searchByViewport(viewport, tokens); } @Override public boolean isZippedGPXFile(final String fileName) { return gpxZipFilePattern.matcher(fileName).matches(); } @Override public boolean isReliableLatLon(boolean cacheHasReliableLatLon) { return cacheHasReliableLatLon; } @Override public boolean isOwner(final ICache cache) { return StringUtils.equalsIgnoreCase(cache.getOwnerUserId(), Settings.getUsername()); } public static boolean addToWatchlist(Geocache cache) { final boolean added = GCParser.addToWatchlist(cache); if (added) { cgData.saveChangedCache(cache); } return added; } public static boolean removeFromWatchlist(Geocache cache) { final boolean removed = GCParser.removeFromWatchlist(cache); if (removed) { cgData.saveChangedCache(cache); } return removed; } /** * Add a cache to the favorites list. * * This must not be called from the UI thread. * * @param cache the cache to add * @return true if the cache was sucessfully added, false otherwise */ public static boolean addToFavorites(Geocache cache) { final boolean added = GCParser.addToFavorites(cache); if (added) { cgData.saveChangedCache(cache); } return added; } /** * Remove a cache from the favorites list. * * This must not be called from the UI thread. * * @param cache the cache to add * @return true if the cache was sucessfully added, false otherwise */ public static boolean removeFromFavorites(Geocache cache) { final boolean removed = GCParser.removeFromFavorites(cache); if (removed) { cgData.saveChangedCache(cache); } return removed; } @Override public boolean uploadModifiedCoordinates(Geocache cache, Geopoint wpt) { final boolean uploaded = GCParser.uploadModifiedCoordinates(cache, wpt); if (uploaded) { cgData.saveChangedCache(cache); } return uploaded; } @Override public boolean deleteModifiedCoordinates(Geocache cache) { final boolean deleted = GCParser.deleteModifiedCoordinates(cache); if (deleted) { cgData.saveChangedCache(cache); } return deleted; } @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; } @Override public boolean supportsFavoritePoints() { return true; } @Override protected String getCacheUrlPrefix() { return CACHE_URL_SHORT; } @Override public CacheRealm getCacheRealm() { return CacheRealm.GC; } @Override public boolean isActivated() { return Settings.isGCConnectorActive(); } }