package cgeo.geocaching.export; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.activity.Progress; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.os.AsyncTask; import android.os.Environment; import android.view.View; import android.widget.Button; import android.widget.CheckBox; 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.HashMap; import java.util.List; import java.util.Map; /** * Exports offline-logs in the Groundspeak Field Note format.
*
* * Field Notes are simple plain text files, but poorly documented. Syntax:
* GCxxxxx,yyyy-mm-ddThh:mm:ssZ,Found it,"logtext" */ class FieldnoteExport extends AbstractExport { private static final File exportLocation = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/field-notes"); private static final SimpleDateFormat fieldNoteDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); protected FieldnoteExport() { super(getString(R.string.export_fieldnotes)); } /** * A dialog to allow the user to set options for the export. * * Currently available options are: upload field notes, only new logs since last export/upload */ private class ExportOptionsDialog extends AlertDialog { public ExportOptionsDialog(final List caches, final Activity activity) { super(activity); View layout = activity.getLayoutInflater().inflate(R.layout.fieldnote_export_dialog, null); setView(layout); ((Button) layout.findViewById(R.id.export)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); new ExportTask( caches, activity, ((CheckBox) findViewById(R.id.upload)).isChecked(), ((CheckBox) findViewById(R.id.onlynew)).isChecked()) .execute((Void) null); } }); } } @Override public void export(final List caches, final Activity activity) { if (null == activity) { // No activity given, so no user interaction possible. // Start export with default parameters. new ExportTask(caches, null, false, false).execute((Void) null); } else { // Show configuration dialog new ExportOptionsDialog(caches, activity).show(); } } private class ExportTask extends AsyncTask { private final List caches; private final Activity activity; private final boolean onlyNew; private final Progress progress = new Progress(); private File exportFile; private static final int STATUS_UPLOAD = -1; /** * Instantiates and configurates the task for exporting field notes. * * @param caches * The {@link List} of {@link cgCache} to be exported * @param activity * optional: Show a progress bar and toasts * @param upload * Upload the Field Note to geocaching.com * @param onlyNew * Upload/export only new logs since last export */ public ExportTask(final List caches, final Activity activity, final boolean upload, final boolean onlyNew) { this.caches = caches; this.activity = activity; this.onlyNew = onlyNew; } @Override protected void onPreExecute() { if (null != activity) { progress.show(activity, null, getString(R.string.export) + ": " + getName(), ProgressDialog.STYLE_HORIZONTAL, null); progress.setMaxProgressAndReset(caches.size()); } } @Override protected Boolean doInBackground(Void... params) { final StringBuilder fieldNoteBuffer = new StringBuilder(); // We need our own HashMap because LogType will give us localized and maybe // different strings than gc.com expects in the field note // We only need such logtypes that are possible to log via c:geo Map logTypes = new HashMap(); logTypes.put(LogType.LOG_FOUND_IT, "Found it"); logTypes.put(LogType.LOG_DIDNT_FIND_IT, "Didn't find it"); logTypes.put(LogType.LOG_NOTE, "Write Note"); logTypes.put(LogType.LOG_NEEDS_ARCHIVE, "Needs archived"); logTypes.put(LogType.LOG_NEEDS_MAINTENANCE, "Needs Maintenance"); logTypes.put(LogType.LOG_WILL_ATTEND, "Will Attend"); logTypes.put(LogType.LOG_ATTENDED, "Attended"); logTypes.put(LogType.LOG_WEBCAM_PHOTO_TAKEN, "Webcam Photo Taken"); for (int i = 0; i < caches.size(); i++) { try { final cgCache cache = caches.get(i); if (cache.isLogOffline()) { LogEntry log = cgeoapplication.getInstance().loadLogOffline(cache.getGeocode()); if (null != logTypes.get(log.type)) { fieldNoteBuffer.append(cache.getGeocode()) .append(',') .append(fieldNoteDateFormat.format(new Date(log.date))) .append(',') .append(logTypes.get(log.type)) .append(",\"") .append(StringUtils.replaceChars(log.log, '"', '\'')) .append("\"\n"); } } publishProgress(i + 1); } catch (Exception e) { Log.e("FieldnoteExport.ExportTask generation", e); return false; } } fieldNoteBuffer.append("\n"); if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { exportLocation.mkdirs(); SimpleDateFormat fileNameDateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); exportFile = new File(exportLocation.toString() + '/' + fileNameDateFormat.format(new Date()) + ".txt"); OutputStream os = null; Writer fw = null; try { os = new FileOutputStream(exportFile); fw = new OutputStreamWriter(os, "ISO-8859-1"); // TODO: gc.com doesn't support UTF-8 fw.write(fieldNoteBuffer.toString()); } catch (IOException e) { Log.e("FieldnoteExport.ExportTask export", e); return false; } finally { if (fw != null) { try { fw.close(); } catch (IOException e) { Log.e("FieldnoteExport.ExportTask export", e); return false; } } } } else { return false; } /* * if (upload) { * TODO Use multipart POST request for uploading * publishProgress(STATUS_UPLOAD); * * final Parameters uploadParams = new Parameters( * "__EVENTTARGET", "", * "__EVENTARGUMENT", "", * "__VIEWSTATE", "", * //TODO "ctl00$ContentBody$chkSuppressDate", "on", * "ctl00$ContentBody$FieldNoteLoader", fieldNoteBuffer.toString(), * "ctl00$ContentBody$btnUpload", "Upload Field Note"); * final String uri = "http://www.geocaching.com/my/uploadfieldnotes.aspx"; * * String page = Network.getResponseData(Network.postRequest(uri, uploadParams)); * if (!Login.getLoginStatus(page)) { * final StatusCode loginState = Login.login(); * if (loginState == StatusCode.NO_ERROR) { * page = Network.getResponseData(Network.postRequest(uri, uploadParams)); * } else { * Log.e(Settings.tag, "FieldnoteExport.ExportTask upload: No login (error: " + loginState + ")"); * return false; * } * } * * if (StringUtils.isBlank(page)) { * Log.e(Settings.tag, "FieldnoteExport.ExportTask upload: No data from server"); * return false; * } * } */ return true; } @Override protected void onPostExecute(Boolean result) { if (null != activity) { progress.dismiss(); if (result) { if (onlyNew) { // update last export time in settings } ActivityMixin.showToast(activity, getName() + ' ' + getString(R.string.export_exportedto) + ": " + exportFile.toString()); } else { ActivityMixin.showToast(activity, getString(R.string.export_failed)); } } } @Override protected void onProgressUpdate(Integer... status) { if (null != activity) { if (STATUS_UPLOAD == status[0]) { progress.setMessage(getString(R.string.export_fieldnotes_uploading)); } else { progress.setProgress(status[0]); } } } } }