forked from upstream-mirrors/OpenTracks
Add fractional seconds to timestamp in GPX, KML, CSV, and TCX.
Move FileUtils.FILE_TIMSTAMP_FORMAT to StringUtils.java Clean up StringUtils.java - add formatDateTimeIso8601 - add formatDistance - add formatSpeed - rename all methods to either formatXXX or getXXX
This commit is contained in:
@@ -2596,13 +2596,53 @@ limitations under the License.
|
|||||||
Screenshot: value_auto_resume.png">
|
Screenshot: value_auto_resume.png">
|
||||||
Always
|
Always
|
||||||
</string>
|
</string>
|
||||||
<string name="value_float_mile"
|
<string name="value_float_feet"
|
||||||
translation_description="In a setting picker, the string format to display a float value in
|
translation_description="Display a float value in feet (ft).
|
||||||
miles (mi).
|
|
||||||
|
|
||||||
%1$.1f: a float number. E.g., '2.2'
|
%1$.2f: a float number. E.g., '2.22'">
|
||||||
Screenshot: value_gps_accuracy_mile_poor_gps.png">
|
%1$.2f ft
|
||||||
%1$.1f mi
|
</string>
|
||||||
|
<string name="value_float_kilometer"
|
||||||
|
translation_description="Display a float value in kilometers (km).
|
||||||
|
|
||||||
|
%1$.2f: a float number. E.g., '2.22'">
|
||||||
|
%1$.2f km
|
||||||
|
</string>
|
||||||
|
<string name="value_float_kilometer_hour"
|
||||||
|
translation_description="Display a float value in kilometers per hour (km/h).
|
||||||
|
|
||||||
|
%1$.2f: a float number. E.g., '2.22'">
|
||||||
|
%1$.2f km/h
|
||||||
|
</string>
|
||||||
|
<string name="value_float_meter"
|
||||||
|
translation_description="Display a float value in meters (m).
|
||||||
|
|
||||||
|
%1$.2f: a float number. E.g., '2.22'">
|
||||||
|
%1$.2f m
|
||||||
|
</string>
|
||||||
|
<string name="value_float_mile"
|
||||||
|
translation_description="Display a float value in miles (mi).
|
||||||
|
|
||||||
|
%1$.2f: a float number. E.g., '2.22'">
|
||||||
|
%1$.2f mi
|
||||||
|
</string>
|
||||||
|
<string name="value_float_mile_hour"
|
||||||
|
translation_description="Display a float value in miles per hour (mi/h).
|
||||||
|
|
||||||
|
%1$.2f: a float number. E.g., '2.22'">
|
||||||
|
%1$.2f mi/h
|
||||||
|
</string>
|
||||||
|
<string name="value_float_minute_kilometer"
|
||||||
|
translation_description="Display a float value in minutes per kilometer (min/km).
|
||||||
|
|
||||||
|
%1$.2f: a float number. E.g., '2.22'">
|
||||||
|
%1$.2f min/km
|
||||||
|
</string>
|
||||||
|
<string name="value_float_minute_mile"
|
||||||
|
translation_description="Display a float value in minutes per mile (min/mi).
|
||||||
|
|
||||||
|
%1$.2f: a float number. E.g., '2.22'">
|
||||||
|
%1$.2f min/mi
|
||||||
</string>
|
</string>
|
||||||
<string name="value_float_mile_poor_gps"
|
<string name="value_float_mile_poor_gps"
|
||||||
translation_description="In a setting picker, the string format to display a float value in
|
translation_description="In a setting picker, the string format to display a float value in
|
||||||
|
|||||||
@@ -207,9 +207,8 @@ public class SearchActivity extends ListActivity {
|
|||||||
resultMap.put(DESCRIPTION_FIELD, track.getDescription());
|
resultMap.put(DESCRIPTION_FIELD, track.getDescription());
|
||||||
resultMap.put(CATEGORY_FIELD, track.getCategory());
|
resultMap.put(CATEGORY_FIELD, track.getCategory());
|
||||||
resultMap.put(TIME_FIELD, StringUtils.formatDateTime(this, stats.getStartTime()));
|
resultMap.put(TIME_FIELD, StringUtils.formatDateTime(this, stats.getStartTime()));
|
||||||
resultMap.put(STATS_FIELD,
|
resultMap.put(STATS_FIELD, StringUtils.formatTimeDistance(
|
||||||
StringUtils.formatTimeDistance(this, stats.getTotalDistance(), stats.getTotalTime(),
|
this, stats.getTotalTime(), stats.getTotalDistance(), metricUnits));
|
||||||
metricUnits));
|
|
||||||
resultMap.put(TRACK_ID_FIELD, track.getId());
|
resultMap.put(TRACK_ID_FIELD, track.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -349,7 +349,8 @@ public class TrackList extends ListActivity
|
|||||||
} else if (columnIndex == totalDistanceIdx) {
|
} else if (columnIndex == totalDistanceIdx) {
|
||||||
double totalDistance = cursor.getDouble(totalDistanceIdx);
|
double totalDistance = cursor.getDouble(totalDistanceIdx);
|
||||||
long totalTime = cursor.getLong(totalTimeIdx);
|
long totalTime = cursor.getLong(totalTimeIdx);
|
||||||
String totalDistanceStr = StringUtils.formatTimeDistance(TrackList.this, totalDistance, totalTime, metricUnits);
|
String totalDistanceStr = StringUtils.formatTimeDistance(
|
||||||
|
TrackList.this, totalTime, totalDistance, metricUnits);
|
||||||
textView.setText(totalDistanceStr);
|
textView.setText(totalDistanceStr);
|
||||||
} else {
|
} else {
|
||||||
textView.setText(cursor.getString(columnIndex));
|
textView.setText(cursor.getString(columnIndex));
|
||||||
|
|||||||
@@ -335,7 +335,7 @@ public class SendDocsUtils {
|
|||||||
.append("<gsx:")
|
.append("<gsx:")
|
||||||
.append(name)
|
.append(name)
|
||||||
.append(">")
|
.append(">")
|
||||||
.append(StringUtils.stringAsCData(value))
|
.append(StringUtils.formatCData(value))
|
||||||
.append("</gsx:")
|
.append("</gsx:")
|
||||||
.append(name)
|
.append(name)
|
||||||
.append(">");
|
.append(">");
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
|
|||||||
import com.google.android.apps.mytracks.content.Track;
|
import com.google.android.apps.mytracks.content.Track;
|
||||||
import com.google.android.apps.mytracks.content.Waypoint;
|
import com.google.android.apps.mytracks.content.Waypoint;
|
||||||
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
|
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
|
||||||
import com.google.android.apps.mytracks.util.FileUtils;
|
import com.google.android.apps.mytracks.util.StringUtils;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -130,7 +130,7 @@ public class CsvTrackWriter implements TrackFormatWriter {
|
|||||||
Double.toString(location.getBearing()),
|
Double.toString(location.getBearing()),
|
||||||
SHORT_FORMAT.format(location.getAccuracy()),
|
SHORT_FORMAT.format(location.getAccuracy()),
|
||||||
SHORT_FORMAT.format(location.getSpeed()),
|
SHORT_FORMAT.format(location.getSpeed()),
|
||||||
FileUtils.FILE_TIMESTAMP_FORMAT.format(location.getTime()));
|
StringUtils.formatDateTimeIso8601(location.getTime()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -211,7 +211,7 @@ public class CsvTrackWriter implements TrackFormatWriter {
|
|||||||
Double.toString(location.getBearing()),
|
Double.toString(location.getBearing()),
|
||||||
SHORT_FORMAT.format(location.getAccuracy()),
|
SHORT_FORMAT.format(location.getAccuracy()),
|
||||||
SHORT_FORMAT.format(location.getSpeed()),
|
SHORT_FORMAT.format(location.getSpeed()),
|
||||||
FileUtils.FILE_TIMESTAMP_FORMAT.format(location.getTime()),
|
StringUtils.formatDateTimeIso8601(location.getTime()),
|
||||||
power,
|
power,
|
||||||
cadence,
|
cadence,
|
||||||
heartRate,
|
heartRate,
|
||||||
|
|||||||
@@ -450,7 +450,7 @@ public class GpxImporter extends DefaultHandler {
|
|||||||
// Parse the time
|
// Parse the time
|
||||||
long time;
|
long time;
|
||||||
try {
|
try {
|
||||||
time = StringUtils.parseXmlDateTime(content.trim());
|
time = StringUtils.getTime(content.trim());
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
String msg = createErrorMessage("Unable to parse time: " + content);
|
String msg = createErrorMessage("Unable to parse time: " + content);
|
||||||
throw new SAXException(msg, e);
|
throw new SAXException(msg, e);
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package com.google.android.apps.mytracks.io.file;
|
|||||||
import com.google.android.apps.mytracks.content.Track;
|
import com.google.android.apps.mytracks.content.Track;
|
||||||
import com.google.android.apps.mytracks.content.Waypoint;
|
import com.google.android.apps.mytracks.content.Waypoint;
|
||||||
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
|
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
|
||||||
import com.google.android.apps.mytracks.util.FileUtils;
|
|
||||||
import com.google.android.apps.mytracks.util.StringUtils;
|
import com.google.android.apps.mytracks.util.StringUtils;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
|
|
||||||
@@ -94,8 +93,8 @@ public class GpxTrackWriter implements TrackFormatWriter {
|
|||||||
+ " http://www.topografix.com/GPX/Private/TopoGrafix/0/1"
|
+ " http://www.topografix.com/GPX/Private/TopoGrafix/0/1"
|
||||||
+ " http://www.topografix.com/GPX/Private/TopoGrafix/0/1/topografix.xsd\">");
|
+ " http://www.topografix.com/GPX/Private/TopoGrafix/0/1/topografix.xsd\">");
|
||||||
printWriter.println("<metadata>");
|
printWriter.println("<metadata>");
|
||||||
printWriter.println("<name>" + StringUtils.stringAsCData(track.getName()) + "</name>");
|
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||||
printWriter.println("<desc>" + StringUtils.stringAsCData(track.getDescription()) + "</desc>");
|
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
|
||||||
printWriter.println("</metadata>");
|
printWriter.println("</metadata>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,8 +110,8 @@ public class GpxTrackWriter implements TrackFormatWriter {
|
|||||||
public void writeBeginTrack(Location firstLocation) {
|
public void writeBeginTrack(Location firstLocation) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<trk>");
|
printWriter.println("<trk>");
|
||||||
printWriter.println("<name>" + StringUtils.stringAsCData(track.getName()) + "</name>");
|
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||||
printWriter.println("<desc>" + StringUtils.stringAsCData(track.getDescription()) + "</desc>");
|
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
|
||||||
printWriter.println("<extensions><topografix:color>c0c0c0</topografix:color></extensions>");
|
printWriter.println("<extensions><topografix:color>c0c0c0</topografix:color></extensions>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,8 +138,7 @@ public class GpxTrackWriter implements TrackFormatWriter {
|
|||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<trkpt " + formatLocation(location) + ">");
|
printWriter.println("<trkpt " + formatLocation(location) + ">");
|
||||||
printWriter.println("<ele>" + ELEVATION_FORMAT.format(location.getAltitude()) + "</ele>");
|
printWriter.println("<ele>" + ELEVATION_FORMAT.format(location.getAltitude()) + "</ele>");
|
||||||
printWriter.println(
|
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</time>");
|
||||||
"<time>" + FileUtils.FILE_TIMESTAMP_FORMAT.format(location.getTime()) + "</time>");
|
|
||||||
printWriter.println("</trkpt>");
|
printWriter.println("</trkpt>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,11 +160,10 @@ public class GpxTrackWriter implements TrackFormatWriter {
|
|||||||
if (location != null) {
|
if (location != null) {
|
||||||
printWriter.println("<wpt " + formatLocation(location) + ">");
|
printWriter.println("<wpt " + formatLocation(location) + ">");
|
||||||
printWriter.println("<ele>" + ELEVATION_FORMAT.format(location.getAltitude()) + "</ele>");
|
printWriter.println("<ele>" + ELEVATION_FORMAT.format(location.getAltitude()) + "</ele>");
|
||||||
|
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</time>");
|
||||||
|
printWriter.println("<name>" + StringUtils.formatCData(waypoint.getName()) + "</name>");
|
||||||
printWriter.println(
|
printWriter.println(
|
||||||
"<time>" + FileUtils.FILE_TIMESTAMP_FORMAT.format(location.getTime()) + "</time>");
|
"<desc>" + StringUtils.formatCData(waypoint.getDescription()) + "</desc>");
|
||||||
printWriter.println("<name>" + StringUtils.stringAsCData(waypoint.getName()) + "</name>");
|
|
||||||
printWriter.println(
|
|
||||||
"<desc>" + StringUtils.stringAsCData(waypoint.getDescription()) + "</desc>");
|
|
||||||
printWriter.println("</wpt>");
|
printWriter.println("</wpt>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
|
|||||||
import com.google.android.apps.mytracks.content.Track;
|
import com.google.android.apps.mytracks.content.Track;
|
||||||
import com.google.android.apps.mytracks.content.Waypoint;
|
import com.google.android.apps.mytracks.content.Waypoint;
|
||||||
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
|
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
|
||||||
import com.google.android.apps.mytracks.util.FileUtils;
|
|
||||||
import com.google.android.apps.mytracks.util.StringUtils;
|
import com.google.android.apps.mytracks.util.StringUtils;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
@@ -107,9 +106,9 @@ public class KmlTrackWriter implements TrackFormatWriter {
|
|||||||
printWriter.println("<open>1</open>");
|
printWriter.println("<open>1</open>");
|
||||||
printWriter.println("<visibility>1</visibility>");
|
printWriter.println("<visibility>1</visibility>");
|
||||||
printWriter.println(
|
printWriter.println(
|
||||||
"<description>" + StringUtils.stringAsCData(track.getDescription()) + "</description>");
|
"<description>" + StringUtils.formatCData(track.getDescription()) + "</description>");
|
||||||
printWriter.println("<name>" + StringUtils.stringAsCData(track.getName()) + "</name>");
|
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||||
printWriter.println("<atom:author><atom:name>" + StringUtils.stringAsCData(
|
printWriter.println("<atom:author><atom:name>" + StringUtils.formatCData(
|
||||||
context.getString(R.string.send_google_by_my_tracks, "", ""))
|
context.getString(R.string.send_google_by_my_tracks, "", ""))
|
||||||
+ "</atom:name></atom:author>");
|
+ "</atom:name></atom:author>");
|
||||||
writeTrackStyle();
|
writeTrackStyle();
|
||||||
@@ -142,7 +141,7 @@ public class KmlTrackWriter implements TrackFormatWriter {
|
|||||||
public void writeBeginWaypoints() {
|
public void writeBeginWaypoints() {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println(
|
printWriter.println(
|
||||||
"<Folder><name>" + StringUtils.stringAsCData(context.getString(R.string.menu_markers))
|
"<Folder><name>" + StringUtils.formatCData(context.getString(R.string.menu_markers))
|
||||||
+ "</name>");
|
+ "</name>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,8 +170,8 @@ public class KmlTrackWriter implements TrackFormatWriter {
|
|||||||
writePlacemark(name, track.getDescription(), START_STYLE, firstLocation);
|
writePlacemark(name, track.getDescription(), START_STYLE, firstLocation);
|
||||||
printWriter.println("<Placemark>");
|
printWriter.println("<Placemark>");
|
||||||
printWriter.println(
|
printWriter.println(
|
||||||
"<description>" + StringUtils.stringAsCData(track.getDescription()) + "</description>");
|
"<description>" + StringUtils.formatCData(track.getDescription()) + "</description>");
|
||||||
printWriter.println("<name>" + StringUtils.stringAsCData(track.getName()) + "</name>");
|
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||||
printWriter.println("<styleUrl>#" + TRACK_STYLE + "</styleUrl>");
|
printWriter.println("<styleUrl>#" + TRACK_STYLE + "</styleUrl>");
|
||||||
printWriter.println("<gx:MultiTrack>");
|
printWriter.println("<gx:MultiTrack>");
|
||||||
printWriter.println("<altitudeMode>absolute</altitudeMode>");
|
printWriter.println("<altitudeMode>absolute</altitudeMode>");
|
||||||
@@ -232,8 +231,7 @@ public class KmlTrackWriter implements TrackFormatWriter {
|
|||||||
@Override
|
@Override
|
||||||
public void writeLocation(Location location) {
|
public void writeLocation(Location location) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println(
|
printWriter.println("<when>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</when>");
|
||||||
"<when>" + FileUtils.FILE_TIMESTAMP_FORMAT.format(location.getTime()) + "</when>");
|
|
||||||
printWriter.println(
|
printWriter.println(
|
||||||
"<gx:coord>" + location.getLongitude() + " " + location.getLatitude() + " "
|
"<gx:coord>" + location.getLongitude() + " " + location.getLatitude() + " "
|
||||||
+ location.getAltitude() + "</gx:coord>");
|
+ location.getAltitude() + "</gx:coord>");
|
||||||
@@ -309,8 +307,8 @@ public class KmlTrackWriter implements TrackFormatWriter {
|
|||||||
if (location != null) {
|
if (location != null) {
|
||||||
printWriter.println("<Placemark>");
|
printWriter.println("<Placemark>");
|
||||||
printWriter.println(
|
printWriter.println(
|
||||||
"<description>" + StringUtils.stringAsCData(description) + "</description>");
|
"<description>" + StringUtils.formatCData(description) + "</description>");
|
||||||
printWriter.println("<name>" + StringUtils.stringAsCData(name) + "</name>");
|
printWriter.println("<name>" + StringUtils.formatCData(name) + "</name>");
|
||||||
printWriter.println("<styleUrl>#" + styleName + "</styleUrl>");
|
printWriter.println("<styleUrl>#" + styleName + "</styleUrl>");
|
||||||
printWriter.println("<Point>");
|
printWriter.println("<Point>");
|
||||||
printWriter.println(
|
printWriter.println(
|
||||||
@@ -356,7 +354,7 @@ public class KmlTrackWriter implements TrackFormatWriter {
|
|||||||
private void writeSensorStyle(String name, String displayName) {
|
private void writeSensorStyle(String name, String displayName) {
|
||||||
printWriter.println("<gx:SimpleArrayField name=\"" + name + "\" type=\"int\">");
|
printWriter.println("<gx:SimpleArrayField name=\"" + name + "\" type=\"int\">");
|
||||||
printWriter.println(
|
printWriter.println(
|
||||||
"<displayName>" + StringUtils.stringAsCData(displayName) + "</displayName>");
|
"<displayName>" + StringUtils.formatCData(displayName) + "</displayName>");
|
||||||
printWriter.println("</gx:SimpleArrayField>");
|
printWriter.println("</gx:SimpleArrayField>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import com.google.android.apps.mytracks.content.Track;
|
|||||||
import com.google.android.apps.mytracks.content.Waypoint;
|
import com.google.android.apps.mytracks.content.Waypoint;
|
||||||
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
|
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
|
||||||
import com.google.android.apps.mytracks.lib.R;
|
import com.google.android.apps.mytracks.lib.R;
|
||||||
import com.google.android.apps.mytracks.util.FileUtils;
|
|
||||||
import com.google.android.apps.mytracks.util.StringUtils;
|
import com.google.android.apps.mytracks.util.StringUtils;
|
||||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||||
|
|
||||||
@@ -31,7 +30,6 @@ import android.location.Location;
|
|||||||
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -133,7 +131,7 @@ public class TcxTrackWriter implements TrackFormatWriter {
|
|||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<Author xsi:type=\"Application_t\">");
|
printWriter.println("<Author xsi:type=\"Application_t\">");
|
||||||
printWriter.println("<Name>"
|
printWriter.println("<Name>"
|
||||||
+ StringUtils.stringAsCData(context.getString(R.string.send_google_by_my_tracks, "", ""))
|
+ StringUtils.formatCData(context.getString(R.string.send_google_by_my_tracks, "", ""))
|
||||||
+ "</Name>");
|
+ "</Name>");
|
||||||
// <Build>, <LangID>, and <PartNumber> are required by type=Application_t.
|
// <Build>, <LangID>, and <PartNumber> are required by type=Application_t.
|
||||||
printWriter.println("<Build>");
|
printWriter.println("<Build>");
|
||||||
@@ -149,8 +147,7 @@ public class TcxTrackWriter implements TrackFormatWriter {
|
|||||||
@Override
|
@Override
|
||||||
public void writeBeginTrack(Location firstPoint) {
|
public void writeBeginTrack(Location firstPoint) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
String startTime = FileUtils.FILE_TIMESTAMP_FORMAT.format(
|
String startTime = StringUtils.formatDateTimeIso8601(track.getStatistics().getStartTime());
|
||||||
track.getStatistics().getStartTime());
|
|
||||||
long totalTimeInSeconds = track.getStatistics().getTotalTime() / 1000;
|
long totalTimeInSeconds = track.getStatistics().getTotalTime() / 1000;
|
||||||
|
|
||||||
printWriter.println("<Activities>");
|
printWriter.println("<Activities>");
|
||||||
@@ -173,7 +170,7 @@ public class TcxTrackWriter implements TrackFormatWriter {
|
|||||||
printWriter.println("</Lap>");
|
printWriter.println("</Lap>");
|
||||||
printWriter.println("<Creator xsi:type=\"Device_t\">");
|
printWriter.println("<Creator xsi:type=\"Device_t\">");
|
||||||
printWriter.println("<Name>"
|
printWriter.println("<Name>"
|
||||||
+ StringUtils.stringAsCData(context.getString(R.string.send_google_by_my_tracks, "", ""))
|
+ StringUtils.formatCData(context.getString(R.string.send_google_by_my_tracks, "", ""))
|
||||||
+ "</Name>");
|
+ "</Name>");
|
||||||
// <UnitId>, <ProductID>, and <Version> are required for type=Device_t.
|
// <UnitId>, <ProductID>, and <Version> are required for type=Device_t.
|
||||||
printWriter.println("<UnitId>0</UnitId>");
|
printWriter.println("<UnitId>0</UnitId>");
|
||||||
@@ -203,8 +200,7 @@ public class TcxTrackWriter implements TrackFormatWriter {
|
|||||||
public void writeLocation(Location location) {
|
public void writeLocation(Location location) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<Trackpoint>");
|
printWriter.println("<Trackpoint>");
|
||||||
printWriter.println("<Time>"
|
printWriter.println("<Time>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</Time>");
|
||||||
+ FileUtils.FILE_TIMESTAMP_FORMAT.format(new Date(location.getTime())) + "</Time>");
|
|
||||||
printWriter.println("<Position>");
|
printWriter.println("<Position>");
|
||||||
printWriter.println("<LatitudeDegrees>" + location.getLatitude() + "</LatitudeDegrees>");
|
printWriter.println("<LatitudeDegrees>" + location.getLatitude() + "</LatitudeDegrees>");
|
||||||
printWriter.println("<LongitudeDegrees>" + location.getLongitude() + "</LongitudeDegrees>");
|
printWriter.println("<LongitudeDegrees>" + location.getLongitude() + "</LongitudeDegrees>");
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ import com.google.common.annotations.VisibleForTesting;
|
|||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.TimeZone;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utilities for dealing with files.
|
* Utilities for dealing with files.
|
||||||
@@ -38,16 +36,6 @@ public class FileUtils {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final int MAX_FAT32_PATH_LENGTH = 260;
|
static final int MAX_FAT32_PATH_LENGTH = 260;
|
||||||
|
|
||||||
/**
|
|
||||||
* Timestamp format in UTC time zone.
|
|
||||||
*/
|
|
||||||
public static final SimpleDateFormat FILE_TIMESTAMP_FORMAT =
|
|
||||||
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
|
||||||
|
|
||||||
static {
|
|
||||||
FILE_TIMESTAMP_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a path inside the My Tracks directory in the SD card.
|
* Builds a path inside the My Tracks directory in the SD card.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -21,12 +21,10 @@ import android.content.Context;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.text.NumberFormat;
|
|
||||||
import java.text.ParsePosition;
|
import java.text.ParsePosition;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.SimpleTimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@@ -38,19 +36,20 @@ import java.util.regex.Pattern;
|
|||||||
*/
|
*/
|
||||||
public class StringUtils {
|
public class StringUtils {
|
||||||
|
|
||||||
private StringUtils() {}
|
private static final SimpleDateFormat ISO_8601_DATE_TIME_FORMAT = new SimpleDateFormat(
|
||||||
|
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
|
||||||
|
private static final SimpleDateFormat ISO_8601_BASE = new SimpleDateFormat(
|
||||||
|
"yyyy-MM-dd'T'HH:mm:ss");
|
||||||
|
private static final Pattern ISO_8601_EXTRAS = Pattern.compile(
|
||||||
|
"^(\\.\\d+)?(?:Z|([+-])(\\d{2}):(\\d{2}))?$");
|
||||||
|
|
||||||
/**
|
static {
|
||||||
* Formats the date and time based on user's phone date/time preferences.
|
ISO_8601_DATE_TIME_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
*
|
ISO_8601_BASE.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
* @param context the context
|
|
||||||
* @param time the time in milliseconds
|
|
||||||
*/
|
|
||||||
public static String formatDateTime(Context context, long time) {
|
|
||||||
DateFormat dateFormatter = android.text.format.DateFormat.getDateFormat(context);
|
|
||||||
return dateFormatter.format(new Date(time)) + " " + formatTime(context, time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private StringUtils() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats the time based on user's phone date/time preferences.
|
* Formats the time based on user's phone date/time preferences.
|
||||||
*
|
*
|
||||||
@@ -58,8 +57,28 @@ public class StringUtils {
|
|||||||
* @param time the time in milliseconds
|
* @param time the time in milliseconds
|
||||||
*/
|
*/
|
||||||
public static String formatTime(Context context, long time) {
|
public static String formatTime(Context context, long time) {
|
||||||
DateFormat timeFormatter = android.text.format.DateFormat.getTimeFormat(context);
|
return android.text.format.DateFormat.getTimeFormat(context).format(time);
|
||||||
return timeFormatter.format(new Date(time));
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the date and time based on user's phone date/time preferences.
|
||||||
|
*
|
||||||
|
* @param context the context
|
||||||
|
* @param time the time in milliseconds
|
||||||
|
*/
|
||||||
|
public static String formatDateTime(Context context, long time) {
|
||||||
|
return android.text.format.DateFormat.getDateFormat(context).format(time) + " "
|
||||||
|
+ formatTime(context, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the time using the ISO 8601 date time format with fractional
|
||||||
|
* seconds in UTC time zone.
|
||||||
|
*
|
||||||
|
* @param time the time in milliseconds
|
||||||
|
*/
|
||||||
|
public static String formatDateTimeIso8601(long time) {
|
||||||
|
return ISO_8601_DATE_TIME_FORMAT.format(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,10 +89,10 @@ public class StringUtils {
|
|||||||
public static String formatElapsedTime(long time) {
|
public static String formatElapsedTime(long time) {
|
||||||
return DateUtils.formatElapsedTime(time / 1000);
|
return DateUtils.formatElapsedTime(time / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats the elapsed time in the form "H:MM:SS".
|
* Formats the elapsed time in the form "H:MM:SS".
|
||||||
*
|
*
|
||||||
* @param time the time in milliseconds
|
* @param time the time in milliseconds
|
||||||
*/
|
*/
|
||||||
public static String formatElapsedTimeWithHour(long time) {
|
public static String formatElapsedTimeWithHour(long time) {
|
||||||
@@ -82,103 +101,107 @@ public class StringUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats the elapsed time and total distance.
|
* Formats the distance.
|
||||||
*
|
*
|
||||||
* @param context the current context
|
* @param context the context
|
||||||
* @param totalDistance the total distance in meters
|
* @param distance the distance in meters
|
||||||
* @param totalTime the total time in milliseconds
|
* @param metric true to use metric. False to use imperial
|
||||||
* @param metric whether to use metric units
|
|
||||||
* @return the formatted string
|
|
||||||
*/
|
*/
|
||||||
public static String formatTimeDistance(Context context, double totalDistance, long totalTime, boolean metric) {
|
public static String formatDistance(Context context, double distance, boolean metric) {
|
||||||
String distanceUnit;
|
|
||||||
if (metric) {
|
if (metric) {
|
||||||
if (totalDistance > 2000.0) {
|
if (distance > 2000.0) {
|
||||||
totalDistance *= UnitConversions.M_TO_KM;
|
distance *= UnitConversions.M_TO_KM;
|
||||||
distanceUnit = context.getString(R.string.unit_kilometer);
|
return context.getString(R.string.value_float_kilometer, distance);
|
||||||
} else {
|
} else {
|
||||||
distanceUnit = context.getString(R.string.unit_meter);
|
return context.getString(R.string.value_float_meter, distance);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (totalDistance * UnitConversions.M_TO_MI > 2) {
|
if (distance * UnitConversions.M_TO_MI > 2) {
|
||||||
totalDistance *= UnitConversions.M_TO_MI;
|
distance *= UnitConversions.M_TO_MI;
|
||||||
distanceUnit = context.getString(R.string.unit_mile);
|
return context.getString(R.string.value_float_mile, distance);
|
||||||
} else {
|
} else {
|
||||||
totalDistance *= UnitConversions.M_TO_FT;
|
distance *= UnitConversions.M_TO_FT;
|
||||||
distanceUnit = context.getString(R.string.unit_feet);
|
return context.getString(R.string.value_float_feet, distance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return String.format("%s %.2f %s",
|
|
||||||
formatElapsedTime(totalTime),
|
|
||||||
totalDistance,
|
|
||||||
distanceUnit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final NumberFormat SINGLE_DECIMAL_PLACE_FORMAT = NumberFormat.getNumberInstance();
|
|
||||||
|
|
||||||
static {
|
|
||||||
SINGLE_DECIMAL_PLACE_FORMAT.setMaximumFractionDigits(1);
|
|
||||||
SINGLE_DECIMAL_PLACE_FORMAT.setMinimumFractionDigits(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a double precision number as decimal number with a single decimal
|
* Formats the speed.
|
||||||
* place.
|
*
|
||||||
*
|
* @param context the context
|
||||||
* @param number A double precision number
|
* @param speed the speed in meters per second
|
||||||
* @return A string representation of a decimal number, derived from the input
|
* @param metric true to use metric. False to use imperial
|
||||||
* double, with a single decimal place
|
* @param reportSpeed true to report as speed. False to report as pace
|
||||||
*/
|
*/
|
||||||
public static final String formatSingleDecimalPlace(double number) {
|
public static String formatSpeed(
|
||||||
return SINGLE_DECIMAL_PLACE_FORMAT.format(number);
|
Context context, double speed, boolean metric, boolean reportSpeed) {
|
||||||
}
|
if (Double.isNaN(speed) || Double.isInfinite(speed)) {
|
||||||
|
return context.getString(R.string.value_unknown);
|
||||||
/**
|
|
||||||
* Formats the given text as a CDATA element to be used in a XML file. This
|
|
||||||
* includes adding the starting and ending CDATA tags. Please notice that this
|
|
||||||
* may result in multiple consecutive CDATA tags.
|
|
||||||
*
|
|
||||||
* @param unescaped the unescaped text to be formatted
|
|
||||||
* @return the formatted text, inside one or more CDATA tags
|
|
||||||
*/
|
|
||||||
public static String stringAsCData(String unescaped) {
|
|
||||||
// "]]>" needs to be broken into multiple CDATA segments, like:
|
|
||||||
// "Foo]]>Bar" becomes "<![CDATA[Foo]]]]><![CDATA[>Bar]]>"
|
|
||||||
// (the end of the first CDATA has the "]]", the other has ">")
|
|
||||||
String escaped = unescaped.replaceAll("]]>", "]]]]><![CDATA[>");
|
|
||||||
return "<![CDATA[" + escaped + "]]>";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final SimpleDateFormat BASE_XML_DATE_FORMAT =
|
|
||||||
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
|
||||||
static {
|
|
||||||
BASE_XML_DATE_FORMAT.setTimeZone(new SimpleTimeZone(0, "UTC"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Pattern XML_DATE_EXTRAS_PATTERN =
|
|
||||||
Pattern.compile("^(\\.\\d+)?(?:Z|([+-])(\\d{2}):(\\d{2}))?$");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses an XML dateTime element as defined by the XML standard.
|
|
||||||
*
|
|
||||||
* @see <a href="http://www.w3.org/TR/xmlschema-2/#dateTime">dateTime</a>
|
|
||||||
*/
|
|
||||||
public static long parseXmlDateTime(String xmlTime) {
|
|
||||||
// Parse the base date (fixed format)
|
|
||||||
ParsePosition position = new ParsePosition(0);
|
|
||||||
Date date = BASE_XML_DATE_FORMAT.parse(xmlTime, position);
|
|
||||||
if (date == null) {
|
|
||||||
throw new IllegalArgumentException("Invalid XML dateTime value: '" + xmlTime
|
|
||||||
+ "' (at position " + position.getErrorIndex() + ")");
|
|
||||||
}
|
}
|
||||||
|
if (metric) {
|
||||||
|
speed = speed * UnitConversions.MS_TO_KMH;
|
||||||
|
if (reportSpeed) {
|
||||||
|
return context.getString(R.string.value_float_kilometer_hour, speed);
|
||||||
|
} else {
|
||||||
|
double paceInMinute = speed == 0 ? 0.0 : 60 / speed;
|
||||||
|
return context.getString(R.string.value_float_minute_kilometer, paceInMinute);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
speed = speed * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI;
|
||||||
|
if (reportSpeed) {
|
||||||
|
return context.getString(R.string.value_float_mile_hour, speed);
|
||||||
|
} else {
|
||||||
|
double paceInMinute = speed == 0 ? 0.0 : 60 / speed;
|
||||||
|
return context.getString(R.string.value_float_minute_mile, paceInMinute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the elapsed time and distance.
|
||||||
|
*
|
||||||
|
* @param context the context
|
||||||
|
* @param elapsedTime the elapsed time in milliseconds
|
||||||
|
* @param distance the distance in meters
|
||||||
|
* @param metric true to use metric. False to use imperial
|
||||||
|
*/
|
||||||
|
public static String formatTimeDistance(
|
||||||
|
Context context, long elapsedTime, double distance, boolean metric) {
|
||||||
|
return formatElapsedTime(elapsedTime) + " " + formatDistance(context, distance, metric);
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the extras
|
/**
|
||||||
Matcher matcher =
|
* Formats the given text as a XML CDATA element. This includes adding the
|
||||||
XML_DATE_EXTRAS_PATTERN.matcher(xmlTime.substring(position.getIndex()));
|
* starting and ending CDATA tags. Please notice that this may result in
|
||||||
|
* multiple consecutive CDATA tags.
|
||||||
|
*
|
||||||
|
* @param text the given text
|
||||||
|
*/
|
||||||
|
public static String formatCData(String text) {
|
||||||
|
return "<![CDATA[" + text.replaceAll("]]>", "]]]]><![CDATA[>") + "]]>";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the time, in milliseconds, from an XML date time string as defined at
|
||||||
|
* http://www.w3.org/TR/xmlschema-2/#dateTime
|
||||||
|
*
|
||||||
|
* @param xmlDateTime the XML date time string
|
||||||
|
*/
|
||||||
|
public static long getTime(String xmlDateTime) {
|
||||||
|
// Parse the date time base
|
||||||
|
ParsePosition position = new ParsePosition(0);
|
||||||
|
Date date = ISO_8601_BASE.parse(xmlDateTime, position);
|
||||||
|
if (date == null) {
|
||||||
|
throw new IllegalArgumentException("Invalid XML dateTime value: " + xmlDateTime
|
||||||
|
+ " (at position " + position.getErrorIndex() + ")"); }
|
||||||
|
|
||||||
|
// Parse the date time extras
|
||||||
|
Matcher matcher = ISO_8601_EXTRAS.matcher(xmlDateTime.substring(position.getIndex()));
|
||||||
if (!matcher.matches()) {
|
if (!matcher.matches()) {
|
||||||
// This will match even an empty string as all groups are optional,
|
// This will match even an empty string as all groups are optional. Thus a
|
||||||
// so a non-match means some other garbage was there
|
// non-match means invalid content.
|
||||||
throw new IllegalArgumentException("Invalid XML dateTime value: " + xmlTime);
|
throw new IllegalArgumentException("Invalid XML dateTime value: " + xmlDateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
long time = date.getTime();
|
long time = date.getTime();
|
||||||
@@ -186,7 +209,7 @@ public class StringUtils {
|
|||||||
// Account for fractional seconds
|
// Account for fractional seconds
|
||||||
String fractional = matcher.group(1);
|
String fractional = matcher.group(1);
|
||||||
if (fractional != null) {
|
if (fractional != null) {
|
||||||
// Regex ensures fractional part is in (0,1(
|
// Regex ensures fractional part is in (0,1)
|
||||||
float fractionalSeconds = Float.parseFloat(fractional);
|
float fractionalSeconds = Float.parseFloat(fractional);
|
||||||
long fractionalMillis = (long) (fractionalSeconds * 1000.0f);
|
long fractionalMillis = (long) (fractionalSeconds * 1000.0f);
|
||||||
time += fractionalMillis;
|
time += fractionalMillis;
|
||||||
@@ -204,24 +227,28 @@ public class StringUtils {
|
|||||||
|
|
||||||
// Regex ensures values are >= 0
|
// Regex ensures values are >= 0
|
||||||
if (offsetHours > 14 || offsetMins > 59) {
|
if (offsetHours > 14 || offsetMins > 59) {
|
||||||
throw new IllegalArgumentException("Bad timezone in " + xmlTime);
|
throw new IllegalArgumentException("Bad timezone: " + xmlDateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
long totalOffsetMillis = (offsetMins + offsetHours * 60L) * 60000L;
|
long totalOffsetMillis = (offsetMins + offsetHours * 60L) * 60000L;
|
||||||
|
|
||||||
// Make time go back to UTC
|
// Convert to UTC
|
||||||
if (plusSign) {
|
if (plusSign) {
|
||||||
time -= totalOffsetMillis;
|
time -= totalOffsetMillis;
|
||||||
} else {
|
} else {
|
||||||
time += totalOffsetMillis;
|
time += totalOffsetMillis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the time as an array of parts.
|
* Gets the time as an array of three integers. Index 0 contains the number of
|
||||||
|
* seconds, index 1 contains the number of minutes, and index 2 contains the
|
||||||
|
* number of hours.
|
||||||
|
*
|
||||||
|
* @param time the time in milliseconds
|
||||||
|
* @return an array of 3 elements.
|
||||||
*/
|
*/
|
||||||
public static int[] getTimeParts(long time) {
|
public static int[] getTimeParts(long time) {
|
||||||
if (time < 0) {
|
if (time < 0) {
|
||||||
@@ -235,10 +262,9 @@ public class StringUtils {
|
|||||||
|
|
||||||
long seconds = time / 1000;
|
long seconds = time / 1000;
|
||||||
parts[0] = (int) (seconds % 60);
|
parts[0] = (int) (seconds % 60);
|
||||||
int tmp = (int) (seconds / 60);
|
int minutes = (int) (seconds / 60);
|
||||||
parts[1] = tmp % 60;
|
parts[1] = minutes % 60;
|
||||||
parts[2] = tmp / 60;
|
parts[2] = minutes / 60;
|
||||||
|
|
||||||
return parts;
|
return parts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import com.google.android.apps.mytracks.content.TracksColumns;
|
|||||||
import com.google.android.apps.mytracks.services.ControlRecordingService;
|
import com.google.android.apps.mytracks.services.ControlRecordingService;
|
||||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||||
import com.google.android.apps.mytracks.util.StringUtils;
|
import com.google.android.apps.mytracks.util.StringUtils;
|
||||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
@@ -68,9 +67,6 @@ public class TrackWidgetProvider
|
|||||||
private MyTracksProviderUtils providerUtils;
|
private MyTracksProviderUtils providerUtils;
|
||||||
private Context context;
|
private Context context;
|
||||||
private String unknown;
|
private String unknown;
|
||||||
private String distanceLabel;
|
|
||||||
private String speedLabel;
|
|
||||||
private String paceLabel;
|
|
||||||
private TrackObserver trackObserver;
|
private TrackObserver trackObserver;
|
||||||
private boolean isMetric;
|
private boolean isMetric;
|
||||||
private boolean reportSpeed;
|
private boolean reportSpeed;
|
||||||
@@ -213,32 +209,10 @@ public class TrackWidgetProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
TripStatistics stats = track.getStatistics();
|
TripStatistics stats = track.getStatistics();
|
||||||
|
String distance = StringUtils.formatDistance(context, stats.getTotalDistance(), isMetric);
|
||||||
// TODO replace this with format strings and miles.
|
|
||||||
// convert meters to kilometers
|
|
||||||
double displayDistance = stats.getTotalDistance() * UnitConversions.M_TO_KM;
|
|
||||||
if (!isMetric) {
|
|
||||||
displayDistance *= UnitConversions.KM_TO_MI;
|
|
||||||
}
|
|
||||||
String distance =
|
|
||||||
StringUtils.formatSingleDecimalPlace(displayDistance) + " " + this.distanceLabel;
|
|
||||||
|
|
||||||
// convert ms to minutes
|
|
||||||
String time = StringUtils.formatElapsedTime(stats.getMovingTime());
|
String time = StringUtils.formatElapsedTime(stats.getMovingTime());
|
||||||
String speed = unknown;
|
String speed = StringUtils.formatSpeed(
|
||||||
if (!Double.isNaN(stats.getAverageMovingSpeed())) {
|
context, stats.getAverageMovingSpeed(), isMetric, reportSpeed);
|
||||||
// Convert m/s to km/h
|
|
||||||
double displaySpeed = stats.getAverageMovingSpeed() * UnitConversions.MS_TO_KMH;
|
|
||||||
if (!isMetric) {
|
|
||||||
displaySpeed *= UnitConversions.KM_TO_MI;
|
|
||||||
}
|
|
||||||
if (reportSpeed) {
|
|
||||||
speed = StringUtils.formatSingleDecimalPlace(displaySpeed) + " " + this.speedLabel;
|
|
||||||
} else {
|
|
||||||
long displayPace = (long) (3600000.0 / displaySpeed);
|
|
||||||
speed = StringUtils.formatElapsedTime(displayPace) + " " + paceLabel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
views.setTextViewText(R.id.appwidget_distance_text, distance);
|
views.setTextViewText(R.id.appwidget_distance_text, distance);
|
||||||
views.setTextViewText(R.id.appwidget_time_text, time);
|
views.setTextViewText(R.id.appwidget_time_text, time);
|
||||||
@@ -250,11 +224,6 @@ public class TrackWidgetProvider
|
|||||||
String metricUnitsKey = context.getString(R.string.metric_units_key);
|
String metricUnitsKey = context.getString(R.string.metric_units_key);
|
||||||
if (key == null || key.equals(metricUnitsKey)) {
|
if (key == null || key.equals(metricUnitsKey)) {
|
||||||
isMetric = prefs.getBoolean(metricUnitsKey, true);
|
isMetric = prefs.getBoolean(metricUnitsKey, true);
|
||||||
distanceLabel = context.getString(isMetric ? R.string.unit_kilometer : R.string.unit_mile);
|
|
||||||
speedLabel = context.getString(
|
|
||||||
isMetric ? R.string.unit_kilometer_per_hour : R.string.unit_mile_per_hour);
|
|
||||||
paceLabel = context.getString(
|
|
||||||
isMetric ? R.string.unit_minute_per_kilometer : R.string.unit_minute_per_mile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String reportSpeedKey = context.getString(R.string.report_speed_key);
|
String reportSpeedKey = context.getString(R.string.report_speed_key);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ package com.google.android.apps.mytracks.io.file;
|
|||||||
|
|
||||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||||
import com.google.android.apps.mytracks.content.Sensor;
|
import com.google.android.apps.mytracks.content.Sensor;
|
||||||
import com.google.android.apps.mytracks.util.FileUtils;
|
import com.google.android.apps.mytracks.util.StringUtils;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -70,8 +70,7 @@ public class TcxTrackWriterTest extends TrackFormatWriterTest {
|
|||||||
* @param location the location
|
* @param location the location
|
||||||
*/
|
*/
|
||||||
private void assertTagMatchesLocation(Element tag, MyTracksLocation location) {
|
private void assertTagMatchesLocation(Element tag, MyTracksLocation location) {
|
||||||
assertEquals(
|
assertEquals(StringUtils.formatDateTimeIso8601(location.getTime()), getChildTextValue(tag, "Time"));
|
||||||
FileUtils.FILE_TIMESTAMP_FORMAT.format(location.getTime()), getChildTextValue(tag, "Time"));
|
|
||||||
|
|
||||||
Element positionTag = getChildElement(tag, "Position");
|
Element positionTag = getChildElement(tag, "Position");
|
||||||
assertEquals(
|
assertEquals(
|
||||||
|
|||||||
@@ -15,69 +15,43 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.apps.mytracks.util;
|
package com.google.android.apps.mytracks.util;
|
||||||
|
|
||||||
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link StringUtils}.
|
* Tests for {@link StringUtils}.
|
||||||
*
|
*
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
public class StringUtilsTest extends TestCase {
|
public class StringUtilsTest extends AndroidTestCase {
|
||||||
public void testParseXmlDateTime() {
|
|
||||||
assertParseXmlDateTime("2010-05-04T03:02:01",
|
/**
|
||||||
2010, 5, 4, 3, 2, 1, 0);
|
* Tests {@link StringUtils#formatDateTime(android.content.Context, long)}.
|
||||||
|
*/
|
||||||
|
public void testFormatTime() {
|
||||||
|
// Unix time 0 in UTC is "4:00 PM" in Pacific Standard time zone.
|
||||||
|
// This test can break if run on a different time zone or with a different
|
||||||
|
// time format preference.
|
||||||
|
assertEquals("4:00 PM", StringUtils.formatTime(getContext(), 0L));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testParseXmlDateTime_fractional() {
|
/**
|
||||||
assertParseXmlDateTime("2010-05-04T03:02:01.3",
|
* Tests {@link StringUtils#formatDateTime(android.content.Context, long)}.
|
||||||
2010, 5, 4, 3, 2, 1, 300);
|
*/
|
||||||
assertParseXmlDateTime("2010-05-04T03:02:01.35",
|
public void testFormatDateTime() {
|
||||||
2010, 5, 4, 3, 2, 1, 350);
|
// Unix time 0 in UTC is "12/31/1969 4:00 PM" in Pacific Standard time zone.
|
||||||
assertParseXmlDateTime("2010-05-04T03:02:01.352",
|
// This test can break if run on a different time zone or with a different
|
||||||
2010, 5, 4, 3, 2, 1, 352);
|
// date/time format preference.
|
||||||
assertParseXmlDateTime("2010-05-04T03:02:01.3525",
|
assertEquals("12/31/1969 4:00 PM", StringUtils.formatDateTime(getContext(), 0L));
|
||||||
2010, 5, 4, 3, 2, 1, 352);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testParseXmlDateTime_timezone() {
|
/**
|
||||||
assertParseXmlDateTime("2010-05-04T03:02:01Z",
|
* Tests {@link StringUtils#formatDateTimeIso8601(long)}.
|
||||||
2010, 5, 4, 3, 2, 1, 0);
|
*/
|
||||||
assertParseXmlDateTime("2010-05-04T03:02:01+00:00",
|
public void testFormatDateTimeIso8601() {
|
||||||
2010, 5, 4, 3, 2, 1, 0);
|
assertEquals("1970-01-01T00:00:12.345Z", StringUtils.formatDateTimeIso8601(12345));
|
||||||
assertParseXmlDateTime("2010-05-04T03:02:01-00:00",
|
|
||||||
2010, 5, 4, 3, 2, 1, 0);
|
|
||||||
assertParseXmlDateTime("2010-05-04T03:02:01+01:00",
|
|
||||||
2010, 5, 4, 2, 2, 1, 0);
|
|
||||||
assertParseXmlDateTime("2010-05-04T03:02:01+10:30",
|
|
||||||
2010, 5, 3, 16, 32, 1, 0);
|
|
||||||
assertParseXmlDateTime("2010-05-04T03:02:01-09:30",
|
|
||||||
2010, 5, 4, 12, 32, 1, 0);
|
|
||||||
assertParseXmlDateTime("2010-05-04T03:02:01-05:00",
|
|
||||||
2010, 5, 4, 8, 2, 1, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testParseXmlDateTime_fractionalAndTimezone() {
|
|
||||||
assertParseXmlDateTime("2010-05-04T03:02:01.352Z",
|
|
||||||
2010, 5, 4, 3, 2, 1, 352);
|
|
||||||
assertParseXmlDateTime("2010-05-04T03:02:01.47+00:00",
|
|
||||||
2010, 5, 4, 3, 2, 1, 470);
|
|
||||||
assertParseXmlDateTime("2010-05-04T03:02:01.5791+03:00",
|
|
||||||
2010, 5, 4, 0, 2, 1, 579);
|
|
||||||
assertParseXmlDateTime("2010-05-04T03:02:01.8-05:30",
|
|
||||||
2010, 5, 4, 8, 32, 1, 800);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertParseXmlDateTime(String dateTime,
|
|
||||||
int year, int month, int day, int hour, int min, int second, int millis) {
|
|
||||||
long timestamp = StringUtils.parseXmlDateTime(dateTime);
|
|
||||||
GregorianCalendar calendar =
|
|
||||||
new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
|
||||||
calendar.set(year, month - 1, day, hour, min, second);
|
|
||||||
calendar.set(GregorianCalendar.MILLISECOND, millis);
|
|
||||||
assertEquals(calendar.getTimeInMillis(), timestamp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -119,4 +93,139 @@ public class StringUtilsTest extends TestCase {
|
|||||||
// 100 hours
|
// 100 hours
|
||||||
assertEquals("100:00:00", StringUtils.formatElapsedTimeWithHour(360000000));
|
assertEquals("100:00:00", StringUtils.formatElapsedTimeWithHour(360000000));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link StringUtils#formatDistance(android.content.Context, double,
|
||||||
|
* boolean)}.
|
||||||
|
*/
|
||||||
|
public void testFormatDistance() {
|
||||||
|
// A large number in metric
|
||||||
|
assertEquals("5.00 km", StringUtils.formatDistance(getContext(), 5000, true));
|
||||||
|
// A large number in imperial
|
||||||
|
assertEquals("3.11 mi", StringUtils.formatDistance(getContext(), 5000, false));
|
||||||
|
// A small number in metric
|
||||||
|
assertEquals("100.00 m", StringUtils.formatDistance(getContext(), 100, true));
|
||||||
|
// A small number in imperial
|
||||||
|
assertEquals("328.08 ft", StringUtils.formatDistance(getContext(), 100, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link StringUtils#formatSpeed(android.content.Context, double,
|
||||||
|
* boolean, boolean)}.
|
||||||
|
*/
|
||||||
|
public void testFormatSpeed() {
|
||||||
|
// Speed in metric
|
||||||
|
assertEquals("36.00 km/h", StringUtils.formatSpeed(getContext(), 10, true, true));
|
||||||
|
// Speed in imperial
|
||||||
|
assertEquals("22.37 mi/h", StringUtils.formatSpeed(getContext(), 10, false, true));
|
||||||
|
// Pace in metric
|
||||||
|
assertEquals("1.67 min/km", StringUtils.formatSpeed(getContext(), 10, true, false));
|
||||||
|
// Pace in imperial
|
||||||
|
assertEquals("2.68 min/mi", StringUtils.formatSpeed(getContext(), 10, false, false));
|
||||||
|
// zero pace
|
||||||
|
assertEquals("0.00 min/km", StringUtils.formatSpeed(getContext(), 0, true, false));
|
||||||
|
assertEquals("0.00 min/mi", StringUtils.formatSpeed(getContext(), 0, false, false));
|
||||||
|
// speed is NaN
|
||||||
|
assertEquals("-", StringUtils.formatSpeed(getContext(), Double.NaN, true, true));
|
||||||
|
// speed is infinite
|
||||||
|
assertEquals("-", StringUtils.formatSpeed(getContext(), Double.NEGATIVE_INFINITY, true, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link StringUtils#formatTimeDistance(android.content.Context, long, double, boolean)}.
|
||||||
|
*/
|
||||||
|
public void testFormatTimeDistance() {
|
||||||
|
assertEquals("00:10 5.00 km", StringUtils.formatTimeDistance(getContext(), 10000, 5000, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link StringUtils#formatCData(String)}.
|
||||||
|
*/
|
||||||
|
public void testFormatCData() {
|
||||||
|
assertEquals("<![CDATA[hello]]>", StringUtils.formatCData("hello"));
|
||||||
|
assertEquals("<![CDATA[hello]]]]><![CDATA[>there]]>", StringUtils.formatCData("hello]]>there"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link StringUtils#getTime(String)}.
|
||||||
|
*/
|
||||||
|
public void testGetTime() {
|
||||||
|
assertGetTime("2010-05-04T03:02:01", 2010, 5, 4, 3, 2, 1, 0);
|
||||||
|
assertGetTime("2010-05-04T03:02:01Z", 2010, 5, 4, 3, 2, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link StringUtils#getTime(String)} with fractional seconds.
|
||||||
|
*/
|
||||||
|
public void testGetTime_fractional() {
|
||||||
|
assertGetTime("2010-05-04T03:02:01.3", 2010, 5, 4, 3, 2, 1, 300);
|
||||||
|
assertGetTime("2010-05-04T03:02:01.35", 2010, 5, 4, 3, 2, 1, 350);
|
||||||
|
assertGetTime("2010-05-04T03:02:01.352Z", 2010, 5, 4, 3, 2, 1, 352);
|
||||||
|
assertGetTime("2010-05-04T03:02:01.3525Z", 2010, 5, 4, 3, 2, 1, 352);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link StringUtils#getTime(String)} with time zone.
|
||||||
|
*/
|
||||||
|
public void testGetTime_timezone() {
|
||||||
|
assertGetTime("2010-05-04T03:02:01Z", 2010, 5, 4, 3, 2, 1, 0);
|
||||||
|
assertGetTime("2010-05-04T03:02:01+00:00", 2010, 5, 4, 3, 2, 1, 0);
|
||||||
|
assertGetTime("2010-05-04T03:02:01-00:00", 2010, 5, 4, 3, 2, 1, 0);
|
||||||
|
assertGetTime("2010-05-04T03:02:01+01:00", 2010, 5, 4, 2, 2, 1, 0);
|
||||||
|
assertGetTime("2010-05-04T03:02:01+10:30", 2010, 5, 3, 16, 32, 1, 0);
|
||||||
|
assertGetTime("2010-05-04T03:02:01-09:30", 2010, 5, 4, 12, 32, 1, 0);
|
||||||
|
assertGetTime("2010-05-04T03:02:01-05:00", 2010, 5, 4, 8, 2, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link StringUtils#getTime(String)} with fractional seconds and time
|
||||||
|
* zone.
|
||||||
|
*/
|
||||||
|
public void testGetTime_fractionalAndTimezone() {
|
||||||
|
assertGetTime("2010-05-04T03:02:01.352Z", 2010, 5, 4, 3, 2, 1, 352);
|
||||||
|
assertGetTime("2010-05-04T03:02:01.47+00:00", 2010, 5, 4, 3, 2, 1, 470);
|
||||||
|
assertGetTime("2010-05-04T03:02:01.5791+03:00", 2010, 5, 4, 0, 2, 1, 579);
|
||||||
|
assertGetTime("2010-05-04T03:02:01.8-05:30", 2010, 5, 4, 8, 32, 1, 800);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the {@link StringUtils#getTime(String)} returns the expected
|
||||||
|
* values.
|
||||||
|
*
|
||||||
|
* @param xmlDateTime the xml date time string
|
||||||
|
* @param year the expected year
|
||||||
|
* @param month the expected month
|
||||||
|
* @param day the expected day
|
||||||
|
* @param hour the expected hour
|
||||||
|
* @param minute the expected minute
|
||||||
|
* @param second the expected second
|
||||||
|
* @param millisecond the expected milliseconds
|
||||||
|
*/
|
||||||
|
private void assertGetTime(String xmlDateTime, int year, int month, int day, int hour, int minute,
|
||||||
|
int second, int millisecond) {
|
||||||
|
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
||||||
|
calendar.set(year, month - 1, day, hour, minute, second);
|
||||||
|
calendar.set(GregorianCalendar.MILLISECOND, millisecond);
|
||||||
|
assertEquals(calendar.getTimeInMillis(), StringUtils.getTime(xmlDateTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link StringUtils#getTimeParts(long)} with a positive number.
|
||||||
|
*/
|
||||||
|
public void testGetTimeParts_postive() {
|
||||||
|
int parts[] = StringUtils.getTimeParts(61000);
|
||||||
|
assertEquals(1, parts[0]);
|
||||||
|
assertEquals(1, parts[1]);
|
||||||
|
assertEquals(0, parts[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link StringUtils#getTimeParts(long)} with a negative number.
|
||||||
|
*/
|
||||||
|
public void testGetTimeParts_negative() {
|
||||||
|
int parts[] = StringUtils.getTimeParts(-61000);
|
||||||
|
assertEquals(-1, parts[0]);
|
||||||
|
assertEquals(-1, parts[1]);
|
||||||
|
assertEquals(0, parts[2]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user