package cgeo.geocaching.export; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; import cgeo.geocaching.Settings; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgData; import cgeo.geocaching.cgWaypoint; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.activity.Progress; import cgeo.geocaching.enumerations.CacheAttribute; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.utils.BaseUtils; import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Intent; import android.net.Uri; import android.os.AsyncTask; import android.os.Environment; import android.view.View; import android.widget.CheckBox; import android.widget.TextView; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Locale; class GpxExport extends AbstractExport { private static final SimpleDateFormat dateFormatZ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); protected GpxExport() { super(getString(R.string.export_gpx)); } @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).execute((Void) null); } else { // Show configuration dialog new ExportOptionsDialog(caches, activity).show(); } } /** * A dialog to allow the user to set options for the export. * * Currently available option is: opening of share menu after successful export */ private class ExportOptionsDialog extends AlertDialog { public ExportOptionsDialog(final List caches, final Activity activity) { super(activity); View layout = activity.getLayoutInflater().inflate(R.layout.gpx_export_dialog, null); setView(layout); final TextView text = (TextView) layout.findViewById(R.id.info); text.setText(getString(R.string.export_gpx_info, Settings.getGpxExportDir())); final CheckBox shareOption = (CheckBox) layout.findViewById(R.id.share); shareOption.setChecked(Settings.getShareAfterExport()); shareOption.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Settings.setShareAfterExport(shareOption.isChecked()); } }); layout.findViewById(R.id.export).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); new ExportTask(caches, activity).execute((Void) null); } }); } } private class ExportTask extends AsyncTask { private final List caches; private final Activity activity; private final Progress progress = new Progress(); private File exportFile; private Writer gpx; /** * Instantiates and configures 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 */ public ExportTask(final List caches, final Activity activity) { this.caches = caches; this.activity = activity; } @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) { // quick check for being able to write the GPX file if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { return false; } try { final File exportLocation = new File(Settings.getGpxExportDir()); exportLocation.mkdirs(); final SimpleDateFormat fileNameDateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US); exportFile = new File(Settings.getGpxExportDir() + File.separatorChar + "export_" + fileNameDateFormat.format(new Date()) + ".gpx"); gpx = new BufferedWriter(new FileWriter(exportFile)); gpx.write(""); gpx.write(""); for (int i = 0; i < caches.size(); i++) { final cgCache cache = cgData.loadCache(caches.get(i).getGeocode(), LoadFlags.LOAD_ALL_DB_ONLY); gpx.write(""); final Date hiddenDate = cache.getHiddenDate(); if (hiddenDate != null) { gpx.write(""); } gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(cache.getGeocode())); gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(cache.getName())); gpx.write(""); gpx.write(""); gpx.write(cache.getUrl()); gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(cache.getName())); gpx.write(""); gpx.write(""); gpx.write(cache.isFound() ? "Geocache Found" : "Geocache"); gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml("Geocache|" + cache.getType().pattern)); gpx.write(""); gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(cache.getName())); gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(cache.getOwnerDisplayName())); gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(cache.getOwnerUserId())); gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(cache.getType().pattern)); gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(cache.getSize().id)); gpx.write(""); writeAttributes(cache); gpx.write(""); gpx.write(Float.toString(cache.getDifficulty())); gpx.write(""); gpx.write(""); gpx.write(Float.toString(cache.getTerrain())); gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(cache.getLocation())); gpx.write(""); gpx.write(""); // c:geo cannot manage 2 separate fields, so we export as country gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(cache.getShortDescription())); gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(cache.getDescription())); gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(cache.getHint())); gpx.write(""); writeLogs(cache); gpx.write(""); gpx.write(""); writeWaypoints(cache); publishProgress(i + 1); } gpx.write(""); gpx.close(); } catch (Exception e) { Log.e("GpxExport.ExportTask export", e); if (gpx != null) { try { gpx.close(); } catch (IOException ee) { } } // delete partial gpx file on error if (exportFile.exists()) { exportFile.delete(); } return false; } return true; } private void writeWaypoints(final cgCache cache) throws IOException { List waypoints = cache.getWaypoints(); List ownWaypoints = new ArrayList(waypoints.size()); List originWaypoints = new ArrayList(waypoints.size()); for (cgWaypoint wp : cache.getWaypoints()) { if (wp.isUserDefined()) { ownWaypoints.add(wp); } else { originWaypoints.add(wp); } } int maxPrefix = 0; for (cgWaypoint wp : originWaypoints) { String prefix = wp.getPrefix(); try { maxPrefix = Math.max(Integer.parseInt(prefix), maxPrefix); } catch (NumberFormatException ex) { Log.e("Unexpected origin waypoint prefix='" + prefix + "'", ex); } writeCacheWaypoint(wp, prefix); } for (cgWaypoint wp : ownWaypoints) { maxPrefix++; String prefix = StringUtils.leftPad(String.valueOf(maxPrefix), 2, '0'); writeCacheWaypoint(wp, prefix); } } /** * Writes one waypoint entry for cache waypoint. * * @param cache * The * @param wp * @param prefix * @throws IOException */ private void writeCacheWaypoint(final cgWaypoint wp, final String prefix) throws IOException { gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(prefix)); gpx.write(StringEscapeUtils.escapeXml(wp.getGeocode().substring(2))); gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(wp.getNote())); gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(wp.getName())); gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(wp.getWaypointType().toString())); //TODO: Correct identifier string gpx.write(""); gpx.write("Waypoint|"); gpx.write(StringEscapeUtils.escapeXml(wp.getWaypointType().toString())); //TODO: Correct identifier string gpx.write(""); gpx.write(""); } private void writeLogs(final cgCache cache) throws IOException { if (cache.getLogs().isEmpty()) { return; } gpx.write(""); for (LogEntry log : cache.getLogs()) { gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(dateFormatZ.format(new Date(log.date)))); gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(log.type.type)); gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(log.author)); gpx.write(""); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(log.log)); gpx.write(""); gpx.write(""); } gpx.write(""); } private void writeAttributes(final cgCache cache) throws IOException { if (cache.getAttributes().isEmpty()) { return; } //TODO: Attribute conversion required: English verbose name, gpx-id gpx.write(""); for (String attribute : cache.getAttributes()) { final CacheAttribute attr = CacheAttribute.getByGcRawName(CacheAttribute.trimAttributeName(attribute)); final boolean enabled = CacheAttribute.isEnabled(attribute); gpx.write(""); gpx.write(StringEscapeUtils.escapeXml(attr.getL10n(enabled))); gpx.write(""); } gpx.write(""); } @Override protected void onPostExecute(Boolean result) { if (null != activity) { progress.dismiss(); if (result) { ActivityMixin.showToast(activity, getName() + ' ' + getString(R.string.export_exportedto) + ": " + exportFile.toString()); if (Settings.getShareAfterExport()) { Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(exportFile)); shareIntent.setType("application/xml"); activity.startActivity(Intent.createChooser(shareIntent, getString(R.string.export_gpx_to))); } } else { ActivityMixin.showToast(activity, getString(R.string.export_failed)); } } } @Override protected void onProgressUpdate(Integer... status) { if (null != activity) { progress.setProgress(status[0]); } } } }