1 package tim.prune.save;
3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 import java.awt.FlowLayout;
6 import java.awt.GridLayout;
7 import java.awt.event.ActionEvent;
8 import java.awt.event.ActionListener;
10 import java.io.FileOutputStream;
11 import java.io.IOException;
12 import java.io.OutputStreamWriter;
13 import java.io.Writer;
14 import java.nio.charset.Charset;
16 import javax.swing.BorderFactory;
17 import javax.swing.Box;
18 import javax.swing.BoxLayout;
19 import javax.swing.JButton;
20 import javax.swing.JCheckBox;
21 import javax.swing.JDialog;
22 import javax.swing.JFileChooser;
23 import javax.swing.JFrame;
24 import javax.swing.JLabel;
25 import javax.swing.JOptionPane;
26 import javax.swing.JPanel;
27 import javax.swing.JTextField;
30 import tim.prune.GenericFunction;
31 import tim.prune.GpsPruner;
32 import tim.prune.I18nManager;
33 import tim.prune.UpdateMessageBroker;
34 import tim.prune.config.Config;
35 import tim.prune.data.Altitude;
36 import tim.prune.data.AudioFile;
37 import tim.prune.data.Coordinate;
38 import tim.prune.data.DataPoint;
39 import tim.prune.data.Field;
40 import tim.prune.data.MediaFile;
41 import tim.prune.data.Photo;
42 import tim.prune.data.Timestamp;
43 import tim.prune.data.TrackInfo;
44 import tim.prune.gui.DialogCloser;
45 import tim.prune.load.GenericFileFilter;
46 import tim.prune.save.xml.GpxCacherList;
50 * Class to export track information
51 * into a specified Gpx file
53 public class GpxExporter extends GenericFunction implements Runnable
55 private TrackInfo _trackInfo = null;
56 private JDialog _dialog = null;
57 private JTextField _nameField = null;
58 private JTextField _descriptionField = null;
59 private PointTypeSelector _pointTypeSelector = null;
60 private JCheckBox _timestampsCheckbox = null;
61 private JCheckBox _copySourceCheckbox = null;
62 private File _exportFile = null;
64 /** this program name */
65 private static final String GPX_CREATOR = "Prune v" + GpsPruner.VERSION_NUMBER + " activityworkshop.net";
70 * @param inApp app object
72 public GpxExporter(App inApp)
75 _trackInfo = inApp.getTrackInfo();
79 public String getNameKey() {
80 return "function.exportgpx";
84 * Show the dialog to select options and export file
91 _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
92 _dialog.setLocationRelativeTo(_parentFrame);
93 _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
94 _dialog.getContentPane().add(makeDialogComponents());
97 _pointTypeSelector.init(_app.getTrackInfo());
98 _dialog.setVisible(true);
103 * Create dialog components
104 * @return Panel containing all gui elements in dialog
106 private Component makeDialogComponents()
108 JPanel dialogPanel = new JPanel();
109 dialogPanel.setLayout(new BorderLayout());
110 JPanel mainPanel = new JPanel();
111 mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
112 // Make a central panel with the text boxes
113 JPanel descPanel = new JPanel();
114 descPanel.setLayout(new GridLayout(2, 2));
115 descPanel.add(new JLabel(I18nManager.getText("dialog.exportgpx.name")));
116 _nameField = new JTextField(10);
117 descPanel.add(_nameField);
118 descPanel.add(new JLabel(I18nManager.getText("dialog.exportgpx.desc")));
119 _descriptionField = new JTextField(10);
120 descPanel.add(_descriptionField);
121 mainPanel.add(descPanel);
122 mainPanel.add(Box.createVerticalStrut(5));
123 // point type selection (track points, waypoints, photo points)
124 _pointTypeSelector = new PointTypeSelector();
125 mainPanel.add(_pointTypeSelector);
126 // checkboxes for timestamps and copying
127 JPanel checkPanel = new JPanel();
128 _timestampsCheckbox = new JCheckBox(I18nManager.getText("dialog.exportgpx.includetimestamps"));
129 _timestampsCheckbox.setSelected(true);
130 checkPanel.add(_timestampsCheckbox);
131 _copySourceCheckbox = new JCheckBox(I18nManager.getText("dialog.exportgpx.copysource"));
132 _copySourceCheckbox.setSelected(true);
133 checkPanel.add(_copySourceCheckbox);
134 mainPanel.add(checkPanel);
135 dialogPanel.add(mainPanel, BorderLayout.CENTER);
137 // close dialog if escape pressed
138 _nameField.addKeyListener(new DialogCloser(_dialog));
139 // button panel at bottom
140 JPanel buttonPanel = new JPanel();
141 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
142 JButton okButton = new JButton(I18nManager.getText("button.ok"));
143 ActionListener okListener = new ActionListener() {
144 public void actionPerformed(ActionEvent e)
149 okButton.addActionListener(okListener);
150 _descriptionField.addActionListener(okListener);
151 buttonPanel.add(okButton);
152 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
153 cancelButton.addActionListener(new ActionListener() {
154 public void actionPerformed(ActionEvent e) {
158 buttonPanel.add(cancelButton);
159 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
160 dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
166 * Start the export process based on the input parameters
168 private void startExport()
170 // OK pressed, so check selections
171 if (!_pointTypeSelector.getAnythingSelected()) {
172 JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.save.notypesselected"),
173 I18nManager.getText("dialog.saveoptions.title"), JOptionPane.WARNING_MESSAGE);
176 // Choose output file
177 File saveFile = chooseGpxFile(_parentFrame);
178 if (saveFile != null)
180 // New file or overwrite confirmed, so initiate export in separate thread
181 _exportFile = saveFile;
182 new Thread(this).start();
187 * Select a GPX file to save to
188 * @param inParentFrame parent frame for file chooser dialog
189 * @return selected File, or null if selection cancelled
191 public static File chooseGpxFile(JFrame inParentFrame)
193 File saveFile = null;
194 JFileChooser fileChooser = new JFileChooser();
195 fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
196 fileChooser.setFileFilter(new GenericFileFilter("filetype.gpx", new String[] {"gpx"}));
197 fileChooser.setAcceptAllFileFilterUsed(false);
198 // start from directory in config which should be set
199 String configDir = Config.getConfigString(Config.KEY_TRACK_DIR);
200 if (configDir != null) {fileChooser.setCurrentDirectory(new File(configDir));}
202 // Allow choose again if an existing file is selected
203 boolean chooseAgain = false;
207 if (fileChooser.showSaveDialog(inParentFrame) == JFileChooser.APPROVE_OPTION)
209 // OK pressed and file chosen
210 File file = fileChooser.getSelectedFile();
211 // Check file extension
212 if (!file.getName().toLowerCase().endsWith(".gpx"))
214 file = new File(file.getAbsolutePath() + ".gpx");
216 // Check if file exists and if necessary prompt for overwrite
217 Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
218 if (!file.exists() || JOptionPane.showOptionDialog(inParentFrame,
219 I18nManager.getText("dialog.save.overwrite.text"),
220 I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION,
221 JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
222 == JOptionPane.YES_OPTION)
224 // new file or overwrite confirmed
229 // file exists and overwrite cancelled - select again
233 } while (chooseAgain);
238 * Run method for controlling separate thread for exporting
242 OutputStreamWriter writer = null;
245 // normal writing to file
246 writer = new OutputStreamWriter(new FileOutputStream(_exportFile));
247 final boolean[] saveFlags = {_pointTypeSelector.getTrackpointsSelected(), _pointTypeSelector.getWaypointsSelected(),
248 _pointTypeSelector.getPhotopointsSelected(), _pointTypeSelector.getAudiopointsSelected(),
249 _pointTypeSelector.getJustSelection(), _timestampsCheckbox.isSelected()};
251 final int numPoints = exportData(writer, _trackInfo, _nameField.getText(),
252 _descriptionField.getText(), saveFlags, _copySourceCheckbox.isSelected());
256 // Store directory in config for later
257 Config.setConfigString(Config.KEY_TRACK_DIR, _exportFile.getParentFile().getAbsolutePath());
259 UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1")
260 + " " + numPoints + " " + I18nManager.getText("confirm.save.ok2")
261 + " " + _exportFile.getAbsolutePath());
262 // export successful so need to close dialog and return
266 catch (IOException ioe)
268 // System.out.println("Exception: " + ioe.getClass().getName() + " - " + ioe.getMessage());
270 if (writer != null) writer.close();
272 catch (IOException ioe2) {}
273 JOptionPane.showMessageDialog(_parentFrame,
274 I18nManager.getText("error.save.failed") + " : " + ioe.getMessage(),
275 I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
277 // if not returned already, export failed so need to recall the file selection
283 * Export the information to the given writer
284 * @param inWriter writer object
285 * @param inInfo track info object
286 * @param inName name of track (optional)
287 * @param inDesc description of track (optional)
288 * @param inSaveFlags array of booleans to export tracks, waypoints, photos, audios, selection, timestamps
289 * @param inUseCopy true to copy source if available
290 * @return number of points written
291 * @throws IOException if io errors occur on write
293 public static int exportData(OutputStreamWriter inWriter, TrackInfo inInfo, String inName,
294 String inDesc, boolean[] inSaveFlags, boolean inUseCopy) throws IOException
296 // Instantiate source file cachers in case we want to copy output
297 GpxCacherList gpxCachers = null;
298 if (inUseCopy) gpxCachers = new GpxCacherList(inInfo.getFileInfo());
299 // Write or copy headers
300 inWriter.write(getXmlHeaderString(inWriter));
301 inWriter.write(getGpxHeaderString(gpxCachers));
303 String trackName = "PruneTrack";
304 if (inName != null && !inName.equals(""))
307 inWriter.write("\t<name>");
308 inWriter.write(trackName);
309 inWriter.write("</name>\n");
312 inWriter.write("\t<desc>");
313 inWriter.write((inDesc != null && !inDesc.equals(""))?inDesc:"Export from Prune");
314 inWriter.write("</desc>\n");
317 DataPoint point = null;
318 final boolean exportTrackpoints = inSaveFlags[0];
319 final boolean exportWaypoints = inSaveFlags[1];
320 final boolean exportPhotos = inSaveFlags[2];
321 final boolean exportAudios = inSaveFlags[3];
322 final boolean exportSelection = inSaveFlags[4];
323 final boolean exportTimestamps = inSaveFlags[5];
325 int selStart = -1, selEnd = -1;
326 if (exportSelection) {
327 selStart = inInfo.getSelection().getStart();
328 selEnd = inInfo.getSelection().getEnd();
330 // Loop over waypoints
331 final int numPoints = inInfo.getTrack().getNumPoints();
333 for (i=0; i<numPoints; i++)
335 point = inInfo.getTrack().getPoint(i);
336 if (!exportSelection || (i>=selStart && i<=selEnd)) {
337 // Make a wpt element for each waypoint
338 if (point.isWaypoint()) {
341 String pointSource = (inUseCopy?getPointSource(gpxCachers, point):null);
342 if (pointSource != null) {
343 inWriter.write(pointSource);
344 inWriter.write('\n');
347 exportWaypoint(point, inWriter, exportTimestamps, exportPhotos, exportAudios);
354 // Export both route points and then track points
355 if (exportTrackpoints || exportPhotos || exportAudios)
357 // Output all route points (if any)
358 numSaved += writeTrackPoints(inWriter, inInfo, exportSelection, exportTrackpoints, exportPhotos,
359 exportAudios, exportTimestamps, true, gpxCachers, "<rtept", "\t<rte><number>1</number>\n",
361 // Output all track points, if any
362 String trackStart = "\t<trk><name>" + trackName + "</name><number>1</number><trkseg>\n";
363 numSaved += writeTrackPoints(inWriter, inInfo, exportSelection, exportTrackpoints, exportPhotos,
364 exportAudios, exportTimestamps, false, gpxCachers, "<trkpt", trackStart,
365 "\t</trkseg>\n\t<trkseg>\n", "\t</trkseg></trk>\n");
368 inWriter.write("</gpx>\n");
373 * Loop through the track outputting the relevant track points
374 * @param inWriter writer object for output
375 * @param inInfo track info object containing track
376 * @param inExportSelection true to just output current selection
377 * @param inExportTrackpoints true to output track points
378 * @param inExportPhotos true to output photo points
379 * @param inExportAudios true to output audio points
380 * @param inExportTimestamps true to include timestamps in export
381 * @param inOnlyCopies true to only export if source can be copied
382 * @param inCachers list of GpxCachers
383 * @param inPointTag tag to match for each point
384 * @param inStartTag start tag to output
385 * @param inSegmentTag tag to output between segments (or null)
386 * @param inEndTag end tag to output
388 private static int writeTrackPoints(OutputStreamWriter inWriter,
389 TrackInfo inInfo, boolean inExportSelection, boolean inExportTrackpoints,
390 boolean inExportPhotos, boolean inExportAudios, boolean exportTimestamps,
391 boolean inOnlyCopies, GpxCacherList inCachers, String inPointTag,
392 String inStartTag, String inSegmentTag, String inEndTag)
395 // Note: far too many input parameters to this method but avoids duplication
396 // of output functionality for writing track points and route points
397 int numPoints = inInfo.getTrack().getNumPoints();
398 int selStart = inInfo.getSelection().getStart();
399 int selEnd = inInfo.getSelection().getEnd();
401 // Loop over track points
402 for (int i=0; i<numPoints; i++)
404 DataPoint point = inInfo.getTrack().getPoint(i);
405 if ((!inExportSelection || (i>=selStart && i<=selEnd)) && !point.isWaypoint())
407 if ((point.getPhoto()==null && inExportTrackpoints) || (point.getPhoto()!=null && inExportPhotos)
408 || (point.getAudio()!=null && inExportAudios))
410 if (point.getPhoto() != null) System.out.println("Writetrackpoints: Point has photo " + point.getPhoto().getFile().getName());
411 // get the source from the point (if any)
412 String pointSource = getPointSource(inCachers, point);
413 // Clear point source if it's the wrong type of point (eg changed from waypoint or route point)
414 if (pointSource != null && !pointSource.toLowerCase().startsWith(inPointTag)) {pointSource = null;}
415 if (pointSource != null || !inOnlyCopies)
417 // restart track segment if necessary
418 if ((numSaved > 0) && point.getSegmentStart() && (inSegmentTag != null)) {
419 inWriter.write(inSegmentTag);
421 if (numSaved == 0) {inWriter.write(inStartTag);}
422 if (pointSource != null) {
423 if (point.getPhoto() != null) System.out.println("Point has photo but using source");
424 inWriter.write(pointSource);
425 inWriter.write('\n');
428 if (!inOnlyCopies) {exportTrackpoint(point, inWriter, exportTimestamps, inExportPhotos, inExportAudios);}
435 if (numSaved > 0) {inWriter.write(inEndTag);}
441 * Get the point source for the specified point
442 * @param inCachers list of GPX cachers to ask for source
443 * @param inPoint point object
444 * @return xml source if available, or null otherwise
446 private static String getPointSource(GpxCacherList inCachers, DataPoint inPoint)
448 if (inCachers == null || inPoint == null) {return null;}
449 String source = inCachers.getSourceString(inPoint);
450 if (source == null || !inPoint.isModified()) {return source;}
451 // Point has been modified - maybe it's possible to modify the source
452 source = replaceGpxTags(source, "lat=\"", "\"", inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
453 source = replaceGpxTags(source, "lon=\"", "\"", inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
454 source = replaceGpxTags(source, "<ele>", "</ele>", inPoint.getAltitude().getStringValue(Altitude.Format.METRES));
455 source = replaceGpxTags(source, "<time>", "</time>", inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
456 if (inPoint.isWaypoint()) {source = replaceGpxTags(source, "<name>", "</name>", inPoint.getWaypointName());} // only for waypoints
457 // photo / audio links
458 if (source != null && (inPoint.hasMedia() || source.indexOf("</link>") > 0)) {
459 source = replaceMediaLinks(source, makeMediaLink(inPoint));
465 * Replace the given value into the given XML string
466 * @param inSource source XML for point
467 * @param inStartTag start tag for field
468 * @param inEndTag end tag for field
469 * @param inValue value to replace between start tag and end tag
470 * @return modified String, or null if not possible
472 private static String replaceGpxTags(String inSource, String inStartTag, String inEndTag, String inValue)
474 if (inSource == null) {return null;}
475 // Look for start and end tags within source
476 final int startPos = inSource.indexOf(inStartTag);
477 final int endPos = inSource.indexOf(inEndTag, startPos+inStartTag.length());
478 if (startPos > 0 && endPos > 0)
480 String origValue = inSource.substring(startPos + inStartTag.length(), endPos);
481 if (inValue != null && origValue.equals(inValue)) {
485 else if (inValue == null || inValue.equals("")) {
486 // Need to delete value
487 return inSource.substring(0, startPos) + inSource.substring(endPos + inEndTag.length());
490 // Need to replace value
491 return inSource.substring(0, startPos+inStartTag.length()) + inValue + inSource.substring(endPos);
494 // Value not found for this field in original source
495 if (inValue == null || inValue.equals("")) {return inSource;}
500 * Replace the media tags in the given XML string
501 * @param inSource source XML for point
502 * @param inValue value for the current point
503 * @return modified String, or null if not possible
505 private static String replaceMediaLinks(String inSource, String inValue)
507 if (inSource == null) {return null;}
508 // Note that this method is very similar to replaceGpxTags except there can be multiple link tags
509 // and the tags must have attributes. So either one heavily parameterized method or two.
510 // Look for start and end tags within source
511 final String STARTTEXT = "<link";
512 final String ENDTEXT = "</link>";
513 final int startPos = inSource.indexOf(STARTTEXT);
514 final int endPos = inSource.lastIndexOf(ENDTEXT);
515 if (startPos > 0 && endPos > 0)
517 String origValue = inSource.substring(startPos, endPos + ENDTEXT.length());
518 if (inValue != null && origValue.equals(inValue)) {
522 else if (inValue == null || inValue.equals("")) {
523 // Need to delete value
524 return inSource.substring(0, startPos) + inSource.substring(endPos + ENDTEXT.length());
527 // Need to replace value
528 return inSource.substring(0, startPos) + inValue + inSource.substring(endPos + ENDTEXT.length());
531 // Value not found for this field in original source
532 if (inValue == null || inValue.equals("")) {return inSource;}
537 * Get the header string for the xml document including encoding
538 * @param inWriter writer object
539 * @return header string defining encoding
541 private static String getXmlHeaderString(OutputStreamWriter inWriter)
543 String encoding = inWriter.getEncoding();
545 encoding = Charset.forName(encoding).name();
547 catch (Exception e) {} // ignore failure to find encoding
548 return "<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>\n";
552 * Get the header string for the gpx tag
553 * @param inCachers cacher list to ask for headers, if available
554 * @return header string from cachers or as default
556 private static String getGpxHeaderString(GpxCacherList inCachers)
558 String gpxHeader = null;
559 if (inCachers != null) {gpxHeader = inCachers.getFirstHeader();}
560 if (gpxHeader == null || gpxHeader.length() < 5)
562 // Create default (1.0) header
563 gpxHeader = "<gpx version=\"1.0\" creator=\"" + GPX_CREATOR
564 + "\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
565 + " xmlns=\"http://www.topografix.com/GPX/1/0\""
566 + " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">\n";
568 return gpxHeader + "\n";
572 * Export the specified waypoint into the file
573 * @param inPoint waypoint to export
574 * @param inWriter writer object
575 * @param inTimestamps true to export timestamps too
576 * @param inPhoto true to export link to photo
577 * @param inAudio true to export link to audio
578 * @throws IOException on write failure
580 private static void exportWaypoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps,
581 boolean inPhoto, boolean inAudio)
584 inWriter.write("\t<wpt lat=\"");
585 inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
586 inWriter.write("\" lon=\"");
587 inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
588 inWriter.write("\">\n");
589 // altitude if available
590 if (inPoint.hasAltitude())
592 inWriter.write("\t\t<ele>");
593 inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES));
594 inWriter.write("</ele>\n");
596 // timestamp if available (point might have timestamp and then be turned into a waypoint)
597 if (inPoint.hasTimestamp() && inTimestamps)
599 inWriter.write("\t\t<time>");
600 inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
601 inWriter.write("</time>\n");
603 // write waypoint name after elevation and time
604 inWriter.write("\t\t<name>");
605 inWriter.write(inPoint.getWaypointName().trim());
606 inWriter.write("</name>\n");
607 // Media links, if any
608 if (inPhoto && inPoint.getPhoto() != null)
610 inWriter.write("\t\t");
611 inWriter.write(makeMediaLink(inPoint.getPhoto()));
612 inWriter.write('\n');
614 if (inAudio && inPoint.getAudio() != null)
616 inWriter.write("\t\t");
617 inWriter.write(makeMediaLink(inPoint.getAudio()));
618 inWriter.write('\n');
620 // write waypoint type if any
621 String type = inPoint.getFieldValue(Field.WAYPT_TYPE);
625 if (!type.equals(""))
627 inWriter.write("\t\t<type>");
628 inWriter.write(type);
629 inWriter.write("</type>\n");
632 inWriter.write("\t</wpt>\n");
637 * Export the specified trackpoint into the file
638 * @param inPoint trackpoint to export
639 * @param inWriter writer object
640 * @param inTimestamps true to export timestamps too
641 * @param inExportPhoto true to export photo link
642 * @param inExportAudio true to export audio link
644 private static void exportTrackpoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps,
645 boolean inExportPhoto, boolean inExportAudio)
648 if (inPoint.getPhoto() != null) System.out.println("Point has photo " + inPoint.getPhoto().getFile().getName());
649 inWriter.write("\t\t<trkpt lat=\"");
650 inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
651 inWriter.write("\" lon=\"");
652 inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
653 inWriter.write("\">");
655 if (inPoint.hasAltitude())
657 inWriter.write("<ele>");
658 inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES));
659 inWriter.write("</ele>");
661 // timestamp if available (and selected)
662 if (inPoint.hasTimestamp() && inTimestamps)
664 inWriter.write("<time>");
665 inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
666 inWriter.write("</time>");
669 if (inPoint.getPhoto() != null && inExportPhoto) {
670 inWriter.write(makeMediaLink(inPoint.getPhoto()));
672 if (inPoint.getAudio() != null && inExportAudio) {
673 inWriter.write(makeMediaLink(inPoint.getAudio()));
675 inWriter.write("</trkpt>\n");
679 * Make the xml for the media link(s)
680 * @param inPoint point to generate text for
681 * @return link tags, or null if no links
683 private static String makeMediaLink(DataPoint inPoint)
685 Photo photo = inPoint.getPhoto();
686 AudioFile audio = inPoint.getAudio();
687 if (photo == null && audio == null) {
690 String linkText = "";
692 linkText = makeMediaLink(photo);
695 linkText += makeMediaLink(audio);
701 * Make the media link for a single media item
702 * @param inMedia media item, either photo or audio
703 * @return link for this media
705 private static String makeMediaLink(MediaFile inMedia)
707 return "<link href=\"" + inMedia.getFile().getAbsolutePath() + "\"><text>" + inMedia.getFile().getName() + "</text></link>";