aboutsummaryrefslogtreecommitdiffstats
path: root/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
diff options
context:
space:
mode:
Diffstat (limited to 'main/src/cgeo/geocaching/connector/oc/OkapiClient.java')
-rw-r--r--main/src/cgeo/geocaching/connector/oc/OkapiClient.java370
1 files changed, 336 insertions, 34 deletions
diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
index 0673605..3224549 100644
--- a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
+++ b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java
@@ -3,16 +3,27 @@ package cgeo.geocaching.connector.oc;
import cgeo.geocaching.Geocache;
import cgeo.geocaching.Image;
import cgeo.geocaching.LogEntry;
+import cgeo.geocaching.R;
+import cgeo.geocaching.Settings;
+import cgeo.geocaching.Waypoint;
import cgeo.geocaching.cgData;
+import cgeo.geocaching.cgeoapplication;
import cgeo.geocaching.connector.ConnectorFactory;
import cgeo.geocaching.connector.IConnector;
+import cgeo.geocaching.connector.LogResult;
+import cgeo.geocaching.connector.gc.GCConnector;
+import cgeo.geocaching.enumerations.CacheAttribute;
import cgeo.geocaching.enumerations.CacheSize;
import cgeo.geocaching.enumerations.CacheType;
import cgeo.geocaching.enumerations.LoadFlags.SaveFlag;
import cgeo.geocaching.enumerations.LogType;
+import cgeo.geocaching.enumerations.StatusCode;
+import cgeo.geocaching.enumerations.WaypointType;
import cgeo.geocaching.geopoint.Geopoint;
import cgeo.geocaching.geopoint.GeopointFormatter;
+import cgeo.geocaching.geopoint.Viewport;
import cgeo.geocaching.network.Network;
+import cgeo.geocaching.network.OAuth;
import cgeo.geocaching.network.Parameters;
import cgeo.geocaching.utils.Log;
@@ -27,12 +38,36 @@ import android.text.Html;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
final public class OkapiClient {
+
+ private static final SimpleDateFormat logDateFormat;
+
+ static {
+ logDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ", Locale.US);
+ logDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ private static final String CACHE_ATTRNAMES = "attrnames";
+ private static final String WPT_LOCATION = "location";
+ private static final String WPT_DESCRIPTION = "description";
+ private static final String WPT_TYPE = "type";
+ private static final String WPT_NAME = "name";
+ private static final String CACHE_IS_WATCHED = "is_watched";
+ private static final String CACHE_WPTS = "alt_wpts";
+ private static final String CACHE_STATUS_ARCHIVED = "Archived";
+ private static final String CACHE_STATUS_DISABLED = "Temporarily unavailable";
+ private static final String CACHE_IS_FOUND = "is_found";
private static final String CACHE_SIZE = "size";
private static final String CACHE_VOTES = "rating_votes";
private static final String CACHE_NOTFOUNDS = "notfounds";
@@ -64,12 +99,22 @@ final public class OkapiClient {
private static final String USER_USERNAME = "username";
private static final String SERVICE_CACHE = "/okapi/services/caches/geocache";
- private static final String SERVICE_CACHE_FIELDS = "code|name|location|type|status|owner|founds|notfounds|size|difficulty|terrain|rating|rating_votes|recommendations|description|hint|images|latest_logs|date_hidden";
- private static final String SERVICE_NEAREST = "/okapi/services/caches/search/nearest";
+ private static final String SERVICE_CACHE_CORE_FIELDS = "code|name|location|type|status|difficulty|terrain|size|is_found";
+ private static final String SERVICE_CACHE_FIELDS = SERVICE_CACHE_CORE_FIELDS + "|owner|founds|notfounds|rating|rating_votes|recommendations|description|hint|images|latest_logs|date_hidden|attribution_note|alt_wpts|is_watched|attrnames|gc_code";
+
+ private static final String SERVICE_SEARCH_AND_RETRIEVE = "/okapi/services/caches/shortcuts/search_and_retrieve";
+
+ private static final String METHOD_SEARCH_NEAREST = "services/caches/search/nearest";
+ private static final String METHOD_SEARCH_BBOX = "services/caches/search/bbox";
+ private static final String METHOD_RETRIEVE_CACHES = "services/caches/geocaches";
+
+ private static final String SERVICE_MARK_CACHE = "/okapi/services/caches/mark";
+
+ private static final String SERVICE_SUBMIT_LOG = "/okapi/services/logs/submit";
public static Geocache getCache(final String geoCode) {
- final Parameters params = new Parameters("cache_code", geoCode, "fields", SERVICE_CACHE_FIELDS);
+ final Parameters params = new Parameters("cache_code", geoCode, "fields", SERVICE_CACHE_FIELDS, "attribution_append", "none");
final JSONObject data = request(ConnectorFactory.getConnector(geoCode), SERVICE_CACHE, params);
if (data == null) {
@@ -81,30 +126,115 @@ final public class OkapiClient {
public static List<Geocache> getCachesAround(final Geopoint center, IConnector connector) {
String centerString = GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, center) + "|" + GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, center);
- final Parameters params = new Parameters("center", centerString);
- final JSONObject data = request(connector, SERVICE_NEAREST, params);
+ final Parameters params = new Parameters("search_method", METHOD_SEARCH_NEAREST);
+ final Map<String, String> valueMap = new LinkedHashMap<String, String>();
+ valueMap.put("center", centerString);
+ valueMap.put("limit", "20");
+
+ addFilterParams(valueMap);
+
+ params.add("search_params", new JSONObject(valueMap).toString());
+
+ addRetrieveParams(params);
+
+ final JSONObject data = request(connector, SERVICE_SEARCH_AND_RETRIEVE, params);
if (data == null) {
- return null;
+ return Collections.emptyList();
+ }
+
+ return parseCaches(data);
+ }
+
+ public static List<Geocache> getCachesBBox(final Viewport viewport, IConnector connector) {
+
+ if (viewport.getLatitudeSpan() == 0 || viewport.getLongitudeSpan() == 0) {
+ return Collections.emptyList();
+ }
+
+ String bboxString = GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, viewport.bottomLeft)
+ + "|" + GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, viewport.bottomLeft)
+ + "|" + GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, viewport.topRight)
+ + "|" + GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, viewport.topRight);
+ final Parameters params = new Parameters("search_method", METHOD_SEARCH_BBOX);
+ final Map<String, String> valueMap = new LinkedHashMap<String, String>();
+ valueMap.put("bbox", bboxString);
+
+ addFilterParams(valueMap);
+
+ params.add("search_params", new JSONObject(valueMap).toString());
+
+ addRetrieveParams(params);
+
+ final JSONObject data = request(connector, SERVICE_SEARCH_AND_RETRIEVE, params);
+
+ if (data == null) {
+ return Collections.emptyList();
}
return parseCaches(data);
}
+ public static boolean setWatchState(final Geocache cache, final boolean watched, IConnector connector) {
+ final Parameters params = new Parameters("cache_code", cache.getGeocode());
+ params.add("watched", watched ? "true" : "false");
+
+ final JSONObject data = request(connector, SERVICE_MARK_CACHE, params);
+
+ if (data == null) {
+ return false;
+ }
+
+ cache.setOnWatchlist(watched);
+
+ return true;
+ }
+
+ public static LogResult postLog(final Geocache cache, LogType logType, Calendar date, String log, IConnector connector) {
+ final Parameters params = new Parameters("cache_code", cache.getGeocode());
+ params.add("logtype", logType.oc_type);
+ params.add("comment", log);
+ params.add("comment_format", "plaintext");
+ params.add("when", logDateFormat.format(date.getTime()));
+ if (logType.equals(LogType.NEEDS_MAINTENANCE)) {
+ params.add("needs_maintenance", "true");
+ }
+
+ final JSONObject data = request(connector, SERVICE_SUBMIT_LOG, params);
+
+ if (data == null) {
+ return new LogResult(StatusCode.LOG_POST_ERROR, "");
+ }
+
+ try {
+ if (data.getBoolean("success")) {
+ return new LogResult(StatusCode.NO_ERROR, data.getString("log_uuid"));
+ }
+
+ return new LogResult(StatusCode.LOG_POST_ERROR, "");
+ } catch (JSONException e) {
+ Log.e("OkapiClient.postLog", e);
+ }
+ return new LogResult(StatusCode.LOG_POST_ERROR, "");
+ }
+
private static List<Geocache> parseCaches(final JSONObject response) {
try {
- final JSONArray cachesResponse = response.getJSONArray("results");
+ // Check for empty result
+ final String result = response.getString("results");
+ if (StringUtils.isBlank(result) || StringUtils.equals(result, "[]")) {
+ return Collections.emptyList();
+ }
+
+ // Get and iterate result list
+ final JSONObject cachesResponse = response.getJSONObject("results");
if (cachesResponse != null) {
- ArrayList<String> geocodes = new ArrayList<String>(cachesResponse.length());
- for (int i = 0; i < cachesResponse.length(); i++) {
- String geocode = cachesResponse.getString(i);
- if (StringUtils.isNotBlank(geocode)) {
- geocodes.add(geocode);
- }
- }
- List<Geocache> caches = new ArrayList<Geocache>(geocodes.size());
- for (String geocode : geocodes) {
- Geocache cache = getCache(geocode);
+ List<Geocache> caches = new ArrayList<Geocache>(cachesResponse.length());
+ @SuppressWarnings("unchecked")
+ Iterator<String> keys = cachesResponse.keys();
+ while (keys.hasNext()) {
+ String key = keys.next();
+ Geocache cache = parseSmallCache(cachesResponse.getJSONObject(key));
if (cache != null) {
caches.add(cache);
}
@@ -112,24 +242,31 @@ final public class OkapiClient {
return caches;
}
} catch (JSONException e) {
- Log.e("OkapiClient.parseCaches", e);
+ Log.e("OkapiClient.parseCachesResult", e);
}
- return null;
+ return Collections.emptyList();
+ }
+
+ private static Geocache parseSmallCache(final JSONObject response) {
+ final Geocache cache = new Geocache();
+ cache.setReliableLatLon(true);
+ try {
+
+ parseCoreCache(response, cache);
+
+ cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_CACHE));
+ } catch (JSONException e) {
+ Log.e("OkapiClient.parseSmallCache", e);
+ }
+ return cache;
}
private static Geocache parseCache(final JSONObject response) {
final Geocache cache = new Geocache();
cache.setReliableLatLon(true);
try {
- cache.setGeocode(response.getString(CACHE_CODE));
- cache.setName(response.getString(CACHE_NAME));
- // not used: names
- setLocation(cache, response.getString(CACHE_LOCATION));
- cache.setType(getCacheType(response.getString(CACHE_TYPE)));
- final String status = response.getString(CACHE_STATUS);
- cache.setDisabled(status.equalsIgnoreCase("Temporarily unavailable"));
- cache.setArchived(status.equalsIgnoreCase("Archived"));
+ parseCoreCache(response, cache);
// not used: url
final JSONObject owner = response.getJSONObject(CACHE_OWNER);
@@ -137,9 +274,7 @@ final public class OkapiClient {
cache.getLogCounts().put(LogType.FOUND_IT, response.getInt(CACHE_FOUNDS));
cache.getLogCounts().put(LogType.DIDNT_FIND_IT, response.getInt(CACHE_NOTFOUNDS));
- cache.setSize(getCacheSize(response));
- cache.setDifficulty((float) response.getDouble(CACHE_DIFFICULTY));
- cache.setTerrain((float) response.getDouble(CACHE_TERRAIN));
+
if (!response.isNull(CACHE_RATING)) {
cache.setRating((float) response.getDouble(CACHE_RATING));
}
@@ -147,8 +282,22 @@ final public class OkapiClient {
cache.setFavoritePoints(response.getInt(CACHE_RECOMMENDATIONS));
// not used: req_password
- cache.setDescription(response.getString(CACHE_DESCRIPTION));
- cache.setHint(Html.fromHtml(response.getString(CACHE_HINT)).toString());
+ // Prepend gc-link to description if available
+ StringBuilder description = new StringBuilder(500);
+ if (!response.isNull("gc_code")) {
+ String gccode = response.getString("gc_code");
+ description.append(cgeoapplication.getInstance().getResources()
+ .getString(R.string.cache_listed_on, GCConnector.getInstance().getName()))
+ .append(": <a href=\"http://coord.info/")
+ .append(gccode)
+ .append("\">")
+ .append(gccode)
+ .append("</a><br /><br />");
+ }
+ description.append(response.getString(CACHE_DESCRIPTION));
+ cache.setDescription(description.toString());
+
+ cache.setHint(Html.escapeHtml(response.getString(CACHE_HINT)));
// not used: hints
final JSONArray images = response.getJSONArray(CACHE_IMAGES);
@@ -163,9 +312,13 @@ final public class OkapiClient {
}
}
- // not used: attrnames
+ cache.setAttributes(parseAttributes(response.getJSONArray(CACHE_ATTRNAMES)));
cache.setLogs(parseLogs(response.getJSONArray(CACHE_LATEST_LOGS)));
cache.setHidden(parseDate(response.getString(CACHE_HIDDEN)));
+ //TODO: Store license per cache
+ //cache.setLicense(response.getString("attribution_note"));
+ cache.setWaypoints(parseWaypoints(response.getJSONArray(CACHE_WPTS)), false);
+ cache.setOnWatchlist(response.getBoolean(CACHE_IS_WATCHED));
cache.setDetailedUpdatedNow();
// save full detailed caches
@@ -176,6 +329,24 @@ final public class OkapiClient {
return cache;
}
+ private static void parseCoreCache(final JSONObject response, final Geocache cache) throws JSONException {
+ cache.setGeocode(response.getString(CACHE_CODE));
+ cache.setName(response.getString(CACHE_NAME));
+ // not used: names
+ setLocation(cache, response.getString(CACHE_LOCATION));
+ cache.setType(getCacheType(response.getString(CACHE_TYPE)));
+
+ final String status = response.getString(CACHE_STATUS);
+ cache.setDisabled(status.equalsIgnoreCase(CACHE_STATUS_DISABLED));
+ cache.setArchived(status.equalsIgnoreCase(CACHE_STATUS_ARCHIVED));
+
+ cache.setSize(getCacheSize(response));
+ cache.setDifficulty((float) response.getDouble(CACHE_DIFFICULTY));
+ cache.setTerrain((float) response.getDouble(CACHE_TERRAIN));
+
+ cache.setFound(response.getBoolean(CACHE_IS_FOUND));
+ }
+
private static String absoluteUrl(String url, String geocode) {
final Uri uri = Uri.parse(url);
@@ -214,6 +385,30 @@ final public class OkapiClient {
return result;
}
+ private static List<Waypoint> parseWaypoints(JSONArray wptsJson) {
+ List<Waypoint> result = null;
+ for (int i = 0; i < wptsJson.length(); i++) {
+ try {
+ JSONObject wptResponse = wptsJson.getJSONObject(i);
+ Waypoint wpt = new Waypoint(wptResponse.getString(WPT_NAME),
+ parseWptType(wptResponse.getString(WPT_TYPE)),
+ false);
+ wpt.setNote(wptResponse.getString(WPT_DESCRIPTION));
+ Geopoint pt = parseCoords(wptResponse.getString(WPT_LOCATION));
+ if (pt != null) {
+ wpt.setCoords(pt);
+ }
+ if (result == null) {
+ result = new ArrayList<Waypoint>();
+ }
+ result.add(wpt);
+ } catch (JSONException e) {
+ Log.e("OkapiClient.parseWaypoints", e);
+ }
+ }
+ return result;
+ }
+
private static LogType parseLogType(String logType) {
if ("Found it".equalsIgnoreCase(logType)) {
return LogType.FOUND_IT;
@@ -224,6 +419,31 @@ final public class OkapiClient {
return LogType.NOTE;
}
+ private static WaypointType parseWptType(String wptType) {
+ if ("parking".equalsIgnoreCase(wptType)) {
+ return WaypointType.PARKING;
+ }
+ if ("path".equalsIgnoreCase(wptType)) {
+ return WaypointType.TRAILHEAD;
+ }
+ if ("stage".equalsIgnoreCase(wptType)) {
+ return WaypointType.STAGE;
+ }
+ if ("physical-stage".equalsIgnoreCase(wptType)) {
+ return WaypointType.STAGE;
+ }
+ if ("virtual-stage".equalsIgnoreCase(wptType)) {
+ return WaypointType.PUZZLE;
+ }
+ if ("final".equalsIgnoreCase(wptType)) {
+ return WaypointType.FINAL;
+ }
+ if ("poi".equalsIgnoreCase(wptType)) {
+ return WaypointType.TRAILHEAD;
+ }
+ return WaypointType.WAYPOINT;
+ }
+
private static Date parseDate(final String date) {
final SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault());
final String strippedDate = date.replaceAll("\\+0([0-9]){1}\\:00", "+0$100");
@@ -235,6 +455,36 @@ final public class OkapiClient {
return null;
}
+ private static Geopoint parseCoords(final String location) {
+ final String latitude = StringUtils.substringBefore(location, "|");
+ final String longitude = StringUtils.substringAfter(location, "|");
+ if (StringUtils.isNotBlank(latitude) && StringUtils.isNotBlank(longitude)) {
+ return new Geopoint(latitude, longitude);
+ }
+
+ return null;
+ }
+
+ private static List<String> parseAttributes(JSONArray nameList) {
+
+ List<String> result = new ArrayList<String>();
+
+ for (int i = 0; i < nameList.length(); i++) {
+ try {
+ String name = nameList.getString(i);
+ CacheAttribute attr = CacheAttribute.getByOcId(AttributeParser.getOcDeId(name));
+
+ if (attr != null) {
+ result.add(attr.rawName);
+ }
+ } catch (JSONException e) {
+ Log.e("OkapiClient.parseAttributes", e);
+ }
+ }
+
+ return result;
+ }
+
private static void setLocation(final Geocache cache, final String location) {
final String latitude = StringUtils.substringBefore(location, "|");
final String longitude = StringUtils.substringAfter(location, "|");
@@ -281,6 +531,18 @@ final public class OkapiClient {
if (cacheType.equalsIgnoreCase("Virtual")) {
return CacheType.VIRTUAL;
}
+ if (cacheType.equalsIgnoreCase("Event")) {
+ return CacheType.EVENT;
+ }
+ if (cacheType.equalsIgnoreCase("Webcam")) {
+ return CacheType.WEBCAM;
+ }
+ if (cacheType.equalsIgnoreCase("Math/Physics")) {
+ return CacheType.MYSTERY;
+ }
+ if (cacheType.equalsIgnoreCase("Drive-In")) {
+ return CacheType.TRADITIONAL;
+ }
return CacheType.UNKNOWN;
}
@@ -297,9 +559,10 @@ final public class OkapiClient {
return null;
}
- ((OCApiConnector) connector).addAuthentication(params);
params.add("langpref", getPreferredLanguage());
+ OAuth.signOAuth(host, service, "GET", false, params, Settings.getOCDETokenPublic(), Settings.getOCDETokenSecret(), ((OCApiLiveConnector) connector).getCK(), ((OCApiLiveConnector) connector).getCS());
+
final String uri = "http://" + host + service;
return Network.requestJSON(uri, params);
}
@@ -311,4 +574,43 @@ final public class OkapiClient {
}
return "en";
}
+
+ private static void addFilterParams(final Map<String, String> valueMap) {
+ if (!Settings.isExcludeDisabledCaches()) {
+ valueMap.put("status", "Available|Temporarily unavailable");
+ }
+ if (Settings.isExcludeMyCaches()) {
+ valueMap.put("exclude_my_own", "true");
+ valueMap.put("found_status", "notfound_only");
+ }
+ if (Settings.getCacheType() != CacheType.ALL) {
+ valueMap.put("type", getFilterFromType(Settings.getCacheType()));
+ }
+ }
+
+ private static void addRetrieveParams(final Parameters params) {
+ params.add("retr_method", METHOD_RETRIEVE_CACHES);
+ params.add("retr_params", "{\"fields\": \"" + SERVICE_CACHE_CORE_FIELDS + "\"}");
+ params.add("wrap", "true");
+ }
+
+ private static String getFilterFromType(CacheType cacheType) {
+ switch (cacheType) {
+ case EVENT:
+ return "Event";
+ case MULTI:
+ return "Multi";
+ case MYSTERY:
+ return "Quiz";
+ case TRADITIONAL:
+ return "Traditional";
+ case VIRTUAL:
+ return "Virtual";
+ case WEBCAM:
+ return "Webcam";
+ default:
+ return "";
+ }
+ }
+
}