diff options
Diffstat (limited to 'main/src/cgeo/geocaching/export/GpxExport.java')
| -rw-r--r-- | main/src/cgeo/geocaching/export/GpxExport.java | 393 |
1 files changed, 160 insertions, 233 deletions
diff --git a/main/src/cgeo/geocaching/export/GpxExport.java b/main/src/cgeo/geocaching/export/GpxExport.java index 7573db9..d130e1f 100644 --- a/main/src/cgeo/geocaching/export/GpxExport.java +++ b/main/src/cgeo/geocaching/export/GpxExport.java @@ -1,11 +1,11 @@ package cgeo.geocaching.export; +import cgeo.geocaching.Geocache; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; import cgeo.geocaching.Settings; -import cgeo.geocaching.cgCache; -import cgeo.geocaching.cgWaypoint; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.Waypoint; +import cgeo.geocaching.cgData; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.activity.Progress; import cgeo.geocaching.enumerations.CacheAttribute; @@ -13,26 +13,28 @@ import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.utils.BaseUtils; import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.XmlUtils; -import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; +import org.xmlpull.v1.XmlSerializer; import android.app.Activity; import android.app.AlertDialog; +import android.app.Dialog; import android.app.ProgressDialog; +import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.AsyncTask; import android.os.Environment; +import android.util.Xml; 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; @@ -41,76 +43,73 @@ 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); + public static final String PREFIX_XSI = "http://www.w3.org/2001/XMLSchema-instance"; + public static final String PREFIX_GPX = "http://www.topografix.com/GPX/1/0"; + public static final String PREFIX_GROUNDSPEAK = "http://www.groundspeak.com/cache/1/0"; protected GpxExport() { super(getString(R.string.export_gpx)); } @Override - public void export(final List<cgCache> caches, final Activity activity) { + public void export(final List<Geocache> caches, final Activity activity) { if (null == activity) { // No activity given, so no user interaction possible. // Start export with default parameters. - new ExportTask(caches, activity).execute((Void) null); + new ExportTask(caches, null).execute((Void) null); } else { // Show configuration dialog - new ExportOptionsDialog(caches, activity).show(); + getExportDialog(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<cgCache> caches, final Activity activity) { - super(activity); + private Dialog getExportDialog(final List<Geocache> caches, final Activity activity) { + AlertDialog.Builder builder = new AlertDialog.Builder(activity); - View layout = activity.getLayoutInflater().inflate(R.layout.gpx_export_dialog, null); - setView(layout); + View layout = activity.getLayoutInflater().inflate(R.layout.gpx_export_dialog, null); + builder.setView(layout); - final TextView text = (TextView) layout.findViewById(R.id.info); - text.setText(getString(R.string.export_gpx_info, Settings.getGpxExportDir())); + 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); + final CheckBox shareOption = (CheckBox) layout.findViewById(R.id.share); - shareOption.setChecked(Settings.getShareAfterExport()); + shareOption.setChecked(Settings.getShareAfterExport()); - shareOption.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Settings.setShareAfterExport(shareOption.isChecked()); - } - }); + 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); - } - }); - } + builder.setPositiveButton(R.string.export, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + new ExportTask(caches, activity).execute((Void) null); + } + }); + + return builder.create(); } - private class ExportTask extends AsyncTask<Void, Integer, Boolean> { - private final List<cgCache> caches; + private class ExportTask extends AsyncTask<Void, Integer, File> { + private final List<Geocache> 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 + * The {@link List} of {@link cgeo.geocaching.Geocache} to be exported * @param activity * optional: Show a progress bar and toasts */ - public ExportTask(final List<cgCache> caches, final Activity activity) { + public ExportTask(final List<Geocache> caches, final Activity activity) { this.caches = caches; this.activity = activity; } @@ -124,169 +123,122 @@ class GpxExport extends AbstractExport { } @Override - protected Boolean doInBackground(Void... params) { + protected File doInBackground(Void... params) { // quick check for being able to write the GPX file if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - return false; + return null; } + final SimpleDateFormat fileNameDateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US); + final File exportFile = new File(Settings.getGpxExportDir() + File.separatorChar + "export_" + fileNameDateFormat.format(new Date()) + ".gpx"); + FileWriter writer = null; 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("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); - gpx.write("<gpx version=\"1.0\" creator=\"c:geo - http://www.cgeo.org\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://www.topografix.com/GPX/1/0\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.groundspeak.com/cache/1/0/1 http://www.groundspeak.com/cache/1/0/1/cache.xsd\">"); + final XmlSerializer gpx = Xml.newSerializer(); + writer = new FileWriter(exportFile); + gpx.setOutput(writer); + + gpx.startDocument("UTF-8", true); + gpx.setPrefix("", PREFIX_GPX); + gpx.setPrefix("xsi", PREFIX_XSI); + gpx.setPrefix("groundspeak", PREFIX_GROUNDSPEAK); + gpx.startTag(PREFIX_GPX, "gpx"); + gpx.attribute("", "version", "1.0"); + gpx.attribute("", "creator", "c:geo - http://www.cgeo.org/"); + gpx.attribute(PREFIX_XSI, "schemaLocation", + PREFIX_GPX + " http://www.topografix.com/GPX/1/0/gpx.xsd " + + PREFIX_GROUNDSPEAK + " http://www.groundspeak.com/cache/1/0/1/cache.xsd"); for (int i = 0; i < caches.size(); i++) { - // reload the cache. otherwise logs, attributes and other detailed information is not available - final cgCache cache = cgeoapplication.getInstance().loadCache(caches.get(i).getGeocode(), LoadFlags.LOAD_ALL_DB_ONLY); + final Geocache cache = cgData.loadCache(caches.get(i).getGeocode(), LoadFlags.LOAD_ALL_DB_ONLY); - gpx.write("<wpt "); - gpx.write("lat=\""); - gpx.write(Double.toString(cache.getCoords().getLatitude())); - gpx.write("\" "); - gpx.write("lon=\""); - gpx.write(Double.toString(cache.getCoords().getLongitude())); - gpx.write("\">"); + gpx.startTag(PREFIX_GPX, "wpt"); + gpx.attribute("", "lat", Double.toString(cache.getCoords().getLatitude())); + gpx.attribute("", "lon", Double.toString(cache.getCoords().getLongitude())); final Date hiddenDate = cache.getHiddenDate(); if (hiddenDate != null) { - gpx.write("<time>"); - gpx.write(StringEscapeUtils.escapeXml(dateFormatZ.format(hiddenDate))); - gpx.write("</time>"); + XmlUtils.simpleText(gpx, PREFIX_GPX, "time", dateFormatZ.format(hiddenDate)); } - gpx.write("<name>"); - gpx.write(StringEscapeUtils.escapeXml(cache.getGeocode())); - gpx.write("</name>"); - - gpx.write("<desc>"); - gpx.write(StringEscapeUtils.escapeXml(cache.getName())); - gpx.write("</desc>"); - - gpx.write("<url>"); - gpx.write(cache.getUrl()); - gpx.write("</url>"); - - gpx.write("<urlname>"); - gpx.write(StringEscapeUtils.escapeXml(cache.getName())); - gpx.write("</urlname>"); - - gpx.write("<sym>"); - gpx.write(cache.isFound() ? "Geocache Found" : "Geocache"); - gpx.write("</sym>"); - - gpx.write("<type>"); - gpx.write(StringEscapeUtils.escapeXml("Geocache|" + cache.getType().pattern)); - gpx.write("</type>"); - - gpx.write("<groundspeak:cache "); - gpx.write("id=\""); - gpx.write(cache.getCacheId()); - gpx.write("\" available=\""); - gpx.write(!cache.isDisabled() ? "True" : "False"); - gpx.write("\" archived=\""); - gpx.write(cache.isArchived() ? "True" : "False"); - gpx.write("\" "); - gpx.write("xmlns:groundspeak=\"http://www.groundspeak.com/cache/1/0/1\">"); - - gpx.write("<groundspeak:name>"); - gpx.write(StringEscapeUtils.escapeXml(cache.getName())); - gpx.write("</groundspeak:name>"); - - gpx.write("<groundspeak:placed_by>"); - gpx.write(StringEscapeUtils.escapeXml(cache.getOwnerDisplayName())); - gpx.write("</groundspeak:placed_by>"); - - gpx.write("<groundspeak:owner>"); - gpx.write(StringEscapeUtils.escapeXml(cache.getOwnerUserId())); - gpx.write("</groundspeak:owner>"); + XmlUtils.multipleTexts(gpx, PREFIX_GPX, + "name", cache.getGeocode(), + "desc", cache.getName(), + "url", cache.getUrl(), + "urlname", cache.getName(), + "sym", cache.isFound() ? "Geocache Found" : "Geocache", + "type", "Geocache|" + cache.getType().pattern); - gpx.write("<groundspeak:type>"); - gpx.write(StringEscapeUtils.escapeXml(cache.getType().pattern)); - gpx.write("</groundspeak:type>"); + gpx.startTag(PREFIX_GROUNDSPEAK, "cache"); + gpx.attribute("", "id", cache.getCacheId()); + gpx.attribute("", "available", !cache.isDisabled() ? "True" : "False"); + gpx.attribute("", "archives", cache.isArchived() ? "True" : "False"); - gpx.write("<groundspeak:container>"); - gpx.write(StringEscapeUtils.escapeXml(cache.getSize().id)); - gpx.write("</groundspeak:container>"); - writeAttributes(cache); + XmlUtils.multipleTexts(gpx, PREFIX_GROUNDSPEAK, + "name", cache.getName(), + "placed_by", cache.getOwnerDisplayName(), + "owner", cache.getOwnerUserId(), + "type", cache.getType().pattern, + "container", cache.getSize().id, + "difficulty", Float.toString(cache.getDifficulty()), + "terrain", Float.toString(cache.getTerrain()), + "country", cache.getLocation(), + "state", "", + "encoded_hints", cache.getHint()); - gpx.write("<groundspeak:difficulty>"); - gpx.write(Float.toString(cache.getDifficulty())); - gpx.write("</groundspeak:difficulty>"); + writeAttributes(gpx, cache); - gpx.write("<groundspeak:terrain>"); - gpx.write(Float.toString(cache.getTerrain())); - gpx.write("</groundspeak:terrain>"); + gpx.startTag(PREFIX_GROUNDSPEAK, "short_description"); + gpx.attribute("", "html", BaseUtils.containsHtml(cache.getShortDescription()) ? "True" : "False"); + gpx.text(cache.getShortDescription()); + gpx.endTag(PREFIX_GROUNDSPEAK, "short_description"); - gpx.write("<groundspeak:country>"); - gpx.write(StringEscapeUtils.escapeXml(cache.getLocation())); - gpx.write("</groundspeak:country>"); + gpx.startTag(PREFIX_GROUNDSPEAK, "long_description"); + gpx.attribute("", "html", BaseUtils.containsHtml(cache.getDescription()) ? "True" : "False"); + gpx.text(cache.getDescription()); + gpx.endTag(PREFIX_GROUNDSPEAK, "long_description"); - gpx.write("<groundspeak:state></groundspeak:state>"); // c:geo cannot manage 2 separate fields, so we export as country + writeLogs(gpx, cache); - gpx.write("<groundspeak:short_description html=\""); - gpx.write(BaseUtils.containsHtml(cache.getShortDescription()) ? "True" : "False"); - gpx.write("\">"); - gpx.write(StringEscapeUtils.escapeXml(cache.getShortDescription())); - gpx.write("</groundspeak:short_description>"); + gpx.endTag(PREFIX_GROUNDSPEAK, "cache"); + gpx.endTag(PREFIX_GPX, "wpt"); - gpx.write("<groundspeak:long_description html=\""); - gpx.write(BaseUtils.containsHtml(cache.getDescription()) ? "True" : "False"); - gpx.write("\">"); - gpx.write(StringEscapeUtils.escapeXml(cache.getDescription())); - gpx.write("</groundspeak:long_description>"); - - gpx.write("<groundspeak:encoded_hints>"); - gpx.write(StringEscapeUtils.escapeXml(cache.getHint())); - gpx.write("</groundspeak:encoded_hints>"); - - writeLogs(cache); - - gpx.write("</groundspeak:cache>"); - - gpx.write("</wpt>"); - - writeWaypoints(cache); + writeWaypoints(gpx, cache); publishProgress(i + 1); } - gpx.write("</gpx>"); - - gpx.close(); - } catch (Exception e) { + gpx.endTag(PREFIX_GPX, "gpx"); + gpx.endDocument(); + } catch (final IOException e) { Log.e("GpxExport.ExportTask export", e); - if (gpx != null) { + if (writer != null) { try { - gpx.close(); - } catch (IOException ee) { + writer.close(); + } catch (IOException e1) { + // Ignore double error } } - // delete partial gpx file on error if (exportFile.exists()) { exportFile.delete(); } - return false; + return null; } - return true; + return exportFile; } - private void writeWaypoints(final cgCache cache) throws IOException { - List<cgWaypoint> waypoints = cache.getWaypoints(); - List<cgWaypoint> ownWaypoints = new ArrayList<cgWaypoint>(waypoints.size()); - List<cgWaypoint> originWaypoints = new ArrayList<cgWaypoint>(waypoints.size()); - for (cgWaypoint wp : cache.getWaypoints()) { + private void writeWaypoints(final XmlSerializer gpx, final Geocache cache) throws IOException { + List<Waypoint> waypoints = cache.getWaypoints(); + List<Waypoint> ownWaypoints = new ArrayList<Waypoint>(waypoints.size()); + List<Waypoint> originWaypoints = new ArrayList<Waypoint>(waypoints.size()); + for (Waypoint wp : cache.getWaypoints()) { if (wp.isUserDefined()) { ownWaypoints.add(wp); } else { @@ -294,19 +246,19 @@ class GpxExport extends AbstractExport { } } int maxPrefix = 0; - for (cgWaypoint wp : originWaypoints) { + for (Waypoint 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); + writeCacheWaypoint(gpx, wp, prefix); } - for (cgWaypoint wp : ownWaypoints) { + for (Waypoint wp : ownWaypoints) { maxPrefix++; String prefix = StringUtils.leftPad(String.valueOf(maxPrefix), 2, '0'); - writeCacheWaypoint(wp, prefix); + writeCacheWaypoint(gpx, wp, prefix); } } @@ -319,103 +271,78 @@ class GpxExport extends AbstractExport { * @param prefix * @throws IOException */ - private void writeCacheWaypoint(final cgWaypoint wp, final String prefix) throws IOException { - gpx.write("<wpt lat=\""); + private void writeCacheWaypoint(final XmlSerializer gpx, final Waypoint wp, final String prefix) throws IOException { + gpx.startTag(PREFIX_GPX, "wpt"); final Geopoint coords = wp.getCoords(); - gpx.write(coords != null ? Double.toString(coords.getLatitude()) : ""); // TODO: check whether is the best way to handle unknown waypoint coordinates - gpx.write("\" lon=\""); - gpx.write(coords != null ? Double.toString(coords.getLongitude()) : ""); - gpx.write("\">"); - - gpx.write("<name>"); - gpx.write(StringEscapeUtils.escapeXml(prefix)); - gpx.write(StringEscapeUtils.escapeXml(wp.getGeocode().substring(2))); - gpx.write("</name>"); - - gpx.write("<cmt>"); - gpx.write(StringEscapeUtils.escapeXml(wp.getNote())); - gpx.write("</cmt>"); - - gpx.write("<desc>"); - gpx.write(StringEscapeUtils.escapeXml(wp.getName())); - gpx.write("</desc>"); - - gpx.write("<sym>"); - gpx.write(StringEscapeUtils.escapeXml(wp.getWaypointType().toString())); //TODO: Correct identifier string - gpx.write("</sym>"); - - gpx.write("<type>Waypoint|"); - gpx.write(StringEscapeUtils.escapeXml(wp.getWaypointType().toString())); //TODO: Correct identifier string - gpx.write("</type>"); - - gpx.write("</wpt>"); + gpx.attribute("", "lat", coords != null ? Double.toString(coords.getLatitude()) : ""); // TODO: check whether is the best way to handle unknown waypoint coordinates + gpx.attribute("", "lon", coords != null ? Double.toString(coords.getLongitude()) : ""); + XmlUtils.multipleTexts(gpx, PREFIX_GPX, + "name", prefix + wp.getGeocode().substring(2), + "cmt", wp.getNote(), + "desc", wp.getName(), + "sym", wp.getWaypointType().toString(), //TODO: Correct identifier string + "type", "Waypoint|" + wp.getWaypointType().toString()); //TODO: Correct identifier string + gpx.endTag(PREFIX_GPX, "wpt"); } - private void writeLogs(final cgCache cache) throws IOException { + private void writeLogs(final XmlSerializer gpx, final Geocache cache) throws IOException { if (cache.getLogs().isEmpty()) { return; } - gpx.write("<groundspeak:logs>"); + gpx.startTag(PREFIX_GROUNDSPEAK, "logs"); for (LogEntry log : cache.getLogs()) { - gpx.write("<groundspeak:log id=\""); - gpx.write(Integer.toString(log.id)); - gpx.write("\">"); + gpx.startTag(PREFIX_GROUNDSPEAK, "log"); + gpx.attribute("", "id", Integer.toString(log.id)); - gpx.write("<groundspeak:date>"); - gpx.write(StringEscapeUtils.escapeXml(dateFormatZ.format(new Date(log.date)))); - gpx.write("</groundspeak:date>"); + XmlUtils.multipleTexts(gpx, PREFIX_GROUNDSPEAK, + "date", dateFormatZ.format(new Date(log.date)), + "type", log.type.type); - gpx.write("<groundspeak:type>"); - gpx.write(StringEscapeUtils.escapeXml(log.type.type)); - gpx.write("</groundspeak:type>"); + gpx.startTag(PREFIX_GROUNDSPEAK, "finder"); + gpx.attribute("", "id", log.author); + gpx.endTag(PREFIX_GROUNDSPEAK, "finder"); - gpx.write("<groundspeak:finder id=\"\">"); - gpx.write(StringEscapeUtils.escapeXml(log.author)); - gpx.write("</groundspeak:finder>"); + gpx.startTag(PREFIX_GROUNDSPEAK, "text"); + gpx.attribute("", "encoded", "False"); + gpx.text(log.log); + gpx.endTag(PREFIX_GROUNDSPEAK, "text"); - gpx.write("<groundspeak:text encoded=\"False\">"); - gpx.write(StringEscapeUtils.escapeXml(log.log)); - gpx.write("</groundspeak:text>"); - - gpx.write("</groundspeak:log>"); + gpx.endTag(PREFIX_GROUNDSPEAK, "log"); } - gpx.write("</groundspeak:logs>"); + gpx.endTag(PREFIX_GROUNDSPEAK, "logs"); } - private void writeAttributes(final cgCache cache) throws IOException { + private void writeAttributes(final XmlSerializer gpx, final Geocache cache) throws IOException { if (cache.getAttributes().isEmpty()) { return; } //TODO: Attribute conversion required: English verbose name, gpx-id - gpx.write("<groundspeak:attributes>"); + gpx.startTag(PREFIX_GROUNDSPEAK, "attributes"); for (String attribute : cache.getAttributes()) { - final CacheAttribute attr = CacheAttribute.getByGcRawName(CacheAttribute.trimAttributeName(attribute)); + final CacheAttribute attr = CacheAttribute.getByRawName(CacheAttribute.trimAttributeName(attribute)); + if (attr == null) { + continue; + } final boolean enabled = CacheAttribute.isEnabled(attribute); - gpx.write("<groundspeak:attribute id=\""); - gpx.write(Integer.toString(attr.id)); - gpx.write("\" inc=\""); - if (enabled) { - gpx.write('1'); - } else { - gpx.write('0'); - } - gpx.write("\">"); - gpx.write(StringEscapeUtils.escapeXml(attr.getL10n(enabled))); - gpx.write("</groundspeak:attribute>"); + gpx.startTag(PREFIX_GROUNDSPEAK, "attribute"); + gpx.attribute("", "id", Integer.toString(attr.gcid)); + gpx.attribute("", "inc", enabled ? "1" : "0"); + gpx.text(attr.getL10n(enabled)); + gpx.endTag(PREFIX_GROUNDSPEAK, "attribute"); } - gpx.write("</groundspeak:attributes>"); + gpx.endTag(PREFIX_GROUNDSPEAK, "attributes"); } @Override - protected void onPostExecute(Boolean result) { + protected void onPostExecute(final File exportFile) { if (null != activity) { progress.dismiss(); - if (result) { + if (exportFile != null) { ActivityMixin.showToast(activity, getName() + ' ' + getString(R.string.export_exportedto) + ": " + exportFile.toString()); if (Settings.getShareAfterExport()) { Intent shareIntent = new Intent(); |
