diff options
6 files changed, 172 insertions, 98 deletions
diff --git a/main/src/cgeo/geocaching/connector/capability/FieldNotesCapability.java b/main/src/cgeo/geocaching/connector/capability/FieldNotesCapability.java new file mode 100644 index 0000000..4da9705 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/capability/FieldNotesCapability.java @@ -0,0 +1,13 @@ +package cgeo.geocaching.connector.capability; + +import cgeo.geocaching.connector.IConnector; + +import java.io.File; + +/** + * Connector interface to implement an upload of (already exported) field notes + * + */ +public interface FieldNotesCapability extends IConnector { + public boolean uploadFieldNotes(final File exportFile); +} diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java index a62b1f6..e90dc16 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java @@ -10,6 +10,7 @@ import cgeo.geocaching.SearchResult; import cgeo.geocaching.connector.AbstractConnector; import cgeo.geocaching.connector.ILoggingManager; import cgeo.geocaching.connector.UserAction; +import cgeo.geocaching.connector.capability.FieldNotesCapability; import cgeo.geocaching.connector.capability.ICredentials; import cgeo.geocaching.connector.capability.ILogin; import cgeo.geocaching.connector.capability.ISearchByCenter; @@ -23,6 +24,7 @@ import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.loaders.RecaptchaReceiver; import cgeo.geocaching.network.Network; +import cgeo.geocaching.network.Parameters; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.settings.SettingsActivity; import cgeo.geocaching.utils.CancellableHandler; @@ -32,6 +34,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; + import rx.util.functions.Action1; import android.content.Context; @@ -39,10 +42,11 @@ import android.content.Intent; import android.net.Uri; import android.os.Handler; +import java.io.File; import java.util.List; import java.util.regex.Pattern; -public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort, ISearchByKeyword, ILogin, ICredentials, ISearchByOwner, ISearchByFinder { +public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort, ISearchByKeyword, ILogin, ICredentials, ISearchByOwner, ISearchByFinder, FieldNotesCapability { private static final String CACHE_URL_SHORT = "http://coord.info/"; // Double slash is used to force open in browser @@ -421,4 +425,40 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, return GCParser.searchByUsername(username, Settings.getCacheType(), Settings.isShowCaptcha(), recaptchaReceiver); } + @Override + public boolean uploadFieldNotes(final File exportFile) { + if (!GCLogin.getInstance().isActualLoginStatus()) { + // no need to upload (possibly large file) if we're not logged in + final StatusCode loginState = GCLogin.getInstance().login(); + if (loginState != StatusCode.NO_ERROR) { + Log.e("FieldnoteExport.ExportTask upload: Login failed"); + } + } + + final String uri = "http://www.geocaching.com/my/uploadfieldnotes.aspx"; + final String page = GCLogin.getInstance().getRequestLogged(uri, null); + + if (StringUtils.isBlank(page)) { + Log.e("FieldnoteExport.ExportTask get page: No data from server"); + return false; + } + + final String[] viewstates = GCLogin.getViewstates(page); + + final Parameters uploadParams = new Parameters( + "__EVENTTARGET", "", + "__EVENTARGUMENT", "", + "ctl00$ContentBody$btnUpload", "Upload Field Note"); + + GCLogin.putViewstates(uploadParams, viewstates); + + Network.getResponseData(Network.postRequest(uri, uploadParams, "ctl00$ContentBody$FieldNoteLoader", "text/plain", exportFile)); + + if (StringUtils.isBlank(page)) { + Log.e("FieldnoteExport.ExportTask upload: No data from server"); + return false; + } + return true; + } + } diff --git a/main/src/cgeo/geocaching/export/FieldNotes.java b/main/src/cgeo/geocaching/export/FieldNotes.java new file mode 100644 index 0000000..11d725a --- /dev/null +++ b/main/src/cgeo/geocaching/export/FieldNotes.java @@ -0,0 +1,65 @@ +package cgeo.geocaching.export; + +import cgeo.geocaching.Geocache; +import cgeo.geocaching.LogEntry; +import cgeo.geocaching.files.LocalStorage; +import cgeo.geocaching.utils.FileUtils; +import cgeo.geocaching.utils.SynchronizedDateFormat; + +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +/** + * Field Notes are simple plain text files, but poorly documented. Syntax:<br> + * <code>GCxxxxx,yyyy-mm-ddThh:mm:ssZ,Found it,"logtext"</code> + */ +class FieldNotes { + + private static final SynchronizedDateFormat FIELD_NOTE_DATE_FORMAT = new SynchronizedDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone("UTC"), Locale.US); + + private int size = 0; + private final StringBuilder buffer = new StringBuilder(); + + void add(final Geocache cache, final LogEntry log) { + size++; + buffer.append(cache.getGeocode()) + .append(',') + .append(FIELD_NOTE_DATE_FORMAT.format(new Date(log.date))) + .append(',') + .append(StringUtils.capitalize(log.type.type)) + .append(",\"") + .append(StringUtils.replaceChars(log.log, '"', '\'')) + .append("\"\n"); + } + + public String getContent() { + return buffer.toString(); + } + + File writeToDirectory(File exportLocation) { + if (!LocalStorage.isExternalStorageAvailable()) { + return null; + } + + FileUtils.mkdirs(exportLocation); + + final SimpleDateFormat fileNameDateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US); + final File exportFile = new File(exportLocation.toString() + '/' + fileNameDateFormat.format(new Date()) + ".txt"); + + if (!FileUtils.writeFileUTF16(exportFile, getContent())) { + return null; + } + + return exportFile; + } + + public int size() { + return size; + } + +} diff --git a/main/src/cgeo/geocaching/export/FieldnoteExport.java b/main/src/cgeo/geocaching/export/FieldnoteExport.java index c4906ac..7d3e07e 100644 --- a/main/src/cgeo/geocaching/export/FieldnoteExport.java +++ b/main/src/cgeo/geocaching/export/FieldnoteExport.java @@ -5,20 +5,13 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; import cgeo.geocaching.activity.ActivityMixin; -import cgeo.geocaching.connector.gc.GCLogin; -import cgeo.geocaching.enumerations.StatusCode; -import cgeo.geocaching.network.Network; -import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.connector.ConnectorFactory; +import cgeo.geocaching.connector.IConnector; +import cgeo.geocaching.connector.capability.FieldNotesCapability; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.Formatter; import cgeo.geocaching.utils.AsyncTaskWithProgress; -import cgeo.geocaching.utils.FileUtils; import cgeo.geocaching.utils.Log; -import cgeo.geocaching.utils.SynchronizedDateFormat; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.CharEncoding; -import org.apache.commons.lang3.StringUtils; import android.app.Activity; import android.app.AlertDialog; @@ -29,29 +22,15 @@ import android.view.ContextThemeWrapper; import android.view.View; import android.widget.CheckBox; -import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.text.SimpleDateFormat; -import java.util.Date; import java.util.List; -import java.util.Locale; -import java.util.TimeZone; /** - * Exports offline logs in the Groundspeak Field Note format.<br> - * <br> - * - * Field Notes are simple plain text files, but poorly documented. Syntax:<br> - * <code>GCxxxxx,yyyy-mm-ddThh:mm:ssZ,Found it,"logtext"</code> + * Exports offline logs in the Groundspeak Field Note format. + * */ class FieldnoteExport extends AbstractExport { private static final File exportLocation = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/field-notes"); - private static final SynchronizedDateFormat fieldNoteDateFormat = new SynchronizedDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone("UTC"), Locale.US); private static int fieldNotesCount = 0; protected FieldnoteExport() { @@ -113,7 +92,7 @@ class FieldnoteExport extends AbstractExport { /** * Instantiates and configures the task for exporting field notes. - * + * * @param activity * optional: Show a progress bar and toasts * @param upload @@ -130,14 +109,24 @@ class FieldnoteExport extends AbstractExport { @Override protected Boolean doInBackgroundInternal(final Geocache[] caches) { - final StringBuilder fieldNoteBuffer = new StringBuilder(); + // export field notes separately for each connector, so the file can be uploaded to the respective site afterwards + for (IConnector connector : ConnectorFactory.getConnectors()) { + if (connector instanceof FieldNotesCapability) { + exportFieldNotes((FieldNotesCapability) connector, caches); + } + } + return true; + } + + private boolean exportFieldNotes(final FieldNotesCapability connector, Geocache[] caches) { + final FieldNotes fieldNotes = new FieldNotes(); try { int i = 0; for (final Geocache cache : caches) { - if (cache.isLogOffline()) { + if (ConnectorFactory.getConnector(cache).equals(connector) && cache.isLogOffline()) { final LogEntry log = DataStore.loadLogOffline(cache.getGeocode()); if (!onlyNew || log.date > Settings.getFieldnoteExportDate()) { - appendFieldNote(fieldNoteBuffer, cache, log); + fieldNotes.add(cache, log); } } publishProgress(++i); @@ -146,65 +135,16 @@ class FieldnoteExport extends AbstractExport { Log.e("FieldnoteExport.ExportTask generation", e); return false; } + fieldNotesCount += fieldNotes.size(); - fieldNoteBuffer.append('\n'); - - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - return false; - } - - FileUtils.mkdirs(exportLocation); - - final SimpleDateFormat fileNameDateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US); - exportFile = new File(exportLocation.toString() + '/' + fileNameDateFormat.format(new Date()) + ".txt"); - - Writer fileWriter = null; - BufferedOutputStream buffer = null; - try { - final OutputStream os = new FileOutputStream(exportFile); - buffer = new BufferedOutputStream(os); - fileWriter = new OutputStreamWriter(buffer, CharEncoding.UTF_16); - fileWriter.write(fieldNoteBuffer.toString()); - } catch (final IOException e) { - Log.e("FieldnoteExport.ExportTask export", e); + exportFile = fieldNotes.writeToDirectory(exportLocation); + if (exportFile == null) { return false; - } finally { - IOUtils.closeQuietly(fileWriter); - IOUtils.closeQuietly(buffer); } if (upload) { publishProgress(STATUS_UPLOAD); - - if (!GCLogin.getInstance().isActualLoginStatus()) { - // no need to upload (possibly large file) if we're not logged in - final StatusCode loginState = GCLogin.getInstance().login(); - if (loginState != StatusCode.NO_ERROR) { - Log.e("FieldnoteExport.ExportTask upload: Login failed"); - } - } - - final String uri = "http://www.geocaching.com/my/uploadfieldnotes.aspx"; - final String page = GCLogin.getInstance().getRequestLogged(uri, null); - - if (StringUtils.isBlank(page)) { - Log.e("FieldnoteExport.ExportTask get page: No data from server"); - return false; - } - - final String[] viewstates = GCLogin.getViewstates(page); - - final Parameters uploadParams = new Parameters( - "__EVENTTARGET", "", - "__EVENTARGUMENT", "", - "ctl00$ContentBody$btnUpload", "Upload Field Note"); - - GCLogin.putViewstates(uploadParams, viewstates); - - Network.getResponseData(Network.postRequest(uri, uploadParams, "ctl00$ContentBody$FieldNoteLoader", "text/plain", exportFile)); - - if (StringUtils.isBlank(page)) { - Log.e("FieldnoteExport.ExportTask upload: No data from server"); + if (!connector.uploadFieldNotes(exportFile)) { return false; } } @@ -237,15 +177,4 @@ class FieldnoteExport extends AbstractExport { } } - static void appendFieldNote(final StringBuilder fieldNoteBuffer, final Geocache cache, final LogEntry log) { - fieldNotesCount++; - fieldNoteBuffer.append(cache.getGeocode()) - .append(',') - .append(fieldNoteDateFormat.format(new Date(log.date))) - .append(',') - .append(StringUtils.capitalize(log.type.type)) - .append(",\"") - .append(StringUtils.replaceChars(log.log, '"', '\'')) - .append("\"\n"); - } } diff --git a/main/src/cgeo/geocaching/utils/FileUtils.java b/main/src/cgeo/geocaching/utils/FileUtils.java index f650216..54553c2 100644 --- a/main/src/cgeo/geocaching/utils/FileUtils.java +++ b/main/src/cgeo/geocaching/utils/FileUtils.java @@ -1,12 +1,20 @@ package cgeo.geocaching.utils; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.CharEncoding; import org.apache.commons.lang3.StringUtils; import android.os.Handler; import android.os.Message; +import java.io.BufferedOutputStream; import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; import java.util.List; /** @@ -126,4 +134,23 @@ public final class FileUtils { } return success; } + + public static boolean writeFileUTF16(File file, String content) { + // TODO: replace by some apache.commons IOUtils or FileUtils code + Writer fileWriter = null; + BufferedOutputStream buffer = null; + try { + final OutputStream os = new FileOutputStream(file); + buffer = new BufferedOutputStream(os); + fileWriter = new OutputStreamWriter(buffer, CharEncoding.UTF_16); + fileWriter.write(content.toString()); + } catch (final IOException e) { + Log.e("FieldnoteExport.ExportTask export", e); + return false; + } finally { + IOUtils.closeQuietly(fileWriter); + IOUtils.closeQuietly(buffer); + } + return true; + } } diff --git a/tests/src/cgeo/geocaching/export/ExportTest.java b/tests/src/cgeo/geocaching/export/ExportTest.java index b8f1ffd..b03c9bb 100644 --- a/tests/src/cgeo/geocaching/export/ExportTest.java +++ b/tests/src/cgeo/geocaching/export/ExportTest.java @@ -20,9 +20,9 @@ public class ExportTest extends CGeoTestCase { final Geocache cache = new Geocache(); cache.setGeocode("GCX1234"); final LogEntry log = new LogEntry(1353244820000L, LogType.FOUND_IT, "Hidden in a tree"); - final StringBuilder logStr = new StringBuilder(); - FieldnoteExport.appendFieldNote(logStr, cache, log); - assertEquals("Non matching export " + logStr.toString(), "GCX1234,2012-11-18T13:20:20Z,Found it,\"Hidden in a tree\"\n", logStr.toString()); + FieldNotes fieldNotes = new FieldNotes(); + fieldNotes.add(cache, log); + assertEquals("Non matching export " + fieldNotes.getContent(), "GCX1234,2012-11-18T13:20:20Z,Found it,\"Hidden in a tree\"\n", fieldNotes.getContent()); } public static void testGpxExportSmilies() throws InterruptedException, ExecutionException { |
