aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorblafoo <github@blafoo.de>2012-05-10 20:29:54 +0200
committerblafoo <github@blafoo.de>2012-05-10 20:30:53 +0200
commitbdf40be387b9d6598c05c8f49eef56d9684ccf99 (patch)
tree30d969dc3ed1ff530c87021760772a1a5052b883
parent57eeb6214fb178c45685f7bd5f4cdb2706326994 (diff)
downloadcgeo-bdf40be387b9d6598c05c8f49eef56d9684ccf99.zip
cgeo-bdf40be387b9d6598c05c8f49eef56d9684ccf99.tar.gz
cgeo-bdf40be387b9d6598c05c8f49eef56d9684ccf99.tar.bz2
Add/remove to/from favorites. Closes #676
-rw-r--r--main/res/layout/cacheview_details.xml45
-rw-r--r--main/res/values-de/strings.xml5
-rw-r--r--main/res/values/strings.xml8
-rw-r--r--main/src/cgeo/geocaching/CacheDetailActivity.java55
-rw-r--r--main/src/cgeo/geocaching/cgCache.java4
-rw-r--r--main/src/cgeo/geocaching/connector/AbstractConnector.java5
-rw-r--r--main/src/cgeo/geocaching/connector/IConnector.java7
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCConnector.java32
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCConstants.java4
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCParser.java104
10 files changed, 239 insertions, 30 deletions
diff --git a/main/res/layout/cacheview_details.xml b/main/res/layout/cacheview_details.xml
index 4baf5c2..2598fdd 100644
--- a/main/res/layout/cacheview_details.xml
+++ b/main/res/layout/cacheview_details.xml
@@ -135,7 +135,52 @@
android:visibility="gone" />
</RelativeLayout>
</LinearLayout>
+
+ <!-- Favorite points box -->
+ <LinearLayout
+ android:id="@+id/favpoint_box"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <View
+ style="@style/separator_horizontal"
+ android:layout_marginBottom="9dp"
+ android:layout_marginTop="9dp" />
+
+ <RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <TextView
+ android:id="@+id/favpoint_text"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_gravity="left"
+ android:layout_marginLeft="6dip"
+ android:layout_marginRight="130dip"
+ android:paddingRight="3dip"
+ android:textColor="?text_color"
+ android:textSize="14dip" />
+
+ <Button
+ android:id="@+id/add_to_favpoint"
+ style="@style/button_small"
+ android:layout_alignParentRight="true"
+ android:text="@string/cache_favpoint_add"
+ android:visibility="gone" />
+
+ <Button
+ android:id="@+id/remove_from_favpoint"
+ style="@style/button_small"
+ android:layout_alignParentRight="true"
+ android:text="@string/cache_favpoint_remove"
+ android:visibility="gone" />
+ </RelativeLayout>
+ </LinearLayout>
+
<!-- License Box -->
<LinearLayout
diff --git a/main/res/values-de/strings.xml b/main/res/values-de/strings.xml
index 1b5582c..6270083 100644
--- a/main/res/values-de/strings.xml
+++ b/main/res/values-de/strings.xml
@@ -488,6 +488,11 @@
<string name="cache_watchlist_not_on">Dieser Cache ist nicht auf deiner Watchlist.</string>
<string name="cache_watchlist_add">Hinzufügen</string>
<string name="cache_watchlist_remove">Entfernen</string>
+ <string name="cache_favpoint_on">Dieser Cacher ist einer deiner Favoriten.</string>
+ <string name="cache_favpoint_not_on">Dieser Cacher ist keiner deiner Favoriten.</string>
+ <string name="cache_favpoint_add">Hinzufügen zu Favoriten</string>
+ <string name="cache_favpoint_remove">Entfernen aus Favoriten</string>
+
<string name="cache_waypoints">Wegpunkte</string>
<plurals name="waypoints">
<item quantity="one">1 Wegpunkt</item>
diff --git a/main/res/values/strings.xml b/main/res/values/strings.xml
index 0b11c71..f9bcace 100644
--- a/main/res/values/strings.xml
+++ b/main/res/values/strings.xml
@@ -501,6 +501,11 @@
<string name="cache_watchlist_not_on">This cache is not on your watchlist.</string>
<string name="cache_watchlist_add">Add to Watchlist</string>
<string name="cache_watchlist_remove">Remove from Watchlist</string>
+ <string name="cache_favpoint_on">This cache is one of your favorites.</string>
+ <string name="cache_favpoint_not_on">This cache is not one of your favorites.</string>
+ <string name="cache_favpoint_add">Add to Favorites</string>
+ <string name="cache_favpoint_remove">Remove from Favorites</string>
+
<string name="cache_waypoints">Waypoints</string>
<plurals name="waypoints">
<item quantity="one">1 Waypoint</item>
@@ -983,7 +988,7 @@
· 0xErnie (localization DE)\n
· <a href="mailto:bazsy@freemail.hu">Balazs Szabo (Bazsy1983)</a> (loc. HU)\n
· Bananeweizen (code, loc. DE)\n
- · blafoo (code)\n
+ · blafoo (code, localization DE)\n
· Bonemaro (tester)\n
· BudBundi (localization DE, tester)\n
· campbeb (code)\n
@@ -1034,6 +1039,7 @@
<string name="changelog">\n
<b>Next release</b>\n\n
<b>New Features/Functions:</b>\n
+ · Add/remove cache to/from Favorites #676
· Export caches as GPX file\n
· Filter for D/T in lists\n
· Allow close of popup by click on map\n
diff --git a/main/src/cgeo/geocaching/CacheDetailActivity.java b/main/src/cgeo/geocaching/CacheDetailActivity.java
index 62f946a..a9b6bf7 100644
--- a/main/src/cgeo/geocaching/CacheDetailActivity.java
+++ b/main/src/cgeo/geocaching/CacheDetailActivity.java
@@ -1425,6 +1425,26 @@ public class CacheDetailActivity extends AbstractActivity {
buttonWatchlistRemove.setOnClickListener(new RemoveFromWatchlistClickListener());
updateWatchlistBox();
+ // favorite points
+ Button buttonFavPointAdd = (Button) view.findViewById(R.id.add_to_favpoint);
+ Button buttonFavPointRemove = (Button) view.findViewById(R.id.remove_from_favpoint);
+ buttonFavPointAdd.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ GCConnector.addToFavorites(cache);
+ updateFavPointBox();
+ }
+ });
+ buttonFavPointRemove.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ GCConnector.removeFromFavorites(cache);
+ updateFavPointBox();
+ }
+ });
+
+ updateFavPointBox();
+
// data license
IConnector connector = ConnectorFactory.getConnector(cache);
if (connector != null) {
@@ -1689,6 +1709,41 @@ public class CacheDetailActivity extends AbstractActivity {
}
/**
+ * shows/hides buttons, sets text in watchlist box
+ */
+ private void updateFavPointBox() {
+ boolean userIsOwner = StringUtils.equals(cache.getOwnerReal(), Settings.getUsername());
+
+ LinearLayout layout = (LinearLayout) view.findViewById(R.id.favpoint_box);
+ boolean supportsFavoritePoints = cache.supportsFavoritePoints();
+ layout.setVisibility(supportsFavoritePoints ? View.VISIBLE : View.GONE);
+ if (!supportsFavoritePoints || userIsOwner || !Settings.isPremiumMember()) {
+ return;
+ }
+ Button buttonAdd = (Button) view.findViewById(R.id.add_to_favpoint);
+ Button buttonRemove = (Button) view.findViewById(R.id.remove_from_favpoint);
+ TextView text = (TextView) view.findViewById(R.id.favpoint_text);
+
+ if (cache.isFavorite()) {
+ buttonAdd.setVisibility(View.GONE);
+ buttonRemove.setVisibility(View.VISIBLE);
+ text.setText(R.string.cache_favpoint_on);
+ } else {
+ buttonAdd.setVisibility(View.VISIBLE);
+ buttonRemove.setVisibility(View.GONE);
+ text.setText(R.string.cache_favpoint_not_on);
+ }
+
+ // Add/remove to Favorites is only possible if the cache has been found
+ if (!cache.isFound()) {
+ buttonAdd.setEnabled(false);
+ buttonAdd.setVisibility(View.GONE);
+ buttonRemove.setEnabled(false);
+ buttonRemove.setVisibility(View.GONE);
+ }
+ }
+
+ /**
* Handler, called when watchlist add or remove is done
*/
private class WatchlistHandler extends Handler {
diff --git a/main/src/cgeo/geocaching/cgCache.java b/main/src/cgeo/geocaching/cgCache.java
index 957ddb2..b662a1b 100644
--- a/main/src/cgeo/geocaching/cgCache.java
+++ b/main/src/cgeo/geocaching/cgCache.java
@@ -489,6 +489,10 @@ public class cgCache implements ICache, IWaypoint {
return getConnector().supportsWatchList();
}
+ public boolean supportsFavoritePoints() {
+ return getConnector().supportsFavoritePoints();
+ }
+
public boolean supportsLogging() {
return getConnector().supportsLogging();
}
diff --git a/main/src/cgeo/geocaching/connector/AbstractConnector.java b/main/src/cgeo/geocaching/connector/AbstractConnector.java
index f1d00fa..6abc3bc 100644
--- a/main/src/cgeo/geocaching/connector/AbstractConnector.java
+++ b/main/src/cgeo/geocaching/connector/AbstractConnector.java
@@ -17,6 +17,11 @@ public abstract class AbstractConnector implements IConnector {
}
@Override
+ public boolean supportsFavoritePoints() {
+ return false;
+ }
+
+ @Override
public boolean supportsLogging() {
return false;
}
diff --git a/main/src/cgeo/geocaching/connector/IConnector.java b/main/src/cgeo/geocaching/connector/IConnector.java
index 4bc63fc..9111955 100644
--- a/main/src/cgeo/geocaching/connector/IConnector.java
+++ b/main/src/cgeo/geocaching/connector/IConnector.java
@@ -36,6 +36,13 @@ public interface IConnector {
public boolean supportsWatchList();
/**
+ * enable/disable favorite points controls in cache details
+ *
+ * @return
+ */
+ public boolean supportsFavoritePoints();
+
+ /**
* enable/disable logging controls in cache details
*
* @return
diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java
index e30ce44..6ee0b09 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java
@@ -11,7 +11,6 @@ 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;
import cgeo.geocaching.utils.Log;
@@ -77,18 +76,10 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
@Override
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);
- } else if (StringUtils.isNotBlank(guid)) {
- params.put("guid", guid);
- }
- params.put("log", "y");
- params.put("numlogs", String.valueOf(GCConstants.NUMBER_OF_LOGS));
CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_loadpage);
- final String page = Login.getRequestLogged("http://www.geocaching.com/seek/cache_details.aspx", params);
+ final String page = GCParser.requestHtmlPage(geocode, guid, "y", String.valueOf(GCConstants.NUMBER_OF_LOGS));
if (StringUtils.isEmpty(page)) {
final SearchResult search = new SearchResult();
@@ -150,10 +141,31 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode,
return removed;
}
+ public static boolean addToFavorites(cgCache cache) {
+ final boolean added = GCParser.addToFavorites(cache);
+ if (added) {
+ cgeoapplication.getInstance().updateCache(cache);
+ }
+ return added;
+ }
+
+ public static boolean removeFromFavorites(cgCache cache) {
+ final boolean removed = GCParser.removeFromFavorites(cache);
+ if (removed) {
+ cgeoapplication.getInstance().updateCache(cache);
+ }
+ return removed;
+ }
+
@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;
+ }
+
}
diff --git a/main/src/cgeo/geocaching/connector/gc/GCConstants.java b/main/src/cgeo/geocaching/connector/gc/GCConstants.java
index ac38972..7edeec5 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCConstants.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCConstants.java
@@ -46,7 +46,7 @@ public final class GCConstants {
public final static Pattern PATTERN_TYPE = Pattern.compile("<img src=\"[^\"]*/WptTypes/\\d+\\.gif\" alt=\"([^\"]+?)\" title=\"[^\"]+\" width=\"32\" height=\"32\"");
public final static Pattern PATTERN_HIDDEN = Pattern.compile("<span class=\"minorCacheDetails\">\\W*Hidden[\\s:]*([^<]+?)</span>");
public final static Pattern PATTERN_HIDDENEVENT = Pattern.compile("Event\\s*Date\\s*:\\s*([^<]+)</span>", Pattern.DOTALL);
- public final static Pattern PATTERN_FAVORITE = Pattern.compile("<img src=\"/images/icons/icon_favDelete.png\" alt=\"Remove from your Favorites\" title=\"Remove from your Favorites\" />");
+ public final static Pattern PATTERN_FAVORITE = Pattern.compile("<div id=\"pnlFavoriteCache\">"); // without 'class="hideMe"' inside the tag !
public final static Pattern PATTERN_FAVORITECOUNT = Pattern.compile("<a id=\"uxFavContainerLink\"[^>]+>[^<]*<div[^<]*<span class=\"favorite-value\">\\D*([0-9]+?)</span>");
public final static Pattern PATTERN_COUNTLOGS = Pattern.compile("<span id=\"ctl00_ContentBody_lblFindCounts\"><p(.+?)</p></span>");
public final static Pattern PATTERN_LOGBOOK = Pattern.compile("initalLogs = (\\{.+\\});");
@@ -144,7 +144,7 @@ public final class GCConstants {
public final static Pattern PATTERN_OK2 = Pattern.compile("<div id=[\"|']ctl00_ContentBody_LogBookPanel1_ViewLogPanel[\"|']>", Pattern.CASE_INSENSITIVE);
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_USERTOKEN2 = Pattern.compile("userToken\\s*=\\s*'([^']+)'");
+ public final static Pattern PATTERN_USERTOKEN = Pattern.compile("userToken\\s*=\\s*'([^']+)'");
/**
* Patterns for GC and TB codes
diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java
index ed5a5a6..936ce6c 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCParser.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java
@@ -288,7 +288,7 @@ public abstract class GCParser {
LocParser.parseLoc(searchResult, coordinates);
} catch (Exception e) {
- Log.e("cgBase.parseSearch.CIDs: " + e.toString());
+ Log.e("GCParser.parseSearch.CIDs: " + e.toString());
}
}
@@ -840,7 +840,7 @@ public abstract class GCParser {
boolean my = false;
if (userName.equalsIgnoreCase(Settings.getLogin().left)) {
my = true;
- Log.i("cgBase.searchByUsername: Overriding users choice, downloading all caches.");
+ Log.i("GCParser.searchByUsername: Overriding users choice, downloading all caches.");
}
return searchByAny(cacheType, my, showCaptcha, params);
@@ -1099,7 +1099,7 @@ public abstract class GCParser {
/**
* Adds the cache to the watchlist of the user.
- *
+ *
* @param cache
* the cache to add
* @return <code>false</code> if an error occurred, <code>true</code> otherwise
@@ -1109,23 +1109,23 @@ public abstract class GCParser {
String page = Login.postRequestLogged(uri, null);
if (StringUtils.isBlank(page)) {
- Log.e("cgBase.addToWatchlist: No data from server");
+ Log.e("GCParser.addToWatchlist: No data from server");
return false; // error
}
boolean guidOnPage = cache.isGuidContainedInPage(page);
if (guidOnPage) {
- Log.i("cgBase.addToWatchlist: cache is on watchlist");
+ Log.i("GCParser.addToWatchlist: cache is on watchlist");
cache.setOnWatchlist(true);
} else {
- Log.e("cgBase.addToWatchlist: cache is not on watchlist");
+ Log.e("GCParser.addToWatchlist: cache is not on watchlist");
}
return guidOnPage; // on watchlist (=added) / else: error
}
/**
* Removes the cache from the watchlist
- *
+ *
* @param cache
* the cache to remove
* @return <code>false</code> if an error occurred, <code>true</code> otherwise
@@ -1135,7 +1135,7 @@ public abstract class GCParser {
String page = Login.postRequestLogged(uri, null);
if (StringUtils.isBlank(page)) {
- Log.e("cgBase.removeFromWatchlist: No data from server");
+ Log.e("GCParser.removeFromWatchlist: No data from server");
return false; // error
}
@@ -1149,14 +1149,84 @@ public abstract class GCParser {
page = Network.getResponseData(Network.postRequest(uri, params));
boolean guidOnPage = cache.isGuidContainedInPage(page);
if (!guidOnPage) {
- Log.i("cgBase.removeFromWatchlist: cache removed from watchlist");
+ Log.i("GCParser.removeFromWatchlist: cache removed from watchlist");
cache.setOnWatchlist(false);
} else {
- Log.e("cgBase.removeFromWatchlist: cache not removed from watchlist");
+ Log.e("GCParser.removeFromWatchlist: cache not removed from watchlist");
}
return !guidOnPage; // on watchlist (=error) / not on watchlist
}
+ static String requestHtmlPage(final String geocode, final String guid, final String log, final String numlogs) {
+ final Parameters params = new Parameters("decrypt", "y");
+ if (StringUtils.isNotBlank(geocode)) {
+ params.put("wp", geocode);
+ } else if (StringUtils.isNotBlank(guid)) {
+ params.put("guid", guid);
+ }
+ params.put("log", log);
+ params.put("numlogs", numlogs);
+
+ return Login.getRequestLogged("http://www.geocaching.com/seek/cache_details.aspx", params);
+ }
+
+ /**
+ * Adds the cache to the favorites of the user.
+ *
+ * @param cache
+ * the cache to add
+ * @return <code>false</code> if an error occurred, <code>true</code> otherwise
+ */
+ static boolean addToFavorites(final cgCache cache) {
+
+ final String page = requestHtmlPage(cache.getGeocode(), null, "n", "0");
+ final String userToken = BaseUtils.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=true";
+
+ HttpResponse response = Network.postRequest(uri, null);
+
+ if (response != null && response.getStatusLine().getStatusCode() == 200) {
+ Log.i("GCParser.addToFavorites: cache added to favorites");
+ cache.setFavorite(true);
+ cache.setFavoritePoints(cache.getFavoritePoints() + 1);
+ return true;
+ }
+ Log.e("GCParser.addToFavorites: cache not added to favorites");
+ return false;
+ }
+
+ /**
+ * Removes the cache from the Favorites
+ *
+ * @param cache
+ * the cache to remove
+ * @return <code>false</code> if an error occurred, <code>true</code> otherwise
+ */
+ static boolean removeFromFavorites(final cgCache cache) {
+ final String page = requestHtmlPage(cache.getGeocode(), null, "n", "0");
+ final String userToken = BaseUtils.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=false";
+
+ HttpResponse response = Network.postRequest(uri, null);
+
+ if (response != null && response.getStatusLine().getStatusCode() == 200) {
+ Log.i("GCParser.removeFromFavorites: cache removed from favorites");
+ cache.setFavorite(false);
+ cache.setFavoritePoints(cache.getFavoritePoints() - 1);
+ return true;
+ }
+ Log.e("GCParser.removeFromFavorites: cache not removed from favorites");
+ return false;
+ }
+
/**
* Parse a trackable HTML description into a cgTrackable object
*
@@ -1349,9 +1419,9 @@ public abstract class GCParser {
String rawResponse;
if (!getDataFromPage) {
- final Matcher userTokenMatcher = GCConstants.PATTERN_USERTOKEN2.matcher(page);
+ final Matcher userTokenMatcher = GCConstants.PATTERN_USERTOKEN.matcher(page);
if (!userTokenMatcher.find()) {
- Log.e("cgBase.loadLogsFromDetails: unable to extract userToken");
+ Log.e("GCParser.loadLogsFromDetails: unable to extract userToken");
return null;
}
@@ -1366,17 +1436,17 @@ public abstract class GCParser {
final HttpResponse response = Network.getRequest("http://www.geocaching.com/seek/geocache.logbook", params);
if (response == null) {
- Log.e("cgBase.loadLogsFromDetails: cannot log logs, response is null");
+ Log.e("GCParser.loadLogsFromDetails: cannot log logs, response is null");
return null;
}
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != 200) {
- Log.e("cgBase.loadLogsFromDetails: error " + statusCode + " when requesting log information");
+ Log.e("GCParser.loadLogsFromDetails: error " + statusCode + " when requesting log information");
return null;
}
rawResponse = Network.getResponseData(response);
if (rawResponse == null) {
- Log.e("cgBase.loadLogsFromDetails: unable to read whole response");
+ Log.e("GCParser.loadLogsFromDetails: unable to read whole response");
return null;
}
} else {
@@ -1389,7 +1459,7 @@ public abstract class GCParser {
try {
final JSONObject resp = new JSONObject(rawResponse);
if (!resp.getString("status").equals("success")) {
- Log.e("cgBase.loadLogsFromDetails: status is " + resp.getString("status"));
+ Log.e("GCParser.loadLogsFromDetails: status is " + resp.getString("status"));
return null;
}
@@ -1430,7 +1500,7 @@ public abstract class GCParser {
}
} catch (JSONException e) {
// failed to parse logs
- Log.w("cgBase.loadLogsFromDetails: Failed to parse cache logs", e);
+ Log.w("GCParser.loadLogsFromDetails: Failed to parse cache logs", e);
}
return logs;