aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcampbeb <bpcampbell@gmail.com>2013-01-16 21:04:35 -1000
committercampbeb <bpcampbell@gmail.com>2013-01-16 21:04:35 -1000
commitcf0ed3e3a1449115705bd4443990df7e4aa152d4 (patch)
tree80ca3f723c5bcd3adb0e1982c8e3b6d877f7dfad
parent884a60814d1a72ca30e9e9b0e31d59e895b2aca0 (diff)
downloadcgeo-cf0ed3e3a1449115705bd4443990df7e4aa152d4.zip
cgeo-cf0ed3e3a1449115705bd4443990df7e4aa152d4.tar.gz
cgeo-cf0ed3e3a1449115705bd4443990df7e4aa152d4.tar.bz2
Fix #6 - Support uploading images with cache logs
-rw-r--r--main/AndroidManifest.xml5
-rw-r--r--main/res/layout/visit.xml12
-rw-r--r--main/res/values/strings.xml8
-rw-r--r--main/src/cgeo/geocaching/VisitCacheActivity.java85
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCConstants.java2
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCParser.java96
-rw-r--r--main/src/cgeo/geocaching/network/Network.java4
7 files changed, 195 insertions, 17 deletions
diff --git a/main/AndroidManifest.xml b/main/AndroidManifest.xml
index d1a1e47..43c9ab2 100644
--- a/main/AndroidManifest.xml
+++ b/main/AndroidManifest.xml
@@ -10,6 +10,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-feature android:name="android.hardware.camera" android:required="false"/>
<supports-screens
android:largeScreens="true"
android:normalScreens="true"
@@ -232,5 +233,9 @@
<activity android:name=".files.SimpleDirChooser"
android:label="@string/app_name">
</activity>
+ <activity
+ android:name=".ImageSelectActivity"
+ android:label="@string/app_name">
+ </activity>
</application>
</manifest>
diff --git a/main/res/layout/visit.xml b/main/res/layout/visit.xml
index e88c21b..33c1af6 100644
--- a/main/res/layout/visit.xml
+++ b/main/res/layout/visit.xml
@@ -75,6 +75,18 @@
android:textColor="?text_color"
android:text="@string/visit_tweet" />
</LinearLayout>
+ <ImageView
+ android:id="@+id/image_preview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="5dip"
+ android:layout_marginBottom="5dip"
+ android:background="#000000"
+ android:padding="1dp"
+ android:visibility="gone"/>
+ <Button style="@style/button_full"
+ android:id="@+id/image_btn"
+ android:text="@string/log_image_attach" />
<Button style="@style/button_full"
android:id="@+id/post"
android:text="@string/log_post" />
diff --git a/main/res/values/strings.xml b/main/res/values/strings.xml
index 9831059..601f874 100644
--- a/main/res/values/strings.xml
+++ b/main/res/values/strings.xml
@@ -85,6 +85,7 @@
<string name="log_tb_changeall">Change All</string>
<string name="log_save">Save</string>
<string name="log_saving">Sending log…</string>
+ <string name="log_saving_and_uploading">Sending log and uploading image…</string>
<string name="log_clear">Clear</string>
<string name="log_post">Submit Log</string>
<string name="log_post_rate">Submit Log &amp; Rate</string>
@@ -118,6 +119,13 @@
<string name="log_today">Today</string>
<string name="log_yesterday">Yesterday</string>
<string name="log_smilies">Smilies</string>
+ <string name="log_image">Image</string>
+ <string name="log_image_attach">Attach Image</string>
+ <string name="log_image_edit">Edit Image</string>
+ <string name="log_image_stored">Existing</string>
+ <string name="log_image_camera">New</string>
+ <string name="log_image_caption">Caption</string>
+ <string name="log_image_description">Description</string>
<!-- translation -->
<string name="translate_to_sys_lang">Translate to %s</string>
diff --git a/main/src/cgeo/geocaching/VisitCacheActivity.java b/main/src/cgeo/geocaching/VisitCacheActivity.java
index a29256c..760d992 100644
--- a/main/src/cgeo/geocaching/VisitCacheActivity.java
+++ b/main/src/cgeo/geocaching/VisitCacheActivity.java
@@ -18,6 +18,7 @@ import cgeo.geocaching.utils.LogTemplateProvider.LogContext;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
@@ -26,6 +27,7 @@ import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -57,6 +59,11 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
private static final String SAVED_STATE_RATING = "cgeo.geocaching.saved_state_rating";
private static final String SAVED_STATE_TYPE = "cgeo.geocaching.saved_state_type";
private static final String SAVED_STATE_DATE = "cgeo.geocaching.saved_state_date";
+ private static final String SAVED_STATE_IMAGE_CAPTION = "cgeo.geocaching.saved_state_image_caption";
+ private static final String SAVED_STATE_IMAGE_DESCRIPTION = "cgeo.geocaching.saved_state_image_description";
+ private static final String SAVED_STATE_IMAGE_URI = "cgeo.geocaching.saved_state_image_uri";
+
+ private static final int SELECT_IMAGE = 101;
private LayoutInflater inflater = null;
private cgCache cache = null;
@@ -77,6 +84,9 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
private double rating;
private LogType typeSelected;
private Calendar date;
+ private String imageCaption;
+ private String imageDescription;
+ private Uri imageUri;
@Override
public Loader<String> onCreateLoader(final int id, final Bundle args) {
@@ -297,6 +307,9 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
rating = savedInstanceState.getDouble(SAVED_STATE_RATING);
typeSelected = LogType.getById(savedInstanceState.getInt(SAVED_STATE_TYPE));
date.setTimeInMillis(savedInstanceState.getLong(SAVED_STATE_DATE));
+ imageCaption = savedInstanceState.getString(SAVED_STATE_IMAGE_CAPTION);
+ imageDescription = savedInstanceState.getString(SAVED_STATE_IMAGE_DESCRIPTION);
+ imageUri = Uri.parse(savedInstanceState.getString(SAVED_STATE_IMAGE_URI));
} else {
// If log had been previously saved, load it now, otherwise initialize signature as needed
final LogEntry log = cgData.loadLogOffline(geocode);
@@ -334,6 +347,15 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
tweetCheck.setChecked(true);
+ final Button imageButton = (Button) findViewById(R.id.image_btn);
+ imageButton.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View view) {
+ selectImage();
+ }
+ });
+
final Button saveButton = (Button) findViewById(R.id.save);
saveButton.setOnClickListener(new View.OnClickListener() {
@@ -374,6 +396,9 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
}
}
text = null;
+ imageCaption = "";
+ imageDescription = "";
+ imageUri = Uri.EMPTY;
}
private void clearLog() {
@@ -454,6 +479,8 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
outState.putDouble(SAVED_STATE_RATING, rating);
outState.putInt(SAVED_STATE_TYPE, typeSelected.id);
outState.putLong(SAVED_STATE_DATE, date.getTimeInMillis());
+ outState.putString(SAVED_STATE_IMAGE_URI, imageUri.getPath());
+ Log.d("saved state");
}
@Override
@@ -497,7 +524,8 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
private class PostListener implements View.OnClickListener {
@Override
public void onClick(View arg0) {
- waitDialog = ProgressDialog.show(VisitCacheActivity.this, null, res.getString(R.string.log_saving), true);
+ waitDialog = ProgressDialog.show(VisitCacheActivity.this, null,
+ res.getString(StringUtils.isBlank(imageUri.getPath()) ? R.string.log_saving : R.string.log_saving_and_uploading), true);
waitDialog.setCancelable(true);
final Thread thread = new PostLogThread(postLogHandler, currentLogText());
@@ -525,11 +553,19 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
public StatusCode postLogFn(String log) {
try {
- final StatusCode status = GCParser.postLog(geocode, cacheid, viewstates, typeSelected,
+ // test call only
+ // if (imageUri != null) {
+ // final StatusCode status = GCParser.uploadLogImage("289163155", imageUri);
+ // if (status == StatusCode.LOG_POST_ERROR) {
+ // return status;
+ // }
+ // }
+
+ final ImmutablePair<StatusCode, String> logResult = GCParser.postLog(geocode, cacheid, viewstates, typeSelected,
date.get(Calendar.YEAR), (date.get(Calendar.MONTH) + 1), date.get(Calendar.DATE),
log, trackables);
- if (status == StatusCode.NO_ERROR) {
+ if (logResult.left == StatusCode.NO_ERROR) {
final LogEntry logNow = new LogEntry(date, typeSelected, log);
cache.getLogs().prepend(logNow);
@@ -541,21 +577,25 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
cgData.saveChangedCache(cache);
}
- if (status == StatusCode.NO_ERROR) {
+ if (logResult.left == StatusCode.NO_ERROR) {
cgData.clearLogOffline(geocode);
}
- if (status == StatusCode.NO_ERROR && typeSelected == LogType.FOUND_IT && Settings.isUseTwitter()
+ if (logResult.left == StatusCode.NO_ERROR && typeSelected == LogType.FOUND_IT && Settings.isUseTwitter()
&& Settings.isTwitterLoginValid()
&& tweetCheck.isChecked() && tweetBox.getVisibility() == View.VISIBLE) {
Twitter.postTweetCache(geocode);
}
- if (status == StatusCode.NO_ERROR && typeSelected == LogType.FOUND_IT && Settings.isGCvoteLogin()) {
+ if (logResult.left == StatusCode.NO_ERROR && typeSelected == LogType.FOUND_IT && Settings.isGCvoteLogin()) {
GCVote.setRating(cache, rating);
}
- return status;
+ if (StringUtils.isNotBlank(imageUri.getPath())) {
+ final StatusCode status = GCParser.uploadLogImage(logResult.right, imageCaption, imageDescription, imageUri);
+ }
+
+ return logResult.left;
} catch (Exception e) {
Log.e("cgeovisit.postLogFn", e);
}
@@ -651,4 +691,35 @@ public class VisitCacheActivity extends AbstractLoggingActivity implements DateD
alert.create().show();
}
+ private void selectImage() {
+ Intent selectImageIntent = new Intent(this, ImageSelectActivity.class);
+ selectImageIntent.putExtra(ImageSelectActivity.EXTRAS_CAPTION, imageCaption);
+ selectImageIntent.putExtra(ImageSelectActivity.EXTRAS_DESCRIPTION, imageDescription);
+ selectImageIntent.putExtra(ImageSelectActivity.EXTRAS_URI_AS_STRING, imageUri.toString());
+
+ startActivityForResult(selectImageIntent, SELECT_IMAGE);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == SELECT_IMAGE) {
+ if (resultCode == RESULT_OK) {
+ imageCaption = data.getStringExtra(ImageSelectActivity.EXTRAS_CAPTION);
+ imageDescription = data.getStringExtra(ImageSelectActivity.EXTRAS_DESCRIPTION);
+ imageUri = Uri.parse(data.getStringExtra(ImageSelectActivity.EXTRAS_URI_AS_STRING));
+ // Image captured and saved to fileUri specified in the Intent
+ showToast("Image saved to:\n" + imageUri);
+ } else if (resultCode == RESULT_CANCELED) {
+ // User cancelled the image capture
+ showToast("Cancelled");
+ } else {
+ // Image capture failed, advise user
+ showToast("Unknown Error");
+ }
+ final Button imageButton = (Button) findViewById(R.id.image_btn);
+ imageButton.setText(StringUtils.isNotBlank(imageUri.getPath()) ?
+ res.getString(R.string.log_image_edit) : res.getString(R.string.log_image_attach));
+
+ }
+ }
}
diff --git a/main/src/cgeo/geocaching/connector/gc/GCConstants.java b/main/src/cgeo/geocaching/connector/gc/GCConstants.java
index 282c88c..e67f6a8 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCConstants.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCConstants.java
@@ -163,6 +163,8 @@ public final class GCConstants {
public final static Pattern PATTERN_USERSESSION = Pattern.compile("UserSession\\('([^']+)'");
public final static Pattern PATTERN_SESSIONTOKEN = Pattern.compile("sessionToken:'([^']+)'");
+ public final static Pattern PATTERN_LOG_IMAGE_UPLOAD = Pattern.compile("/seek/upload\\.aspx\\?LID=(\\d+)", Pattern.CASE_INSENSITIVE);
+
public final static String STRING_PREMIUMONLY_2 = "Sorry, the owner of this listing has made it viewable to Premium Members only.";
public final static String STRING_PREMIUMONLY_1 = "has chosen to make this cache listing visible to Premium Members only.";
public final static String STRING_UNPUBLISHED_OWNER = "cache has not been published yet";
diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java
index 274ba0d..908c21a 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCParser.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java
@@ -1,5 +1,6 @@
package cgeo.geocaching.connector.gc;
+import cgeo.geocaching.Image;
import cgeo.geocaching.LogEntry;
import cgeo.geocaching.R;
import cgeo.geocaching.SearchResult;
@@ -9,7 +10,6 @@ import cgeo.geocaching.TrackableLog;
import cgeo.geocaching.Waypoint;
import cgeo.geocaching.cgCache;
import cgeo.geocaching.cgData;
-import cgeo.geocaching.Image;
import cgeo.geocaching.cgeoapplication;
import cgeo.geocaching.enumerations.CacheSize;
import cgeo.geocaching.enumerations.CacheType;
@@ -39,6 +39,7 @@ import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -46,6 +47,7 @@ import org.json.JSONObject;
import android.net.Uri;
import android.text.Html;
+import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -918,17 +920,17 @@ public abstract class GCParser {
return trackable;
}
- public static StatusCode postLog(final String geocode, final String cacheid, final String[] viewstates,
+ public static ImmutablePair<StatusCode, String> postLog(final String geocode, final String cacheid, final String[] viewstates,
final LogType logType, final int year, final int month, final int day,
final String log, final List<TrackableLog> trackables) {
if (Login.isEmpty(viewstates)) {
Log.e("GCParser.postLog: No viewstate given");
- return StatusCode.LOG_POST_ERROR;
+ return new ImmutablePair<StatusCode, String>(StatusCode.LOG_POST_ERROR, "");
}
if (StringUtils.isBlank(log)) {
Log.e("GCParser.postLog: No log text given");
- return StatusCode.NO_LOG_TEXT;
+ return new ImmutablePair<StatusCode, String>(StatusCode.NO_LOG_TEXT, "");
}
// fix log (non-Latin characters converted to HTML entities)
@@ -987,7 +989,7 @@ public abstract class GCParser {
String page = Login.postRequestLogged(uri, params);
if (!Login.getLoginStatus(page)) {
Log.e("GCParser.postLogTrackable: Can not log in geocaching");
- return StatusCode.NOT_LOGGED_IN;
+ return new ImmutablePair<StatusCode, String>(StatusCode.NOT_LOGGED_IN, "");
}
// maintenance, archived needs to be confirmed
@@ -1000,7 +1002,7 @@ public abstract class GCParser {
if (Login.isEmpty(viewstatesConfirm)) {
Log.e("GCParser.postLog: No viewstate for confirm log");
- return StatusCode.LOG_POST_ERROR;
+ return new ImmutablePair<StatusCode, String>(StatusCode.LOG_POST_ERROR, "");
}
params.clear();
@@ -1054,16 +1056,94 @@ public abstract class GCParser {
if (Login.getActualCachesFound() >= 0) {
Login.setActualCachesFound(Login.getActualCachesFound() + 1);
}
- return StatusCode.NO_ERROR;
+
+ final String logID = BaseUtils.getMatch(page, GCConstants.PATTERN_LOG_IMAGE_UPLOAD, "");
+
+ return new ImmutablePair<StatusCode, String>(StatusCode.NO_ERROR, logID);
}
} catch (Exception e) {
Log.e("GCParser.postLog.check", e);
}
Log.e("GCParser.postLog: Failed to post log because of unknown error");
- return StatusCode.LOG_POST_ERROR;
+ return new ImmutablePair<StatusCode, String>(StatusCode.LOG_POST_ERROR, "");
}
+ /**
+ * Upload an image to a log that has already been posted
+ *
+ * @param logId
+ * the ID of the log to upload the image to. Found on page returned when log is uploaded
+ * @param imageUri
+ * the URI for the image to be uploaded
+ * @return status code to indicate success or failure
+ */
+ public static StatusCode uploadLogImage(final String logId, final String caption, final String description, final Uri imageUri)
+ {
+ final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/seek/upload.aspx").encodedQuery("LID=" + logId).build().toString();
+
+ String page = Network.getResponseData(Network.getRequest(uri));
+
+ if (!Login.getLoginStatus(page)) {
+ // Login.isActualLoginStatus() was wrong, we are not logged in
+ final StatusCode loginState = Login.login();
+ if (loginState == StatusCode.NO_ERROR) {
+ page = Network.getResponseData(Network.getRequest(uri));
+ } else {
+ Log.e("xxx upload: No login (error: " + loginState + ')');
+ return StatusCode.NOT_LOGGED_IN;
+ }
+ }
+
+ final String[] viewstates = Login.getViewstates(page);
+
+ final Parameters uploadParams = new Parameters(
+ "__EVENTTARGET", "",
+ "__EVENTARGUMENT", "",
+ "ctl00$ContentBody$ImageUploadControl1$uxFileCaption", caption,
+ "ctl00$ContentBody$ImageUploadControl1$uxFileDesc", description,
+ "ctl00$ContentBody$ImageUploadControl1$uxUpload", "Upload");
+ Login.putViewstates(uploadParams, viewstates);
+
+ final File image = new File(imageUri.getPath());
+ final String response = Network.getResponseData(Network.postRequest(uri, uploadParams, "ctl00$ContentBody$ImageUploadControl1$uxFileUpload", "image/jpeg", image));
+
+ //TODO: check response and return correct error codes
+
+ /*
+ * String page = Login.postRequestLogged(uri, params);
+ * if (!Login.getLoginStatus(page)) {
+ * Log.e("GCParser.postLogTrackable: Can not log in geocaching");
+ * return StatusCode.NOT_LOGGED_IN;
+ * }
+ *
+ * try {
+ *
+ * final MatcherWrapper matcherOk = new MatcherWrapper(GCConstants.PATTERN_OK1, page);
+ * if (matcherOk.find()) {
+ * Log.i("Log successfully posted to cache #" + cacheid);
+ *
+ * if (geocode != null) {
+ * cgData.saveVisitDate(geocode);
+ * }
+ *
+ * Login.getLoginStatus(page);
+ * // the log-successful-page contains still the old value
+ * if (Login.getActualCachesFound() >= 0) {
+ * Login.setActualCachesFound(Login.getActualCachesFound() + 1);
+ * }
+ *
+ * final String logID = BaseUtils.getMatch(page, GCConstants.PATTERN_LOG_IMAGE_UPLOAD, "");
+ *
+ * return StatusCode.NO_ERROR;
+ * }
+ * } catch (Exception e) {
+ * Log.e("GCParser.postLog.check", e);
+ * }
+ */
+ Log.e("GCParser.postLog: Failed to post log because of unknown error");
+ return StatusCode.LOG_POST_ERROR;
+ }
public static StatusCode postLogTrackable(final String tbid, final String trackingCode, final String[] viewstates,
final LogType logType, final int year, final int month, final int day, final String log) {
if (Login.isEmpty(viewstates)) {
diff --git a/main/src/cgeo/geocaching/network/Network.java b/main/src/cgeo/geocaching/network/Network.java
index 2affab4..2545f4d 100644
--- a/main/src/cgeo/geocaching/network/Network.java
+++ b/main/src/cgeo/geocaching/network/Network.java
@@ -291,9 +291,9 @@ public abstract class Network {
final String timeSpan = Network.formatTimeSpan(before);
final String tries = (i + 1) + "/" + (Network.NB_DOWNLOAD_RETRIES + 1);
if (i == Network.NB_DOWNLOAD_RETRIES) {
- Log.e("Failure " + tries + timeSpan + reqLogStr, e);
+ Log.w("Failure " + tries + timeSpan + reqLogStr + " (" + e.toString() + ")");
} else {
- Log.e("Failure " + tries + " (" + e.toString() + ")" + timeSpan + "- retrying " + reqLogStr);
+ Log.w("Failure " + tries + " (" + e.toString() + ")" + timeSpan + "- retrying " + reqLogStr);
}
}
}