/*------------------------------------------------------------------------------ ** 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"; } }