Merge heads

This commit is contained in:
youtaol
2012-06-27 10:26:22 +08:00
parent 7bacdf35fb
commit faddce53be
14 changed files with 244 additions and 224 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 58 KiB

-2
View File
@@ -47,9 +47,7 @@ limitations under the License.
<string name="send_to_fusion_tables_key">sendToFusionTables</string> <string name="send_to_fusion_tables_key">sendToFusionTables</string>
<string name="send_to_maps_key">sendToMaps</string> <string name="send_to_maps_key">sendToMaps</string>
<string name="sensor_type_key">sensorType</string> <string name="sensor_type_key">sensorType</string>
<string name="show_check_units_dialog_key">showCheckUnitsDialog</string>
<string name="show_confirm_sharing_dialog_key">showConfrimSharingDialog</string> <string name="show_confirm_sharing_dialog_key">showConfrimSharingDialog</string>
<string name="show_welcome_dialog_key">showWelcomeDialog</string>
<string name="split_frequency_key">splitFrequency</string> <string name="split_frequency_key">splitFrequency</string>
<string name="stats_show_coordinate_key">statsShowCoordinate</string> <string name="stats_show_coordinate_key">statsShowCoordinate</string>
<string name="stats_show_elevation_key">statsShowElevation</string> <string name="stats_show_elevation_key">statsShowElevation</string>
@@ -278,23 +278,21 @@ public class TrackListActivity extends FragmentActivity implements DeleteOneTrac
* Shows start up dialogs. * Shows start up dialogs.
*/ */
public void showStartupDialogs() { public void showStartupDialogs() {
if (!EulaUtils.getEulaValue(this)) { if (!EulaUtils.getAcceptEula(this)) {
Fragment fragment = getSupportFragmentManager() Fragment fragment = getSupportFragmentManager()
.findFragmentByTag(EulaDialogFragment.EULA_DIALOG_TAG); .findFragmentByTag(EulaDialogFragment.EULA_DIALOG_TAG);
if (fragment == null) { if (fragment == null) {
EulaDialogFragment.newInstance(false) EulaDialogFragment.newInstance(false)
.show(getSupportFragmentManager(), EulaDialogFragment.EULA_DIALOG_TAG); .show(getSupportFragmentManager(), EulaDialogFragment.EULA_DIALOG_TAG);
} }
} else if (PreferencesUtils.getBoolean( } else if (EulaUtils.getShowWelcome(this)) {
this, R.string.show_welcome_dialog_key, PreferencesUtils.SHOW_WELCOME_DIALOG_DEFAULT)) {
Fragment fragment = getSupportFragmentManager() Fragment fragment = getSupportFragmentManager()
.findFragmentByTag(WelcomeDialogFragment.WELCOME_DIALOG_TAG); .findFragmentByTag(WelcomeDialogFragment.WELCOME_DIALOG_TAG);
if (fragment == null) { if (fragment == null) {
new WelcomeDialogFragment().show( new WelcomeDialogFragment().show(
getSupportFragmentManager(), WelcomeDialogFragment.WELCOME_DIALOG_TAG); getSupportFragmentManager(), WelcomeDialogFragment.WELCOME_DIALOG_TAG);
} }
} else if (PreferencesUtils.getBoolean(this, R.string.show_check_units_dialog_key, } else if (EulaUtils.getShowCheckUnits(this)) {
PreferencesUtils.SHOW_CHECK_UNITS_DIALOG_DEFAULT)) {
Fragment fragment = getSupportFragmentManager() Fragment fragment = getSupportFragmentManager()
.findFragmentByTag(CheckUnitsDialogFragment.CHECK_UNITS_DIALOG_TAG); .findFragmentByTag(CheckUnitsDialogFragment.CHECK_UNITS_DIALOG_TAG);
if (fragment == null) { if (fragment == null) {
@@ -17,6 +17,7 @@
package com.google.android.apps.mytracks.fragments; package com.google.android.apps.mytracks.fragments;
import com.google.android.apps.mytracks.TrackListActivity; import com.google.android.apps.mytracks.TrackListActivity;
import com.google.android.apps.mytracks.util.EulaUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils; import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
@@ -69,7 +70,7 @@ public class CheckUnitsDialogFragment extends DialogFragment {
* Tasks to perform when done. * Tasks to perform when done.
*/ */
private void onDone() { private void onDone() {
PreferencesUtils.setBoolean(getActivity(), R.string.show_check_units_dialog_key, false); EulaUtils.setShowCheckUnits(getActivity());
TrackListActivity trackListActivity = (TrackListActivity) getActivity(); TrackListActivity trackListActivity = (TrackListActivity) getActivity();
trackListActivity.showStartupDialogs(); trackListActivity.showStartupDialogs();
} }
@@ -77,7 +77,7 @@ public class EulaDialogFragment extends DialogFragment {
.setPositiveButton(R.string.eula_accept, new DialogInterface.OnClickListener() { .setPositiveButton(R.string.eula_accept, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
EulaUtils.setEulaValue(getActivity()); EulaUtils.setAcceptEula(getActivity());
TrackListActivity trackListActivity = (TrackListActivity) getActivity(); TrackListActivity trackListActivity = (TrackListActivity) getActivity();
trackListActivity.showStartupDialogs(); trackListActivity.showStartupDialogs();
} }
@@ -17,7 +17,7 @@
package com.google.android.apps.mytracks.fragments; package com.google.android.apps.mytracks.fragments;
import com.google.android.apps.mytracks.TrackListActivity; import com.google.android.apps.mytracks.TrackListActivity;
import com.google.android.apps.mytracks.util.PreferencesUtils; import com.google.android.apps.mytracks.util.EulaUtils;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import android.app.AlertDialog; import android.app.AlertDialog;
@@ -55,7 +55,7 @@ public class WelcomeDialogFragment extends DialogFragment {
} }
private void onDone() { private void onDone() {
PreferencesUtils.setBoolean(getActivity(), R.string.show_welcome_dialog_key, false); EulaUtils.setShowWelcome(getActivity());
TrackListActivity trackListActivity = (TrackListActivity) getActivity(); TrackListActivity trackListActivity = (TrackListActivity) getActivity();
trackListActivity.showStartupDialogs(); trackListActivity.showStartupDialogs();
} }
@@ -31,6 +31,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParser;
@@ -42,19 +43,15 @@ import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.DefaultHandler;
/** /**
* Imports GPX XML files to the my tracks provider. * Imports GPX file to My Tracks.
* *
* TODO: Show progress indication to the user.
*
* @author Leif Hendrik Wilden * @author Leif Hendrik Wilden
* @author Steffen Horlacher * @author Steffen Horlacher
* @author Rodrigo Damazio * @author Rodrigo Damazio
*/ */
public class GpxImporter extends DefaultHandler { public class GpxImporter extends DefaultHandler {
/* // GPX tag names and attributes.
* GPX-XML tag names and attributes.
*/
private static final String TAG_TRACK = "trk"; private static final String TAG_TRACK = "trk";
private static final String TAG_TRACK_POINT = "trkpt"; private static final String TAG_TRACK_POINT = "trkpt";
private static final Object TAG_TRACK_SEGMENT = "trkseg"; private static final Object TAG_TRACK_SEGMENT = "trkseg";
@@ -66,20 +63,21 @@ public class GpxImporter extends DefaultHandler {
private static final String ATT_LON = "lon"; private static final String ATT_LON = "lon";
/** /**
* The maximum number of locations to buffer for bulk-insertion into the database. * The maximum number of locations to buffer for bulk-insertion into the
* database.
*/ */
private static final int MAX_BUFFERED_LOCATIONS = 512; private static final int MAX_BUFFERED_LOCATIONS = 512;
/** /**
* Utilities for accessing the contnet provider. * Utilities for accessing the content provider.
*/ */
private final MyTracksProviderUtils providerUtils; private final MyTracksProviderUtils myTracksProviderUtils;
/** /**
* List of track ids written in the database. Only contains successfully * List of track ids written in the database. Only contains successfully
* written tracks. * written tracks.
*/ */
private final List<Long> tracksWritten; private final List<Long> tracksIds;
/** /**
* Contains the current elements content. * Contains the current elements content.
@@ -87,15 +85,20 @@ public class GpxImporter extends DefaultHandler {
private String content; private String content;
/** /**
* Currently reading location. * Currently location.
*/ */
private Location location; private Location location;
/** /**
* Previous location, required for calculations. * Last location.
*/ */
private Location lastLocation; private Location lastLocation;
/**
* Last segment location.
*/
private Location lastSegmentLocation;
/** /**
* Currently reading track. * Currently reading track.
*/ */
@@ -104,7 +107,7 @@ public class GpxImporter extends DefaultHandler {
/** /**
* Statistics builder for the current track. * Statistics builder for the current track.
*/ */
private TripStatisticsBuilder statsBuilder; private TripStatisticsBuilder tripStatisticsBuilder;
/** /**
* Buffer of locations to be bulk-inserted into the database. * Buffer of locations to be bulk-inserted into the database.
@@ -115,82 +118,79 @@ public class GpxImporter extends DefaultHandler {
* Number of locations buffered to be inserted into the database. * Number of locations buffered to be inserted into the database.
*/ */
private int numBufferedPointInserts = 0; private int numBufferedPointInserts = 0;
/** /**
* Number of locations already processed. * Number of locations already processed.
*/ */
private int numberOfLocations; private int numberOfLocations = 0;
/** /**
* Number of segments already processed. * Number of segments already processed.
*/ */
private int numberOfSegments; private int numberOfSegments = 0;
/** /**
* Used to identify if a track was written to the database but not yet * Used to identify if a track was written to the database but not yet
* finished successfully. * finished successfully.
*/ */
private boolean isCurrentTrackRollbackable; private boolean isCurrentTrackRollbackable = false;
/** /**
* Flag to indicate if we're inside a track's xml element. * Flag to indicate if we're inside a track's xml element. Some sub elements
* Some sub elements like name may be used in other parts of the gpx file, * like name may be used in other parts of the GPX file, and we use this to
* and we use this to ignore them. * ignore them.
*/ */
private boolean isInTrackElement; private boolean isInTrackElement = false;
/** /**
* Counter to find out which child level of track we are processing. * Counter to find out which child level of the track we are processing.
*/ */
private int trackChildDepth; private int trackChildDepth = 0;
/** /**
* SAX-Locator to get current line information. * SAX locator to get the current line information.
*/ */
private Locator locator; private Locator locator;
private Location lastSegmentLocation;
/** /**
* Reads GPS tracks from a GPX file and writes tracks and their coordinates to * Reads GPS tracks from a GPX file and writes tracks and their coordinates to
* the database. * the database.
* *
* @param is a input steam with gpx-xml data * @param inputStream an input stream for the GPX file
* @return long[] array of track ids written in the database * @param myTracksProviderUtils my tracks provider utils
* @throws SAXException a parsing error * @return long[] array of track ids written to the database.
* @throws ParserConfigurationException internal error
* @throws IOException a file reading problem
*/ */
public static long[] importGPXFile(final InputStream is, public static long[] importGPXFile(
final MyTracksProviderUtils providerUtils) final InputStream inputStream, final MyTracksProviderUtils myTracksProviderUtils)
throws ParserConfigurationException, SAXException, IOException { throws ParserConfigurationException, SAXException, IOException {
SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
GpxImporter handler = new GpxImporter(providerUtils); SAXParser saxParser = saxParserFactory.newSAXParser();
SAXParser parser = factory.newSAXParser(); GpxImporter gpxImporter = new GpxImporter(myTracksProviderUtils);
long[] trackIds = null; long[] trackIds = new long[0];
try { try {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
parser.parse(is, handler); saxParser.parse(inputStream, gpxImporter);
long end = System.currentTimeMillis(); long end = System.currentTimeMillis();
Log.d(Constants.TAG, "Total import time: " + (end - start) + "ms"); Log.d(Constants.TAG, "Total import time: " + (end - start) + "ms");
trackIds = handler.getImportedTrackIds(); trackIds = gpxImporter.getImportedTrackIds();
if (trackIds.length == 0) {
throw new IOException("No track imported.");
}
} finally { } finally {
// delete track if not finished // Delete track if not finished
handler.rollbackUnfinishedTracks(); gpxImporter.rollbackUnfinishedTracks();
} }
return trackIds; return trackIds;
} }
/** public GpxImporter(MyTracksProviderUtils myTracksProviderUtils) {
* Constructor, requires providerUtils for writing tracks the database. this.myTracksProviderUtils = myTracksProviderUtils;
*/ tracksIds = new ArrayList<Long>();
public GpxImporter(MyTracksProviderUtils providerUtils) {
this.providerUtils = providerUtils;
tracksWritten = new ArrayList<Long>();
} }
@Override @Override
@@ -199,16 +199,18 @@ public class GpxImporter extends DefaultHandler {
if (content == null) { if (content == null) {
content = newContent; content = newContent;
} else { } else {
// In 99% of the cases, a single call to this method will be made for each /*
// sequence of characters we're interested in, so we'll rarely be * In 99% of the cases, a single call to this method will be made for each
// concatenating strings, thus not justifying the use of a StringBuilder. * sequence of characters we're interested in, so we'll rarely be
* concatenating strings, thus not justifying the use of a StringBuilder.
*/
content += newContent; content += newContent;
} }
} }
@Override @Override
public void startElement(String uri, String localName, String name, public void startElement(String uri, String localName, String name, Attributes attributes)
Attributes attributes) throws SAXException { throws SAXException {
if (isInTrackElement) { if (isInTrackElement) {
trackChildDepth++; trackChildDepth++;
if (localName.equals(TAG_TRACK_POINT)) { if (localName.equals(TAG_TRACK_POINT)) {
@@ -216,8 +218,7 @@ public class GpxImporter extends DefaultHandler {
} else if (localName.equals(TAG_TRACK_SEGMENT)) { } else if (localName.equals(TAG_TRACK_SEGMENT)) {
onTrackSegmentElementStart(); onTrackSegmentElementStart();
} else if (localName.equals(TAG_TRACK)) { } else if (localName.equals(TAG_TRACK)) {
String msg = createErrorMessage("Invalid GPX-XML detected"); throw new SAXException(createErrorMessage("Invalid GPX detected."));
throw new SAXException(msg);
} }
} else if (localName.equals(TAG_TRACK)) { } else if (localName.equals(TAG_TRACK)) {
isInTrackElement = true; isInTrackElement = true;
@@ -227,8 +228,7 @@ public class GpxImporter extends DefaultHandler {
} }
@Override @Override
public void endElement(String uri, String localName, String name) public void endElement(String uri, String localName, String name) throws SAXException {
throws SAXException {
if (!isInTrackElement) { if (!isInTrackElement) {
content = null; content = null;
return; return;
@@ -270,33 +270,58 @@ public class GpxImporter extends DefaultHandler {
} }
/** /**
* Create a new Track object and insert empty track in database. Track will be * Rolls back last track if possible.
* updated with missing values later. */
public void rollbackUnfinishedTracks() {
if (isCurrentTrackRollbackable) {
myTracksProviderUtils.deleteTrack(track.getId());
isCurrentTrackRollbackable = false;
}
}
/**
* On track element start.
*/ */
private void onTrackElementStart() { private void onTrackElementStart() {
track = new Track(); track = new Track();
numberOfLocations = 0; numberOfLocations = 0;
Uri trackUri = providerUtils.insertTrack(track); Uri uri = myTracksProviderUtils.insertTrack(track);
long trackId = Long.parseLong(trackUri.getLastPathSegment()); long trackId = Long.parseLong(uri.getLastPathSegment());
track.setId(trackId); track.setId(trackId);
isCurrentTrackRollbackable = true; isCurrentTrackRollbackable = true;
} }
private void onDescriptionElementEnd() { /**
track.setDescription(content.toString().trim()); * On track element end.
} */
private void onTrackElementEnd() {
if (lastLocation != null) {
flushPoints();
private void onNameElementEnd() { // Calculate statistics for the imported track
track.setName(content.toString().trim()); tripStatisticsBuilder.pauseAt(lastLocation.getTime());
track.setStopId(getLastPointId());
} else {
tripStatisticsBuilder = new TripStatisticsBuilder(0);
tripStatisticsBuilder.pauseAt(0);
}
track.setTripStatistics(tripStatisticsBuilder.getStatistics());
track.setNumberOfPoints(numberOfLocations);
myTracksProviderUtils.updateTrack(track);
tracksIds.add(track.getId());
isCurrentTrackRollbackable = false;
lastSegmentLocation = null;
lastLocation = null;
tripStatisticsBuilder = null;
} }
/** /**
* Track segment started. * On track segment start.
*/ */
private void onTrackSegmentElementStart() { private void onTrackSegmentElementStart() {
if (numberOfSegments > 0) { if (numberOfSegments > 0) {
// Add a segment separator: // Add a segment separator
location = new Location(LocationManager.GPS_PROVIDER); location = new Location(LocationManager.GPS_PROVIDER);
location.setLatitude(100.0); location.setLatitude(100.0);
location.setLongitude(100.0); location.setLongitude(100.0);
@@ -304,73 +329,61 @@ public class GpxImporter extends DefaultHandler {
if (lastLocation != null) { if (lastLocation != null) {
location.setTime(lastLocation.getTime()); location.setTime(lastLocation.getTime());
} }
insertTrackPoint(location); insertPoint(location);
lastLocation = location; lastLocation = location;
lastSegmentLocation = null; lastSegmentLocation = null;
location = null; location = null;
} }
numberOfSegments++; numberOfSegments++;
} }
/** /**
* Reads trackpoint attributes and assigns them to the current location. * On track segment element end.
* */
* @param attributes xml attributes private void onTrackSegmentElementEnd() {
// Nothing to be done
}
/**
* On track point element start.
*
* @param attributes the attributes
*/ */
private void onTrackPointElementStart(Attributes attributes) throws SAXException { private void onTrackPointElementStart(Attributes attributes) throws SAXException {
if (location != null) { if (location != null) {
String errorMsg = createErrorMessage("Found a track point inside another one."); throw new SAXException(createErrorMessage("Found a track point inside another one."));
throw new SAXException(errorMsg);
} }
location = createLocationFromAttributes(attributes);
}
/**
* Creates and returns a location with the position parsed from the given
* attributes.
*
* @param attributes the attributes to parse
* @return the created location
* @throws SAXException if the attributes cannot be parsed
*/
private Location createLocationFromAttributes(Attributes attributes) throws SAXException {
String latitude = attributes.getValue(ATT_LAT); String latitude = attributes.getValue(ATT_LAT);
String longitude = attributes.getValue(ATT_LON); String longitude = attributes.getValue(ATT_LON);
if (latitude == null || longitude == null) { if (latitude == null || longitude == null) {
throw new SAXException(createErrorMessage("Point with no longitude or latitude")); throw new SAXException(createErrorMessage("Point with no longitude or latitude."));
} }
// create new location and set attributes Location newLocation = new Location(LocationManager.GPS_PROVIDER);
Location loc = new Location(LocationManager.GPS_PROVIDER);
try { try {
loc.setLatitude(Double.parseDouble(latitude)); newLocation.setLatitude(Double.parseDouble(latitude));
loc.setLongitude(Double.parseDouble(longitude)); newLocation.setLongitude(Double.parseDouble(longitude));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
String msg = createErrorMessage( throw new SAXException(createErrorMessage(
"Unable to parse lat/long: " + latitude + "/" + longitude); "Unable to parse latitude/longitude: " + latitude + "/" + longitude), e);
throw new SAXException(msg, e);
} }
return loc; location = newLocation;
} }
/** /**
* Track point finished, write in database. * On track point element end.
*
* @throws SAXException - thrown if track point is invalid
*/ */
private void onTrackPointElementEnd() throws SAXException { private void onTrackPointElementEnd() throws SAXException {
if (LocationUtils.isValidLocation(location)) { if (LocationUtils.isValidLocation(location)) {
if (statsBuilder == null) { if (tripStatisticsBuilder == null) {
// first point did not have a time, start stats builder without it // first point did not have a time, start stats builder without it
statsBuilder = new TripStatisticsBuilder(0); tripStatisticsBuilder = new TripStatisticsBuilder(0);
} }
statsBuilder.addLocation(location, location.getTime()); tripStatisticsBuilder.addLocation(location, location.getTime());
// insert in db // insert in db
insertTrackPoint(location); insertPoint(location);
// first track point? // first track point?
if (lastLocation == null && numberOfSegments == 1) { if (lastLocation == null && numberOfSegments == 1) {
@@ -382,78 +395,31 @@ public class GpxImporter extends DefaultHandler {
location = null; location = null;
} else { } else {
// invalid location - abort import // invalid location - abort import
String msg = createErrorMessage("Invalid location detected: " + location); throw new SAXException(createErrorMessage("Invalid location detected: " + location));
throw new SAXException(msg);
}
}
private void insertTrackPoint(Location loc) {
bufferedPointInserts[numBufferedPointInserts] = loc;
numBufferedPointInserts++;
numberOfLocations++;
if (numBufferedPointInserts >= MAX_BUFFERED_LOCATIONS) {
flushPointInserts();
}
}
private void flushPointInserts() {
if (numBufferedPointInserts <= 0) { return; }
providerUtils.bulkInsertTrackPoints(bufferedPointInserts, numBufferedPointInserts, track.getId());
numBufferedPointInserts = 0;
}
/**
* Track segment finished.
*/
private void onTrackSegmentElementEnd() {
// Nothing to be done
}
/**
* Track finished - update in database.
*/
private void onTrackElementEnd() {
if (lastLocation != null) {
flushPointInserts();
// Calculate statistics for the imported track and update
statsBuilder.pauseAt(lastLocation.getTime());
track.setStopId(getLastPointId());
track.setNumberOfPoints(numberOfLocations);
track.setTripStatistics(statsBuilder.getStatistics());
providerUtils.updateTrack(track);
tracksWritten.add(track.getId());
isCurrentTrackRollbackable = false;
lastSegmentLocation = null;
lastLocation = null;
statsBuilder = null;
} else {
// track contains no track points makes no real
// sense to import it as we have no location
// information -> roll back
rollbackUnfinishedTracks();
} }
} }
/** /**
* Setting time and doing additional calculations as this is the last value * On time element end. Sets location time and doing additional calculations
* required. Also sets the start time for track and statistics as there is no * as this is the last value required for the location. Also sets the start time
* start time in the track root element. * for the trip statistics builder as there is no start time in the track root
* * element.
* @throws SAXException on parsing errors
*/ */
private void onTimeElementEnd() throws SAXException { private void onTimeElementEnd() throws SAXException {
if (location == null) { return; } if (location == null) {
return;
}
if (content == null) {
return;
}
// Parse the time // Parse the time
long time; long time;
try { try {
time = StringUtils.getTime(content.trim()); time = StringUtils.getTime(content.trim());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
String msg = createErrorMessage("Unable to parse time: " + content); throw new SAXException(createErrorMessage("Unable to parse time: " + content), e);
throw new SAXException(msg, e);
} }
// Calculate derived attributes from previous point // Calculate derived attributes from previous point
@@ -465,11 +431,12 @@ public class GpxImporter extends DefaultHandler {
Log.w(Constants.TAG, "Found negative time change."); Log.w(Constants.TAG, "Found negative time change.");
} else { } else {
// We don't have a speed and bearing in GPX, make something up from /*
// the last two points. * We don't have a speed and bearing in GPX, make something up from the
// TODO GPS points tend to have some inherent imprecision, * last two points. GPS points tend to have some inherent imprecision,
// speed and bearing will likely be off, so the statistics for things like * speed and bearing will likely be off, so the statistics for things
// max speed will also be off. * like max speed will also be off.
*/
float speed = location.distanceTo(lastLocation) * 1000.0f / timeDifference; float speed = location.distanceTo(lastLocation) * 1000.0f / timeDifference;
location.setSpeed(speed); location.setSpeed(speed);
location.setBearing(lastSegmentLocation.bearingTo(location)); location.setBearing(lastSegmentLocation.bearingTo(location));
@@ -480,69 +447,96 @@ public class GpxImporter extends DefaultHandler {
location.setTime(time); location.setTime(time);
// initialize start time with time of first track point // initialize start time with time of first track point
if (statsBuilder == null) { if (tripStatisticsBuilder == null) {
statsBuilder = new TripStatisticsBuilder(time); tripStatisticsBuilder = new TripStatisticsBuilder(time);
} }
} }
/**
* On altitude element end.
*/
private void onAltitudeElementEnd() throws SAXException { private void onAltitudeElementEnd() throws SAXException {
if (location != null) { if (location != null && content != null) {
try { try {
location.setAltitude(Double.parseDouble(content)); location.setAltitude(Double.parseDouble(content));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
String msg = createErrorMessage("Unable to parse altitude: " + content); throw new SAXException(createErrorMessage("Unable to parse altitude: " + content), e);
throw new SAXException(msg, e);
} }
} }
} }
/** /**
* Deletes the last track if it was not completely imported. * On name element end.
*/ */
public void rollbackUnfinishedTracks() { private void onNameElementEnd() {
if (isCurrentTrackRollbackable) { if (content != null) {
providerUtils.deleteTrack(track.getId()); track.setName(content.toString().trim());
isCurrentTrackRollbackable = false;
} }
} }
/** /**
* Get all track ids of the tracks created by this importer run. * On description element end.
*/
private void onDescriptionElementEnd() {
if (content != null) {
track.setDescription(content.toString().trim());
}
}
/**
* Inserts a point.
* *
* @return array of track ids * @param newLocation the location
*/
private void insertPoint(Location newLocation) {
bufferedPointInserts[numBufferedPointInserts] = newLocation;
numBufferedPointInserts++;
numberOfLocations++;
if (numBufferedPointInserts >= MAX_BUFFERED_LOCATIONS) {
flushPoints();
}
}
/**
* Flushes the points to the database.
*/
private void flushPoints() {
if (numBufferedPointInserts <= 0) {
return;
}
myTracksProviderUtils.bulkInsertTrackPoints(
bufferedPointInserts, numBufferedPointInserts, track.getId());
numBufferedPointInserts = 0;
}
/**
* Gets the imported track ids.
*
*/ */
private long[] getImportedTrackIds() { private long[] getImportedTrackIds() {
// Convert from java.lang.Long for convenience long[] result = new long[tracksIds.size()];
long[] result = new long[tracksWritten.size()];
for (int i = 0; i < result.length; i++) { for (int i = 0; i < result.length; i++) {
result[i] = tracksWritten.get(i); result[i] = tracksIds.get(i);
} }
return result; return result;
} }
/** /**
* Returns the ID of the last point inserted into the database. * Gets the last point id inserted into the database.
*/ */
private long getLastPointId() { private long getLastPointId() {
flushPointInserts(); flushPoints();
return myTracksProviderUtils.getLastLocationId(track.getId());
return providerUtils.getLastLocationId(track.getId());
} }
/** /**
* Builds a parsing error message with current line information. * Creates an error message.
* *
* @param details details about the error, will be appended * @param message the message
* @return error message string with current line information
*/ */
private String createErrorMessage(String details) { private String createErrorMessage(String message) {
StringBuffer msg = new StringBuffer(); return String.format(Locale.US, "Parsing error at line: %d column: %d. %s",
msg.append("Parsing error at line: "); locator.getLineNumber(), locator.getColumnNumber(), message);
msg.append(locator.getLineNumber());
msg.append(" column: ");
msg.append(locator.getColumnNumber());
msg.append(". ");
msg.append(details);
return msg.toString();
} }
} }
@@ -318,7 +318,9 @@ class TrackWriterImpl implements TrackWriter {
locationFactory); locationFactory);
try { try {
if (!it.hasNext()) { if (!it.hasNext()) {
Log.w(Constants.TAG, "Unable to get any points to write"); // Writes an empty track
writer.writeBeginTrack(null);
writer.writeEndTrack(null);
return; return;
} }
int pointNumber = 0; int pointNumber = 0;
@@ -29,21 +29,47 @@ public class EulaUtils {
private static final String EULA_PREFERENCE_FILE = "eula"; private static final String EULA_PREFERENCE_FILE = "eula";
// Accepting Google mobile terms of service // Accepting Google mobile terms of service
private static final String EULA_PREFERENCE_KEY = "eula.google_mobile_tos_accepted"; private static final String ACCEPT_EULA_PREFERENCE_KEY = "eula.google_mobile_tos_accepted";
private static final String SHOW_WELCOME_PREFERENCE_KEY = "showWelcome";
private static final String SHOW_CHECK_UNITS_PREFERENCE_KEY = "showCheckUnits";
private EulaUtils() {} private EulaUtils() {}
public static boolean getEulaValue(Context context) { public static boolean getAcceptEula(Context context) {
SharedPreferences sharedPreferences = context.getSharedPreferences( return getValue(context, ACCEPT_EULA_PREFERENCE_KEY, false);
EULA_PREFERENCE_FILE, Context.MODE_PRIVATE);
return sharedPreferences.getBoolean(EULA_PREFERENCE_KEY, false);
} }
public static void setEulaValue(Context context) { public static void setAcceptEula(Context context) {
setValue(context, ACCEPT_EULA_PREFERENCE_KEY, true);
}
public static boolean getShowWelcome(Context context) {
return getValue(context, SHOW_WELCOME_PREFERENCE_KEY, true);
}
public static void setShowWelcome(Context context) {
setValue(context, SHOW_WELCOME_PREFERENCE_KEY, false);
}
public static boolean getShowCheckUnits(Context context) {
return getValue(context, SHOW_CHECK_UNITS_PREFERENCE_KEY, true);
}
public static void setShowCheckUnits(Context context) {
setValue(context, SHOW_CHECK_UNITS_PREFERENCE_KEY, false);
}
private static boolean getValue(Context context, String key, boolean defaultValue) {
SharedPreferences sharedPreferences = context.getSharedPreferences(
EULA_PREFERENCE_FILE, Context.MODE_PRIVATE);
return sharedPreferences.getBoolean(key, defaultValue);
}
private static void setValue(Context context, String key, boolean value) {
SharedPreferences sharedPreferences = context.getSharedPreferences( SharedPreferences sharedPreferences = context.getSharedPreferences(
EULA_PREFERENCE_FILE, Context.MODE_PRIVATE); EULA_PREFERENCE_FILE, Context.MODE_PRIVATE);
Editor editor = sharedPreferences.edit(); Editor editor = sharedPreferences.edit();
editor.putBoolean(EULA_PREFERENCE_KEY, true); editor.putBoolean(key, value);
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor); ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
} }
} }
@@ -73,9 +73,7 @@ public class PreferencesUtils {
public static final boolean SEND_TO_DOCS_DEFAULT = true; public static final boolean SEND_TO_DOCS_DEFAULT = true;
public static final boolean SEND_TO_FUSION_TABLES_DEFAULT = true; public static final boolean SEND_TO_FUSION_TABLES_DEFAULT = true;
public static final boolean SEND_TO_MAPS_DEFAULT = true; public static final boolean SEND_TO_MAPS_DEFAULT = true;
public static final boolean SHOW_CHECK_UNITS_DIALOG_DEFAULT = true;
public static final boolean SHOW_CONFIRM_SHARING_DIALOG_DEFAULT = true; public static final boolean SHOW_CONFIRM_SHARING_DIALOG_DEFAULT = true;
public static final boolean SHOW_WELCOME_DIALOG_DEFAULT = true;
public static final int SPLIT_FREQUENCY_DEFAULT = 0; public static final int SPLIT_FREQUENCY_DEFAULT = 0;
public static final boolean STATS_SHOW_COORDINATE_DEFAULT = false; public static final boolean STATS_SHOW_COORDINATE_DEFAULT = false;
@@ -225,6 +225,8 @@ public class TrackWriterTest extends AndroidTestCase {
// Set expected mock behavior // Set expected mock behavior
formatWriter.writeHeader(); formatWriter.writeHeader();
formatWriter.writeBeginTrack(null);
formatWriter.writeEndTrack(null);
formatWriter.writeFooter(); formatWriter.writeFooter();
formatWriter.close(); formatWriter.close();
@@ -67,6 +67,7 @@ public class SendFusionTablesUtilsTest extends TestCase {
*/ */
public void testGetMapUrl_valid_track() { public void testGetMapUrl_valid_track() {
Track track = new Track(); Track track = new Track();
track.setNumberOfPoints(2);
TripStatistics stats = new TripStatistics(); TripStatistics stats = new TripStatistics();
stats.setBounds((int) 100.E6, (int) 10.E6, (int) 50.E6, (int) 5.E6); stats.setBounds((int) 100.E6, (int) 10.E6, (int) 50.E6, (int) 5.E6);
track.setTripStatistics(stats); track.setTripStatistics(stats);