/*------------------------------------------------------------------------------
** Ident: Sogeti Smart Mobile Solutions
** Author: rene
** Copyright: (c) Apr 24, 2011 Sogeti Nederland B.V. All Rights Reserved.
**------------------------------------------------------------------------------
** Sogeti Nederland B.V. | No part of this file may be reproduced
** Distributed Software Engineering | or transmitted in any form or by any
** Lange Dreef 17 | means, electronic or mechanical, for the
** 4131 NJ Vianen | purpose, without the express written
** The Netherlands | permission of the copyright holder.
*------------------------------------------------------------------------------
*
* This file is part of OpenGPSTracker.
*
* OpenGPSTracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenGPSTracker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenGPSTracker. If not, see <http://www.gnu.org/licenses/>.
*
*/
package nl.sogeti.android.gpstracker.actions.tasks;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore.MediaColumns;
import android.util.Log;
import android.util.Xml;
import nl.sogeti.android.gpstracker.R;
import nl.sogeti.android.gpstracker.actions.utils.ProgressListener;
import nl.sogeti.android.gpstracker.db.GPStracking;
import nl.sogeti.android.gpstracker.db.GPStracking.Media;
import nl.sogeti.android.gpstracker.db.GPStracking.Segments;
import nl.sogeti.android.gpstracker.db.GPStracking.Tracks;
import nl.sogeti.android.gpstracker.db.GPStracking.Waypoints;
import nl.sogeti.android.gpstracker.util.Constants;
import org.xmlpull.v1.XmlSerializer;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
/**
* Create a GPX version of a stored track
*
* @version $Id: GpxCreator.java 1191 2012-01-01 11:49:47Z rcgroot $
* @author rene (c) Mar 22, 2009, Sogeti B.V.
*/
public class GpxCreator extends XmlCreator
{
public static final String NS_SCHEMA = "http://www.w3.org/2001/XMLSchema-instance";
public static final String NS_GPX_11 = "http://www.topografix.com/GPX/1/1";
public static final String NS_GPX_10 = "http://www.topografix.com/GPX/1/0";
public static final String NS_OGT_10 = "http://gpstracker.android.sogeti.nl/GPX/1/0";
public static final String NS_F8F_10 = "http://bixitracker.android.f8full.net/GPX/1/0";
public static final SimpleDateFormat ZULU_DATE_FORMATER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
static
{
TimeZone utc = TimeZone.getTimeZone("UTC");
ZULU_DATE_FORMATER.setTimeZone(utc); // ZULU_DATE_FORMAT format ends with Z for UTC so make that true
}
private String TAG = "OGT.GpxCreator";
private boolean includeAttachments;
protected String mName;
public GpxCreator(Context context, Uri trackUri, String chosenBaseFileName, boolean attachments, ProgressListener listener)
{
super(context, trackUri, chosenBaseFileName, listener);
includeAttachments = attachments;
}
@Override
protected Uri doInBackground(Void... params)
{
determineProgressGoal();
Uri resultFilename = exportGpx();
return resultFilename;
}
protected Uri exportGpx()
{
String xmlFilePath;
if (mFileName.endsWith(".gpx") || mFileName.endsWith(".xml"))
{
setExportDirectoryPath(Constants.getSdCardDirectory(mContext) + mFileName.substring(0, mFileName.length() - 4));
xmlFilePath = getExportDirectoryPath() + "/" + mFileName;
}
else
{
setExportDirectoryPath(Constants.getSdCardDirectory(mContext) + mFileName);
xmlFilePath = getExportDirectoryPath() + "/" + mFileName + ".gpx";
}
new File(getExportDirectoryPath()).mkdirs();
String resultFilename = null;
FileOutputStream fos = null;
BufferedOutputStream buf = null;
try
{
verifySdCardAvailibility();
XmlSerializer serializer = Xml.newSerializer();
File xmlFile = new File(xmlFilePath);
fos = new FileOutputStream(xmlFile);
buf = new BufferedOutputStream(fos, 8 * 8192);
serializer.setOutput(buf, "UTF-8");
serializeTrack(mTrackUri, serializer);
buf.close();
buf = null;
fos.close();
fos = null;
if (needsBundling())
{
resultFilename = bundlingMediaAndXml(xmlFile.getParentFile().getName(), ".zip");
}
else
{
File finalFile = new File(Constants.getSdCardDirectory(mContext) + xmlFile.getName());
xmlFile.renameTo(finalFile);
resultFilename = finalFile.getAbsolutePath();
XmlCreator.deleteRecursive(xmlFile.getParentFile());
}
mFileName = new File(resultFilename).getName();
}
catch (FileNotFoundException e)
{
String text = mContext.getString(R.string.ticker_failed) + " \"" + xmlFilePath + "\" " + mContext.getString(R.string.error_filenotfound);
handleError(mContext.getString(R.string.taskerror_gpx_write), e, text);
}
catch (IllegalArgumentException e)
{
String text = mContext.getString(R.string.ticker_failed) + " \"" + xmlFilePath + "\" " + mContext.getString(R.string.error_filename);
handleError(mContext.getString(R.string.taskerror_gpx_write), e, text);
}
catch (IllegalStateException e)
{
String text = mContext.getString(R.string.ticker_failed) + " \"" + xmlFilePath + "\" " + mContext.getString(R.string.error_buildxml);
handleError(mContext.getString(R.string.taskerror_gpx_write), e, text);
}
catch (IOException e)
{
String text = mContext.getString(R.string.ticker_failed) + " \"" + xmlFilePath + "\" " + mContext.getString(R.string.error_writesdcard);
handleError(mContext.getString(R.string.taskerror_gpx_write), e, text);
}
finally
{
if (buf != null)
{
try
{
buf.close();
}
catch (IOException e)
{
Log.e(TAG, "Failed to close buf after completion, ignoring.", e);
}
}
if (fos != null)
{
try
{
fos.close();
}
catch (IOException e)
{
Log.e(TAG, "Failed to close fos after completion, ignoring.", e);
}
}
}
return Uri.fromFile(new File(resultFilename));
}
private void serializeTrack(Uri trackUri, XmlSerializer serializer) throws IllegalArgumentException, IllegalStateException, IOException
{
if (isCancelled())
{
throw new IOException("Fail to execute request due to canceling");
}
serializer.startDocument("UTF-8", true);
serializer.setPrefix("xsi", NS_SCHEMA);
serializer.setPrefix("gpx10", NS_GPX_10);
serializer.setPrefix("ogt10", NS_OGT_10);
serializer.setPrefix("f8f10", NS_F8F_10);
serializer.text("\n");
serializer.startTag("", "gpx");
serializer.attribute(null, "version", "1.1");
serializer.attribute(null, "creator", "nl.sogeti.android.gpstracker");
serializer.attribute(NS_SCHEMA, "schemaLocation", NS_GPX_11 + " http://www.topografix.com/gpx/1/1/gpx.xsd");
serializer.attribute(null, "xmlns", NS_GPX_11);
// <metadata/> Big header of the track
serializeTrackHeader(mContext, serializer, trackUri);
// <wpt/> [0...] Waypoints
if (includeAttachments)
{
serializeWaypoints(mContext, serializer, Uri.withAppendedPath(trackUri, "/media" ));
}
// <trk/> [0...] Track
serializer.text("\n");
serializer.startTag("", "trk");
serializer.text("\n");
serializer.startTag("", "name");
serializer.text(mName);
serializer.endTag("", "name");
serializeTrackExtensions(mContext, serializer, trackUri);
//TODO: ADD EXTENSIONS HERE FROM MY OWN SCHEMA
// The list of segments in the track
serializeSegments(serializer, Uri.withAppendedPath(trackUri, "segments"));
serializer.text("\n");
serializer.endTag("", "trk");
serializer.text("\n");
serializer.endTag("", "gpx");
serializer.endDocument();
}
private void serializeTrackHeader(Context context, XmlSerializer serializer, Uri trackUri) throws IOException
{
if (isCancelled())
{
throw new IOException("Fail to execute request due to canceling");
}
ContentResolver resolver = context.getContentResolver();
Cursor trackCursor = null;
String databaseName = null;
try
{
trackCursor = resolver.query(trackUri, new String[] { Tracks._ID, Tracks.NAME, Tracks.CREATION_TIME }, null, null, null);
if (trackCursor.moveToFirst())
{
databaseName = trackCursor.getString(1);
serializer.text("\n");
serializer.startTag("", "metadata");
serializer.text("\n");
serializer.startTag("", "time");
Date time = new Date(trackCursor.getLong(2));
synchronized (ZULU_DATE_FORMATER)
{
serializer.text(ZULU_DATE_FORMATER.format(time));
}
serializer.endTag("", "time");
serializer.text("\n");
serializer.endTag("", "metadata");
}
}
finally
{
if (trackCursor != null)
{
trackCursor.close();
}
}
if (mName == null)
{
mName = "Untitled";
}
if (databaseName != null && !databaseName.equals(""))
{
mName = databaseName;
}
if (mChosenName != null && !mChosenName.equals(""))
{
mName = mChosenName;
}
}
private void serializeTrackExtensions(Context context, XmlSerializer serializer, Uri trackUri) throws IOException
{
if (isCancelled())
{
throw new IOException("Fail to execute request due to canceling");
}
ContentResolver resolver = context.getContentResolver();
Cursor trackCursor = null;
try
{
trackCursor = resolver.query(trackUri, new String[] { Tracks._ID, Tracks.WITH_HELMET, Tracks.START_REASON, Tracks.END_REASON, Tracks.START_STATION_NAME, Tracks.END_STATION_NAME, Tracks.SERVICE_RATING }, null, null, null);
//TODO: add IDs from Bixi XML
if(trackCursor.moveToFirst())
{
serializer.text("\n");
serializer.startTag("", "extensions");
quickTag(serializer, NS_F8F_10, "helmet", Integer.toString(trackCursor.getInt(1)));
quickTag(serializer, NS_F8F_10, "startReason", trackCursor.getString(2));
quickTag(serializer, NS_F8F_10, "endReason", trackCursor.getString(3));
quickTag(serializer, NS_F8F_10, "startStationName", trackCursor.getString(4));
quickTag(serializer, NS_F8F_10, "endStationName", trackCursor.getString(5));
quickTag(serializer, NS_F8F_10, "rating", Integer.toString(trackCursor.getInt(6)));
serializer.text("\n");
serializer.endTag("", "extensions");
//Station_depart ID, terminal_name, location
//Station_arrivee ID, terminal_name, location
//Raison_depart From list like
// Location
// work, home, out
// or activity ? restaurant (food), groceries (food),
// leisure, shopping,
//KISS principle ?
//Raison_arrivée same as above
//Casque Boolean value as of did I had one or not
//Bixi service rating, as a value between 1 to 5
//Note : I also need a free input description, as to explain my rating if required or any other information
//Also, seeing what populates this free field gives ideas on what to add next
//SEE As Ref
// serializer.text("\n");
// serializer.startTag("", "extensions");
//
// double speed = waypointsCursor.getDouble(5);
// double accuracy = waypointsCursor.getDouble(6);
// double bearing = waypointsCursor.getDouble(7);
// if (speed > 0.0)
// {
// quickTag(serializer, NS_GPX_10, "speed", Double.toString(speed));
// }
// if (accuracy > 0.0)
// {
// quickTag(serializer, NS_OGT_10, "accuracy", Double.toString(accuracy));
// }
// if (bearing != 0.0)
// {
// quickTag(serializer, NS_GPX_10, "course", Double.toString(bearing));
// }
// serializer.endTag("", "extensions");
// serializer.text("\n");
// serializer.endTag("", "trkpt");
}
}
finally
{
if (trackCursor != null)
{
trackCursor.close();
}
}
}
private void serializeSegments(XmlSerializer serializer, Uri segments) throws IOException
{
if (isCancelled())
{
throw new IOException("Fail to execute request due to canceling");
}
Cursor segmentCursor = null;
ContentResolver resolver = mContext.getContentResolver();
try
{
segmentCursor = resolver.query(segments, new String[] { Segments._ID }, null, null, null);
if (segmentCursor.moveToFirst())
{
do
{
Uri waypoints = Uri.withAppendedPath(segments, segmentCursor.getLong(0) + "/waypoints");
serializer.text("\n");
serializer.startTag("", "trkseg");
serializeTrackPoints(serializer, waypoints);
serializer.text("\n");
serializer.endTag("", "trkseg");
}
while (segmentCursor.moveToNext());
}
}
finally
{
if (segmentCursor != null)
{
segmentCursor.close();
}
}
}
private void serializeTrackPoints(XmlSerializer serializer, Uri waypoints) throws IOException
{
if (isCancelled())
{
throw new IOException("Fail to execute request due to canceling");
}
Cursor waypointsCursor = null;
ContentResolver resolver = mContext.getContentResolver();
try
{
waypointsCursor = resolver.query(waypoints, new String[] { Waypoints.LONGITUDE, Waypoints.LATITUDE, Waypoints.TIME, Waypoints.ALTITUDE, Waypoints._ID,
Waypoints.SPEED, Waypoints.ACCURACY, Waypoints.BEARING }, null, null, null);
if (waypointsCursor.moveToFirst())
{
do
{
mXmlCreatorProgressAdmin.addWaypointProgress(1);
serializer.text("\n");
serializer.startTag("", "trkpt");
serializer.attribute(null, "lat", Double.toString(waypointsCursor.getDouble(1)));
serializer.attribute(null, "lon", Double.toString(waypointsCursor.getDouble(0)));
serializer.text("\n");
serializer.startTag("", "ele");
serializer.text(Double.toString(waypointsCursor.getDouble(3)));
serializer.endTag("", "ele");
serializer.text("\n");
serializer.startTag("", "time");
Date time = new Date(waypointsCursor.getLong(2));
synchronized (ZULU_DATE_FORMATER)
{
serializer.text(ZULU_DATE_FORMATER.format(time));
}
serializer.endTag("", "time");
serializer.text("\n");
serializer.startTag("", "extensions");
double speed = waypointsCursor.getDouble(5);
double accuracy = waypointsCursor.getDouble(6);
double bearing = waypointsCursor.getDouble(7);
if (speed > 0.0)
{
quickTag(serializer, NS_GPX_10, "speed", Double.toString(speed));
}
if (accuracy > 0.0)
{
quickTag(serializer, NS_OGT_10, "accuracy", Double.toString(accuracy));
}
if (bearing != 0.0)
{
quickTag(serializer, NS_GPX_10, "course", Double.toString(bearing));
}
serializer.endTag("", "extensions");
serializer.text("\n");
serializer.endTag("", "trkpt");
}
while (waypointsCursor.moveToNext());
}
}
finally
{
if (waypointsCursor != null)
{
waypointsCursor.close();
}
}
}
private void serializeWaypoints(Context context, XmlSerializer serializer, Uri media) throws IOException
{
if (isCancelled())
{
throw new IOException("Fail to execute request due to canceling");
}
Cursor mediaCursor = null;
Cursor waypointCursor = null;
ContentResolver resolver = context.getContentResolver();
try
{
mediaCursor = resolver.query(media, new String[] { Media.URI, Media.TRACK, Media.SEGMENT, Media.WAYPOINT }, null, null, null);
if (mediaCursor.moveToFirst())
{
do
{
Uri waypointUri = Waypoints.buildUri( mediaCursor.getLong(1), mediaCursor.getLong(2), mediaCursor.getLong(3));
waypointCursor = resolver.query(waypointUri, new String[]{ Waypoints.LATITUDE, Waypoints.LONGITUDE, Waypoints.ALTITUDE, Waypoints.TIME}, null, null, null);
serializer.text("\n");
serializer.startTag("", "wpt");
if(waypointCursor != null && waypointCursor.moveToFirst() )
{
serializer.attribute(null, "lat", Double.toString(waypointCursor.getDouble(0)));
serializer.attribute(null, "lon", Double.toString(waypointCursor.getDouble(1)));
serializer.text("\n");
serializer.startTag("", "ele");
serializer.text(Double.toString(waypointCursor.getDouble(2)));
serializer.endTag("", "ele");
serializer.text("\n");
serializer.startTag("", "time");
Date time = new Date(waypointCursor.getLong(3));
synchronized (ZULU_DATE_FORMATER)
{
serializer.text(ZULU_DATE_FORMATER.format(time));
}
serializer.endTag("", "time");
}
if( waypointCursor != null )
{
waypointCursor.close();
waypointCursor = null;
}
Uri mediaUri = Uri.parse(mediaCursor.getString(0));
if (mediaUri.getScheme().equals("file"))
{
if (mediaUri.getLastPathSegment().endsWith("3gp"))
{
String fileName = includeMediaFile(mediaUri.getLastPathSegment());
quickTag(serializer, "", "name", fileName);
serializer.startTag("", "link");
serializer.attribute(null, "href", fileName);
quickTag(serializer, "", "text", fileName);
serializer.endTag("", "link");
}
else if (mediaUri.getLastPathSegment().endsWith("jpg"))
{
String mediaPathPrefix = Constants.getSdCardDirectory(mContext);
String fileName = includeMediaFile( mediaPathPrefix+mediaUri.getLastPathSegment() );
quickTag(serializer, "", "name", fileName);
serializer.startTag("", "link");
serializer.attribute(null, "href", fileName);
quickTag(serializer, "", "text", fileName);
serializer.endTag("", "link");
}
else if (mediaUri.getLastPathSegment().endsWith("txt"))
{
quickTag(serializer, "", "name", mediaUri.getLastPathSegment());
serializer.startTag("", "desc");
BufferedReader buf = new BufferedReader(new FileReader(mediaUri.getEncodedPath()));
String line;
while ((line = buf.readLine()) != null)
{
serializer.text(line);
serializer.text("\n");
}
serializer.endTag("", "desc");
}
}
else if (mediaUri.getScheme().equals("content"))
{
if ((GPStracking.AUTHORITY + ".string").equals(mediaUri.getAuthority()))
{
quickTag(serializer, "", "name", mediaUri.getLastPathSegment());
}
else if (mediaUri.getAuthority().equals("media"))
{
Cursor mediaItemCursor = null;
try
{
mediaItemCursor = resolver.query(mediaUri, new String[] { MediaColumns.DATA, MediaColumns.DISPLAY_NAME }, null, null, null);
if (mediaItemCursor.moveToFirst())
{
String fileName = includeMediaFile(mediaItemCursor.getString(0));
quickTag(serializer, "", "name", fileName);
serializer.startTag("", "link");
serializer.attribute(null, "href", fileName);
quickTag(serializer, "", "text", mediaItemCursor.getString(1));
serializer.endTag("", "link");
}
}
finally
{
if (mediaItemCursor != null)
{
mediaItemCursor.close();
}
}
}
}
serializer.text("\n");
serializer.endTag("", "wpt");
}
while (mediaCursor.moveToNext());
}
}
finally
{
if (mediaCursor != null)
{
mediaCursor.close();
}
if( waypointCursor != null )
{
waypointCursor.close();
}
}
}
@Override
protected String getContentType()
{
return needsBundling() ? "application/zip" : "text/xml";
}
}