mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-02 09:33:06 +02:00
Merge heads
This commit is contained in:
@@ -507,6 +507,34 @@ limitations under the License.
|
||||
%2$d elevation in feet">
|
||||
Elevation gain: %1$d m (%2$d ft)
|
||||
</string>
|
||||
<string name="description_location_accuracy"
|
||||
translation_description="Label for location accuracy in meters.">
|
||||
Accuracy (m)
|
||||
</string>
|
||||
<string name="description_location_altitude"
|
||||
translation_description="Label for location altitude in degrees.">
|
||||
Altitude (m)
|
||||
</string>
|
||||
<string name="description_location_bearing"
|
||||
translation_description="Label for location bearing in degrees.">
|
||||
Bearing (deg)
|
||||
</string>
|
||||
<string name="description_location_latitude"
|
||||
translation_description="Label for location latitude in degrees.">
|
||||
Latitude (deg)
|
||||
</string>
|
||||
<string name="description_location_longitude"
|
||||
translation_description="Label for location longitude in degrees.">
|
||||
Longitude (deg)
|
||||
</string>
|
||||
<string name="description_location_speed"
|
||||
translation_description="Label for location speed in meters per second.">
|
||||
Speed (m/s)
|
||||
</string>
|
||||
<string name="description_location_time"
|
||||
translation_description="Label for location time.">
|
||||
Time
|
||||
</string>
|
||||
<string name="description_max_elevation"
|
||||
translation_description="Max elevation description.
|
||||
|
||||
@@ -575,6 +603,14 @@ limitations under the License.
|
||||
translation_description="Label for power with its unit watt (W).">
|
||||
Power (W)
|
||||
</string>
|
||||
<string name="description_track_point"
|
||||
translation_description="Label for a track point">
|
||||
Point
|
||||
</string>
|
||||
<string name="description_track_segment"
|
||||
translation_description="Label for a track segment">
|
||||
Segment
|
||||
</string>
|
||||
<string name="description_total_distance"
|
||||
translation_description="Total distance description.
|
||||
|
||||
@@ -588,7 +624,7 @@ limitations under the License.
|
||||
%1$s time. E.g., '1:08:59'">
|
||||
Total time: %1$s
|
||||
</string>
|
||||
|
||||
|
||||
<!-- EULA -->
|
||||
<string name="eula_accept"
|
||||
translation_description="In the 'Terms of service' dialog, the button to accept.
|
||||
@@ -2560,13 +2596,53 @@ limitations under the License.
|
||||
Screenshot: value_auto_resume.png">
|
||||
Always
|
||||
</string>
|
||||
<string name="value_float_mile"
|
||||
translation_description="In a setting picker, the string format to display a float value in
|
||||
miles (mi).
|
||||
<string name="value_float_feet"
|
||||
translation_description="Display a float value in feet (ft).
|
||||
|
||||
%1$.1f: a float number. E.g., '2.2'
|
||||
Screenshot: value_gps_accuracy_mile_poor_gps.png">
|
||||
%1$.1f mi
|
||||
%1$.2f: a float number. E.g., '2.22'">
|
||||
%1$.2f ft
|
||||
</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 name="value_float_mile_poor_gps"
|
||||
translation_description="In a setting picker, the string format to display a float value in
|
||||
|
||||
@@ -17,7 +17,6 @@ package com.google.android.apps.mytracks;
|
||||
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
|
||||
import com.google.android.apps.analytics.GoogleAnalyticsTracker;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.TrackDataHub;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
@@ -27,6 +26,7 @@ import com.google.android.apps.mytracks.content.WaypointsColumns;
|
||||
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
||||
import com.google.android.apps.mytracks.services.ServiceUtils;
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
|
||||
import com.google.android.apps.mytracks.util.AnalyticsUtils;
|
||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||
import com.google.android.apps.mytracks.util.EulaUtils;
|
||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||
@@ -92,11 +92,6 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
*/
|
||||
private MyTracksProviderUtils providerUtils;
|
||||
|
||||
/**
|
||||
* Google Analytics tracker
|
||||
*/
|
||||
private GoogleAnalyticsTracker tracker;
|
||||
|
||||
/*
|
||||
* Tabs/View navigation:
|
||||
*/
|
||||
@@ -144,12 +139,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
ApiAdapterFactory.getApiAdapter().enableStrictMode();
|
||||
}
|
||||
|
||||
tracker = GoogleAnalyticsTracker.getInstance();
|
||||
// Start the tracker in manual dispatch mode...
|
||||
tracker.start(getString(R.string.my_tracks_analytics_id), getApplicationContext());
|
||||
tracker.setProductVersion("android-mytracks", SystemUtils.getMyTracksVersion(this));
|
||||
tracker.trackPageView("/appstart");
|
||||
tracker.dispatch();
|
||||
AnalyticsUtils.sendPageViews(this, "/appstart");
|
||||
|
||||
providerUtils = MyTracksProviderUtils.Factory.get(this);
|
||||
preferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
@@ -256,8 +246,6 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
protected void onStop() {
|
||||
Log.d(TAG, "MyTracks.onStop");
|
||||
dataHub.stop();
|
||||
tracker.dispatch();
|
||||
tracker.stop();
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
|
||||
@@ -207,9 +207,8 @@ public class SearchActivity extends ListActivity {
|
||||
resultMap.put(DESCRIPTION_FIELD, track.getDescription());
|
||||
resultMap.put(CATEGORY_FIELD, track.getCategory());
|
||||
resultMap.put(TIME_FIELD, StringUtils.formatDateTime(this, stats.getStartTime()));
|
||||
resultMap.put(STATS_FIELD,
|
||||
StringUtils.formatTimeDistance(this, stats.getTotalDistance(), stats.getTotalTime(),
|
||||
metricUnits));
|
||||
resultMap.put(STATS_FIELD, StringUtils.formatTimeDistance(
|
||||
this, stats.getTotalTime(), stats.getTotalDistance(), metricUnits));
|
||||
resultMap.put(TRACK_ID_FIELD, track.getId());
|
||||
}
|
||||
|
||||
|
||||
@@ -349,7 +349,8 @@ public class TrackList extends ListActivity
|
||||
} else if (columnIndex == totalDistanceIdx) {
|
||||
double totalDistance = cursor.getDouble(totalDistanceIdx);
|
||||
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);
|
||||
} else {
|
||||
textView.setText(cursor.getString(columnIndex));
|
||||
|
||||
@@ -300,8 +300,8 @@ public class SendDocsUtils {
|
||||
appendTag(builder, "name", track.getName());
|
||||
appendTag(builder, "description", track.getDescription());
|
||||
appendTag(builder, "date", StringUtils.formatDateTime(context, stats.getStartTime()));
|
||||
appendTag(builder, "totaltime", StringUtils.formatElapsedTime(stats.getTotalTime()));
|
||||
appendTag(builder, "movingtime", StringUtils.formatElapsedTime(stats.getMovingTime()));
|
||||
appendTag(builder, "totaltime", StringUtils.formatElapsedTimeWithHour(stats.getTotalTime()));
|
||||
appendTag(builder, "movingtime", StringUtils.formatElapsedTimeWithHour(stats.getMovingTime()));
|
||||
appendTag(builder, "distance", getDistance(stats.getTotalDistance(), metricUnits));
|
||||
appendTag(builder, "distanceunit", distanceUnit);
|
||||
appendTag(builder, "averagespeed", getSpeed(stats.getAverageSpeed(), metricUnits));
|
||||
@@ -335,7 +335,7 @@ public class SendDocsUtils {
|
||||
.append("<gsx:")
|
||||
.append(name)
|
||||
.append(">")
|
||||
.append(StringUtils.stringAsCData(value))
|
||||
.append(StringUtils.formatCData(value))
|
||||
.append("</gsx:")
|
||||
.append(name)
|
||||
.append(">");
|
||||
|
||||
@@ -15,31 +15,37 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io.file;
|
||||
|
||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.apps.mytracks.content.Sensor.SensorData;
|
||||
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
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 android.content.Context;
|
||||
import android.location.Location;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Exports a track as a CSV file, according to RFC 4180.
|
||||
*
|
||||
* The first field is a type:
|
||||
* TRACK - track description
|
||||
* P - point
|
||||
* WAYPOINT - waypoint
|
||||
*
|
||||
* For each type, the fields are:
|
||||
*
|
||||
* TRACK,,,,,,,,name,description,
|
||||
* P,time,lat,lon,alt,bearing,accurancy,speed,,,segmentIdx
|
||||
* WAYPOINT,time,lat,lon,alt,bearing,accuracy,speed,name,description,
|
||||
* Write track as CSV to a file. See RFC 4180 for info on CSV. Output three
|
||||
* tables.<br>
|
||||
* The first table contains the track info. Its columns are:<br>
|
||||
* "Track name","Activity type","Track description" <br>
|
||||
* <br>
|
||||
* The second table contains the markers. Its columns are:<br>
|
||||
* "Marker name","Marker type","Marker description","Latitude (deg)","Longitude
|
||||
* (deg)","Altitude (m)","Bearing (deg)","Accuracy (m)","Speed (m/s)","Time"<br>
|
||||
* <br>
|
||||
* The thrid table contains the points. Its columns are:<br>
|
||||
* "Segment","Point","Latitude (deg)","Longitude (deg)","Altitude (m)","Bearing
|
||||
* (deg)","Accuracy (m)","Speed (m/s)","Time","Power (W)","Cadence (rpm)","Heart
|
||||
* rate (bpm)","Battery level (%)"<br>
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
@@ -51,121 +57,41 @@ public class CsvTrackWriter implements TrackFormatWriter {
|
||||
SHORT_FORMAT.setMaximumFractionDigits(4);
|
||||
}
|
||||
|
||||
private int segmentIdx = 0;
|
||||
private int numFields = -1;
|
||||
private PrintWriter pw;
|
||||
private final Context context;
|
||||
private PrintWriter printWriter;
|
||||
private Track track;
|
||||
private int segmentIndex;
|
||||
private int pointIndex;
|
||||
|
||||
public CsvTrackWriter(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExtension() {
|
||||
return TrackFileFormat.CSV.getExtension();
|
||||
}
|
||||
|
||||
@SuppressWarnings("hiding")
|
||||
@Override
|
||||
public void prepare(Track track, OutputStream out) {
|
||||
this.track = track;
|
||||
this.pw = new PrintWriter(out);
|
||||
public void prepare(Track aTrack, OutputStream out) {
|
||||
track = aTrack;
|
||||
printWriter = new PrintWriter(out);
|
||||
segmentIndex = 0;
|
||||
pointIndex = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
printWriter.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeHeader() {
|
||||
writeCommaSeparatedLine("TYPE", "TIME", "LAT", "LON", "ALT", "BEARING",
|
||||
"ACCURACY", "SPEED", "NAME", "DESCRIPTION", "SEGMENT");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBeginTrack(Location firstPoint) {
|
||||
writeCommaSeparatedLine("TRACK",
|
||||
null, null, null, null, null, null, null,
|
||||
track.getName(), track.getDescription(),
|
||||
null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeOpenSegment() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeLocation(Location location) {
|
||||
String timeStr = FileUtils.FILE_TIMESTAMP_FORMAT.format(new Date(location.getTime()));
|
||||
writeCommaSeparatedLine("P",
|
||||
timeStr,
|
||||
Double.toString(location.getLatitude()),
|
||||
Double.toString(location.getLongitude()),
|
||||
Double.toString(location.getAltitude()),
|
||||
Double.toString(location.getBearing()),
|
||||
SHORT_FORMAT.format(location.getAccuracy()),
|
||||
SHORT_FORMAT.format(location.getSpeed()),
|
||||
null, null,
|
||||
Integer.toString(segmentIdx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBeginWaypoints() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeEndWaypoints() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeWaypoint(Waypoint waypoint) {
|
||||
Location location = waypoint.getLocation();
|
||||
String timeStr = FileUtils.FILE_TIMESTAMP_FORMAT.format(new Date(location.getTime()));
|
||||
writeCommaSeparatedLine("WAYPOINT",
|
||||
timeStr,
|
||||
Double.toString(location.getLatitude()),
|
||||
Double.toString(location.getLongitude()),
|
||||
Double.toString(location.getAltitude()),
|
||||
Double.toString(location.getBearing()),
|
||||
SHORT_FORMAT.format(location.getAccuracy()),
|
||||
SHORT_FORMAT.format(location.getSpeed()),
|
||||
waypoint.getName(),
|
||||
waypoint.getDescription(),
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a single line of a comma-separated-value file.
|
||||
*
|
||||
* @param strs the values to be written as comma-separated
|
||||
*/
|
||||
private void writeCommaSeparatedLine(String... strs) {
|
||||
if (numFields == -1) {
|
||||
numFields = strs.length;
|
||||
} else if (strs.length != numFields) {
|
||||
throw new IllegalArgumentException(
|
||||
"CSV lines with different number of fields");
|
||||
}
|
||||
|
||||
boolean isFirst = true;
|
||||
for (String str : strs) {
|
||||
if (!isFirst) {
|
||||
pw.print(',');
|
||||
}
|
||||
isFirst = false;
|
||||
|
||||
if (str != null) {
|
||||
pw.print('"');
|
||||
pw.print(str.replaceAll("\"", "\"\""));
|
||||
pw.print('"');
|
||||
}
|
||||
}
|
||||
pw.println();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeCloseSegment() {
|
||||
segmentIdx++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeEndTrack(Location lastPoint) {
|
||||
// Do nothing
|
||||
writeCommaSeparatedLine(context.getString(R.string.track_detail_track_name),
|
||||
context.getString(R.string.track_detail_activity_type_hint),
|
||||
context.getString(R.string.track_detail_track_description));
|
||||
writeCommaSeparatedLine(track.getName(), track.getCategory(), track.getDescription());
|
||||
writeCommaSeparatedLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -174,7 +100,144 @@ public class CsvTrackWriter implements TrackFormatWriter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
pw.close();
|
||||
public void writeBeginWaypoints() {
|
||||
writeCommaSeparatedLine(context.getString(R.string.marker_detail_marker_name),
|
||||
context.getString(R.string.marker_detail_marker_type_hint),
|
||||
context.getString(R.string.marker_detail_marker_description),
|
||||
context.getString(R.string.description_location_latitude),
|
||||
context.getString(R.string.description_location_longitude),
|
||||
context.getString(R.string.description_location_altitude),
|
||||
context.getString(R.string.description_location_bearing),
|
||||
context.getString(R.string.description_location_accuracy),
|
||||
context.getString(R.string.description_location_speed),
|
||||
context.getString(R.string.description_location_time));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeEndWaypoints() {
|
||||
writeCommaSeparatedLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeWaypoint(Waypoint waypoint) {
|
||||
Location location = waypoint.getLocation();
|
||||
writeCommaSeparatedLine(waypoint.getName(),
|
||||
waypoint.getCategory(),
|
||||
waypoint.getDescription(),
|
||||
Double.toString(location.getLatitude()),
|
||||
Double.toString(location.getLongitude()),
|
||||
Double.toString(location.getAltitude()),
|
||||
Double.toString(location.getBearing()),
|
||||
SHORT_FORMAT.format(location.getAccuracy()),
|
||||
SHORT_FORMAT.format(location.getSpeed()),
|
||||
StringUtils.formatDateTimeIso8601(location.getTime()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBeginTrack(Location firstPoint) {
|
||||
writeCommaSeparatedLine(context.getString(R.string.description_track_segment),
|
||||
context.getString(R.string.description_track_point),
|
||||
context.getString(R.string.description_location_latitude),
|
||||
context.getString(R.string.description_location_longitude),
|
||||
context.getString(R.string.description_location_altitude),
|
||||
context.getString(R.string.description_location_bearing),
|
||||
context.getString(R.string.description_location_accuracy),
|
||||
context.getString(R.string.description_location_speed),
|
||||
context.getString(R.string.description_location_time),
|
||||
context.getString(R.string.description_sensor_power),
|
||||
context.getString(R.string.description_sensor_cadence),
|
||||
context.getString(R.string.description_sensor_heart_rate),
|
||||
context.getString(R.string.description_sensor_battery_level));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeEndTrack(Location lastPoint) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeOpenSegment() {
|
||||
segmentIndex++;
|
||||
pointIndex = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeCloseSegment() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeLocation(Location location) {
|
||||
String power = null;
|
||||
String cadence = null;
|
||||
String heartRate = null;
|
||||
String batteryLevel = null;
|
||||
if (location instanceof MyTracksLocation) {
|
||||
SensorDataSet sensorDataSet = ((MyTracksLocation) location).getSensorDataSet();
|
||||
|
||||
if (sensorDataSet != null) {
|
||||
if (sensorDataSet.hasPower()) {
|
||||
SensorData sensorData = sensorDataSet.getPower();
|
||||
if (sensorData.hasValue() && sensorData.getState() == Sensor.SensorState.SENDING) {
|
||||
power = Double.toString(sensorData.getValue());
|
||||
}
|
||||
}
|
||||
if (sensorDataSet.hasCadence()) {
|
||||
SensorData sensorData = sensorDataSet.getCadence();
|
||||
if (sensorData.hasValue() && sensorData.getState() == Sensor.SensorState.SENDING) {
|
||||
cadence = Double.toString(sensorData.getValue());
|
||||
}
|
||||
}
|
||||
if (sensorDataSet.hasHeartRate()) {
|
||||
SensorData sensorData = sensorDataSet.getHeartRate();
|
||||
if (sensorData.hasValue() && sensorData.getState() == Sensor.SensorState.SENDING) {
|
||||
heartRate = Double.toString(sensorData.getValue());
|
||||
}
|
||||
}
|
||||
if (sensorDataSet.hasBatteryLevel()) {
|
||||
SensorData sensorData = sensorDataSet.getBatteryLevel();
|
||||
if (sensorData.hasValue() && sensorData.getState() == Sensor.SensorState.SENDING) {
|
||||
batteryLevel = Double.toString(sensorData.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pointIndex++;
|
||||
writeCommaSeparatedLine(Integer.toString(segmentIndex),
|
||||
Integer.toString(pointIndex),
|
||||
Double.toString(location.getLatitude()),
|
||||
Double.toString(location.getLongitude()),
|
||||
Double.toString(location.getAltitude()),
|
||||
Double.toString(location.getBearing()),
|
||||
SHORT_FORMAT.format(location.getAccuracy()),
|
||||
SHORT_FORMAT.format(location.getSpeed()),
|
||||
StringUtils.formatDateTimeIso8601(location.getTime()),
|
||||
power,
|
||||
cadence,
|
||||
heartRate,
|
||||
batteryLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a single line of a CSV file.
|
||||
*
|
||||
* @param values the values to be written as CSV
|
||||
*/
|
||||
private void writeCommaSeparatedLine(String... values) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
boolean isFirst = true;
|
||||
for (String value : values) {
|
||||
if (!isFirst) {
|
||||
builder.append(',');
|
||||
}
|
||||
isFirst = false;
|
||||
|
||||
if (value != null) {
|
||||
builder.append('"');
|
||||
builder.append(value.replaceAll("\"", "\"\""));
|
||||
builder.append('"');
|
||||
}
|
||||
}
|
||||
printWriter.println(builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,7 +450,7 @@ public class GpxImporter extends DefaultHandler {
|
||||
// Parse the time
|
||||
long time;
|
||||
try {
|
||||
time = StringUtils.parseXmlDateTime(content.trim());
|
||||
time = StringUtils.getTime(content.trim());
|
||||
} catch (IllegalArgumentException e) {
|
||||
String msg = createErrorMessage("Unable to parse time: " + content);
|
||||
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.Waypoint;
|
||||
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;
|
||||
|
||||
@@ -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/topografix.xsd\">");
|
||||
printWriter.println("<metadata>");
|
||||
printWriter.println("<name>" + StringUtils.stringAsCData(track.getName()) + "</name>");
|
||||
printWriter.println("<desc>" + StringUtils.stringAsCData(track.getDescription()) + "</desc>");
|
||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
|
||||
printWriter.println("</metadata>");
|
||||
}
|
||||
}
|
||||
@@ -111,8 +110,8 @@ public class GpxTrackWriter implements TrackFormatWriter {
|
||||
public void writeBeginTrack(Location firstLocation) {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<trk>");
|
||||
printWriter.println("<name>" + StringUtils.stringAsCData(track.getName()) + "</name>");
|
||||
printWriter.println("<desc>" + StringUtils.stringAsCData(track.getDescription()) + "</desc>");
|
||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
|
||||
printWriter.println("<extensions><topografix:color>c0c0c0</topografix:color></extensions>");
|
||||
}
|
||||
}
|
||||
@@ -139,8 +138,7 @@ public class GpxTrackWriter implements TrackFormatWriter {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<trkpt " + formatLocation(location) + ">");
|
||||
printWriter.println("<ele>" + ELEVATION_FORMAT.format(location.getAltitude()) + "</ele>");
|
||||
printWriter.println(
|
||||
"<time>" + FileUtils.FILE_TIMESTAMP_FORMAT.format(location.getTime()) + "</time>");
|
||||
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</time>");
|
||||
printWriter.println("</trkpt>");
|
||||
}
|
||||
}
|
||||
@@ -162,11 +160,10 @@ public class GpxTrackWriter implements TrackFormatWriter {
|
||||
if (location != null) {
|
||||
printWriter.println("<wpt " + formatLocation(location) + ">");
|
||||
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(
|
||||
"<time>" + FileUtils.FILE_TIMESTAMP_FORMAT.format(location.getTime()) + "</time>");
|
||||
printWriter.println("<name>" + StringUtils.stringAsCData(waypoint.getName()) + "</name>");
|
||||
printWriter.println(
|
||||
"<desc>" + StringUtils.stringAsCData(waypoint.getDescription()) + "</desc>");
|
||||
"<desc>" + StringUtils.formatCData(waypoint.getDescription()) + "</desc>");
|
||||
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.Waypoint;
|
||||
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.common.annotations.VisibleForTesting;
|
||||
@@ -43,6 +42,11 @@ import java.util.ArrayList;
|
||||
*/
|
||||
public class KmlTrackWriter implements TrackFormatWriter {
|
||||
|
||||
/**
|
||||
* ID of the KML feature to play a tour.
|
||||
*/
|
||||
public static final String TOUR_FEATURE_ID = "tour";
|
||||
|
||||
private static final String WAYPOINT_STYLE = "waypoint";
|
||||
private static final String STATISTICS_STYLE = "statistics";
|
||||
private static final String START_STYLE = "start";
|
||||
@@ -54,6 +58,17 @@ public class KmlTrackWriter implements TrackFormatWriter {
|
||||
private static final String POWER = "power";
|
||||
private static final String BATTER_LEVEL = "battery_level";
|
||||
|
||||
private static final String WAYPOINT_ICON =
|
||||
"http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png";
|
||||
private static final String STATISTICS_ICON =
|
||||
"http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png";
|
||||
private static final String START_ICON =
|
||||
"http://maps.google.com/mapfiles/kml/paddle/grn-circle.png";
|
||||
private static final String END_ICON =
|
||||
"http://maps.google.com/mapfiles/kml/paddle/red-circle.png";
|
||||
private static final String TRACK_ICON =
|
||||
"http://earth.google.com/images/kml-icons/track-directional/track-0.png";
|
||||
|
||||
private final Context context;
|
||||
private final DescriptionGenerator descriptionGenerator;
|
||||
private Track track;
|
||||
@@ -107,20 +122,16 @@ public class KmlTrackWriter implements TrackFormatWriter {
|
||||
printWriter.println("<open>1</open>");
|
||||
printWriter.println("<visibility>1</visibility>");
|
||||
printWriter.println(
|
||||
"<description>" + StringUtils.stringAsCData(track.getDescription()) + "</description>");
|
||||
printWriter.println("<name>" + StringUtils.stringAsCData(track.getName()) + "</name>");
|
||||
printWriter.println("<atom:author><atom:name>" + StringUtils.stringAsCData(
|
||||
"<description>" + StringUtils.formatCData(track.getDescription()) + "</description>");
|
||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||
printWriter.println("<atom:author><atom:name>" + StringUtils.formatCData(
|
||||
context.getString(R.string.send_google_by_my_tracks, "", ""))
|
||||
+ "</atom:name></atom:author>");
|
||||
writeTrackStyle();
|
||||
writePlacemarkerStyle(
|
||||
START_STYLE, "http://maps.google.com/mapfiles/kml/paddle/grn-circle.png", 32, 1);
|
||||
writePlacemarkerStyle(
|
||||
END_STYLE, "http://maps.google.com/mapfiles/kml/paddle/red-circle.png", 32, 1);
|
||||
writePlacemarkerStyle(
|
||||
STATISTICS_STYLE, "http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png", 20, 2);
|
||||
writePlacemarkerStyle(
|
||||
WAYPOINT_STYLE, "http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png", 20, 2);
|
||||
writePlacemarkerStyle(START_STYLE, START_ICON, 32, 1);
|
||||
writePlacemarkerStyle(END_STYLE, END_ICON, 32, 1);
|
||||
writePlacemarkerStyle(STATISTICS_STYLE, STATISTICS_ICON, 20, 2);
|
||||
writePlacemarkerStyle(WAYPOINT_STYLE, WAYPOINT_ICON, 20, 2);
|
||||
printWriter.println("<Schema id=\"" + SCHEMA_ID + "\">");
|
||||
writeSensorStyle(POWER, context.getString(R.string.description_sensor_power));
|
||||
writeSensorStyle(CADENCE, context.getString(R.string.description_sensor_cadence));
|
||||
@@ -142,7 +153,7 @@ public class KmlTrackWriter implements TrackFormatWriter {
|
||||
public void writeBeginWaypoints() {
|
||||
if (printWriter != null) {
|
||||
printWriter.println(
|
||||
"<Folder><name>" + StringUtils.stringAsCData(context.getString(R.string.menu_markers))
|
||||
"<Folder><name>" + StringUtils.formatCData(context.getString(R.string.menu_markers))
|
||||
+ "</name>");
|
||||
}
|
||||
}
|
||||
@@ -169,10 +180,10 @@ public class KmlTrackWriter implements TrackFormatWriter {
|
||||
if (printWriter != null) {
|
||||
String name = context.getString(R.string.marker_label_start, track.getName());
|
||||
writePlacemark(name, track.getDescription(), START_STYLE, firstLocation);
|
||||
printWriter.println("<Placemark>");
|
||||
printWriter.println("<Placemark id=\"" + TOUR_FEATURE_ID + "\">");
|
||||
printWriter.println(
|
||||
"<description>" + StringUtils.stringAsCData(track.getDescription()) + "</description>");
|
||||
printWriter.println("<name>" + StringUtils.stringAsCData(track.getName()) + "</name>");
|
||||
"<description>" + StringUtils.formatCData(track.getDescription()) + "</description>");
|
||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||
printWriter.println("<styleUrl>#" + TRACK_STYLE + "</styleUrl>");
|
||||
printWriter.println("<gx:MultiTrack>");
|
||||
printWriter.println("<altitudeMode>absolute</altitudeMode>");
|
||||
@@ -232,8 +243,7 @@ public class KmlTrackWriter implements TrackFormatWriter {
|
||||
@Override
|
||||
public void writeLocation(Location location) {
|
||||
if (printWriter != null) {
|
||||
printWriter.println(
|
||||
"<when>" + FileUtils.FILE_TIMESTAMP_FORMAT.format(location.getTime()) + "</when>");
|
||||
printWriter.println("<when>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</when>");
|
||||
printWriter.println(
|
||||
"<gx:coord>" + location.getLongitude() + " " + location.getLatitude() + " "
|
||||
+ location.getAltitude() + "</gx:coord>");
|
||||
@@ -309,8 +319,8 @@ public class KmlTrackWriter implements TrackFormatWriter {
|
||||
if (location != null) {
|
||||
printWriter.println("<Placemark>");
|
||||
printWriter.println(
|
||||
"<description>" + StringUtils.stringAsCData(description) + "</description>");
|
||||
printWriter.println("<name>" + StringUtils.stringAsCData(name) + "</name>");
|
||||
"<description>" + StringUtils.formatCData(description) + "</description>");
|
||||
printWriter.println("<name>" + StringUtils.formatCData(name) + "</name>");
|
||||
printWriter.println("<styleUrl>#" + styleName + "</styleUrl>");
|
||||
printWriter.println("<Point>");
|
||||
printWriter.println(
|
||||
@@ -325,9 +335,13 @@ public class KmlTrackWriter implements TrackFormatWriter {
|
||||
* Writes the track style.
|
||||
*/
|
||||
private void writeTrackStyle() {
|
||||
printWriter.println("<Style id=\"" + TRACK_STYLE + "\"><LineStyle>");
|
||||
printWriter.println("<color>7f0000ff</color><width>4</width>");
|
||||
printWriter.println("</LineStyle></Style>");
|
||||
printWriter.println("<Style id=\"" + TRACK_STYLE + "\">");
|
||||
printWriter.println("<LineStyle><color>7f0000ff</color><width>4</width></LineStyle>");
|
||||
printWriter.println("<IconStyle>");
|
||||
printWriter.println("<scale>1.3</scale>");
|
||||
printWriter.println("<Icon><href>" + TRACK_ICON + "</href></Icon>");
|
||||
printWriter.println("</IconStyle>");
|
||||
printWriter.println("</Style>");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,7 +370,7 @@ public class KmlTrackWriter implements TrackFormatWriter {
|
||||
private void writeSensorStyle(String name, String displayName) {
|
||||
printWriter.println("<gx:SimpleArrayField name=\"" + name + "\" type=\"int\">");
|
||||
printWriter.println(
|
||||
"<displayName>" + StringUtils.stringAsCData(displayName) + "</displayName>");
|
||||
"<displayName>" + StringUtils.formatCData(displayName) + "</displayName>");
|
||||
printWriter.println("</gx:SimpleArrayField>");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,9 +159,11 @@ public class SaveActivity extends Activity {
|
||||
|
||||
Uri uri = Uri.fromFile(new File(writer.getAbsolutePath()));
|
||||
Intent intent = new Intent()
|
||||
.setClassName(PlayTrackUtils.GOOGLE_EARTH_PACKAGE, PlayTrackUtils.GOOGLE_EARTH_CLASS)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.setDataAndType(uri, PlayTrackUtils.KML_MIME_TYPE);
|
||||
.setDataAndType(uri, PlayTrackUtils.KML_MIME_TYPE)
|
||||
.putExtra(PlayTrackUtils.TOUR_FEATURE_ID, KmlTrackWriter.TOUR_FEATURE_ID);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,86 +22,89 @@ import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
|
||||
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.SystemUtils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.os.Build;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Write out a a track in the Garmin training center database, tcx format.
|
||||
* As defined by:
|
||||
* http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2
|
||||
*
|
||||
* The TCX file written by this class has been verified as compatible with
|
||||
* Garmin Training Center 3.5.3.
|
||||
* Write track as TCX to a file. See http://developer.garmin.com/schemas/tcx/v2/
|
||||
* for info on TCX. <br>
|
||||
* The TCX file output is verified by uploading the file to
|
||||
* http://connect.garmin.com/.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
* @author Dominik Rttsches
|
||||
*/
|
||||
public class TcxTrackWriter implements TrackFormatWriter {
|
||||
|
||||
// "Biking" related string IDs
|
||||
/**
|
||||
* TCX sport type. See the TCX spec.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private enum SportType {
|
||||
RUNNING("Running"),
|
||||
BIKING("Biking"),
|
||||
OTHER("Other");
|
||||
|
||||
private final String name;
|
||||
|
||||
private SportType(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the sport type
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
// My Tracks categories that are considered as TCX biking sport type.
|
||||
private static final int TCX_SPORT_BIKING_IDS[] = {
|
||||
R.string.activity_type_cycling,
|
||||
R.string.activity_type_dirt_bike,
|
||||
R.string.activity_type_mountain_biking,
|
||||
R.string.activity_type_road_biking,
|
||||
R.string.activity_type_track_cycling,
|
||||
};
|
||||
R.string.activity_type_track_cycling };
|
||||
|
||||
// "Running" related string IDs
|
||||
// My Tracks categories that are considered as TCX running sport type.
|
||||
private static final int TCX_SPORT_RUNNING_IDS[] = {
|
||||
R.string.activity_type_running,
|
||||
R.string.activity_type_speed_walking,
|
||||
R.string.activity_type_street_running,
|
||||
R.string.activity_type_track_running,
|
||||
R.string.activity_type_trail_running,
|
||||
R.string.activity_type_walking,
|
||||
};
|
||||
|
||||
// These are the only sports allowed by the TCX v2 specification for fields
|
||||
// of type Sport_t.
|
||||
private static final String TCX_SPORT_BIKING = "Biking";
|
||||
private static final String TCX_SPORT_RUNNING = "Running";
|
||||
private static final String TCX_SPORT_OTHER = "Other";
|
||||
|
||||
// Values for fields of type Build_t/Type.
|
||||
private static final String TCX_TYPE_RELEASE = "Release";
|
||||
private static final String TCX_TYPE_INTERNAL = "Internal";
|
||||
R.string.activity_type_walking };
|
||||
|
||||
private final Context context;
|
||||
|
||||
private PrintWriter pw = null;
|
||||
private Track track;
|
||||
|
||||
// Determines whether to encode cadence value as running or cycling cadence.
|
||||
private boolean sportIsCycling;
|
||||
private PrintWriter printWriter;
|
||||
private SportType sportType;
|
||||
|
||||
public TcxTrackWriter(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@SuppressWarnings("hiding")
|
||||
@Override
|
||||
public void prepare(Track track, OutputStream out) {
|
||||
this.track = track;
|
||||
this.pw = new PrintWriter(out);
|
||||
this.sportIsCycling = categoryToTcxSport(track.getCategory()).equals(TCX_SPORT_BIKING);
|
||||
public void prepare(Track aTrack, OutputStream out) {
|
||||
this.track = aTrack;
|
||||
this.printWriter = new PrintWriter(out);
|
||||
this.sportType = getSportType(track.getCategory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (pw != null) {
|
||||
pw.close();
|
||||
pw = null;
|
||||
if (printWriter != null) {
|
||||
printWriter.close();
|
||||
printWriter = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,246 +115,214 @@ public class TcxTrackWriter implements TrackFormatWriter {
|
||||
|
||||
@Override
|
||||
public void writeHeader() {
|
||||
if (pw == null) {
|
||||
return;
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
printWriter.println("<TrainingCenterDatabase"
|
||||
+ " xmlns=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2\"");
|
||||
printWriter.println("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
|
||||
printWriter.println("xsi:schemaLocation="
|
||||
+ "\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2"
|
||||
+ " http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd\">");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeFooter() {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<Author xsi:type=\"Application_t\">");
|
||||
printWriter.println("<Name>"
|
||||
+ StringUtils.formatCData(context.getString(R.string.send_google_by_my_tracks, "", ""))
|
||||
+ "</Name>");
|
||||
// <Build>, <LangID>, and <PartNumber> are required by type=Application_t.
|
||||
printWriter.println("<Build>");
|
||||
writeVersion();
|
||||
printWriter.println("</Build>");
|
||||
printWriter.println("<LangID>" + Locale.getDefault().getLanguage() + "</LangID>");
|
||||
printWriter.println("<PartNumber>000-00000-00</PartNumber>");
|
||||
printWriter.println("</Author>");
|
||||
printWriter.println("</TrainingCenterDatabase>");
|
||||
}
|
||||
pw.format("<?xml version=\"1.0\" encoding=\"%s\" standalone=\"no\" ?>\n",
|
||||
Charset.defaultCharset().name());
|
||||
pw.print("<TrainingCenterDatabase ");
|
||||
pw.print("xmlns=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2\" ");
|
||||
pw.print("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
|
||||
pw.print("xsi:schemaLocation=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 ");
|
||||
pw.println("http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd\">");
|
||||
pw.println();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBeginTrack(Location firstPoint) {
|
||||
if (pw == null) {
|
||||
return;
|
||||
}
|
||||
if (printWriter != null) {
|
||||
String startTime = StringUtils.formatDateTimeIso8601(track.getStatistics().getStartTime());
|
||||
long totalTimeInSeconds = track.getStatistics().getTotalTime() / 1000;
|
||||
|
||||
String startTime = FileUtils.FILE_TIMESTAMP_FORMAT.format(track.getStatistics().getStartTime());
|
||||
|
||||
pw.println(" <Activities>");
|
||||
pw.format(" <Activity Sport=\"%s\">\n", categoryToTcxSport(track.getCategory()));
|
||||
pw.format(" <Id>%s</Id>\n", startTime);
|
||||
pw.format(" <Lap StartTime=\"%s\">\n", startTime);
|
||||
pw.print(" <TotalTimeSeconds>");
|
||||
pw.print(track.getStatistics().getTotalTime() / 1000);
|
||||
pw.println("</TotalTimeSeconds>");
|
||||
pw.print(" <DistanceMeters>");
|
||||
pw.print(track.getStatistics().getTotalDistance());
|
||||
pw.println("</DistanceMeters>");
|
||||
// TODO max speed etc.
|
||||
// Calories are a required element just put in 0.
|
||||
pw.print("<Calories>0</Calories>");
|
||||
pw.println("<Intensity>Active</Intensity>");
|
||||
pw.println("<TriggerMethod>Manual</TriggerMethod>");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeOpenSegment() {
|
||||
if (pw != null) {
|
||||
pw.println(" <Track>");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeLocation(Location location) {
|
||||
if (pw == null) {
|
||||
return;
|
||||
}
|
||||
pw.println(" <Trackpoint>");
|
||||
Date d = new Date(location.getTime());
|
||||
pw.println(" <Time>" + FileUtils.FILE_TIMESTAMP_FORMAT.format(d) + "</Time>");
|
||||
pw.println(" <Position>");
|
||||
|
||||
pw.print(" <LatitudeDegrees>");
|
||||
pw.print(location.getLatitude());
|
||||
pw.println("</LatitudeDegrees>");
|
||||
|
||||
pw.print(" <LongitudeDegrees>");
|
||||
pw.print(location.getLongitude());
|
||||
pw.println("</LongitudeDegrees>");
|
||||
|
||||
pw.println(" </Position>");
|
||||
pw.print(" <AltitudeMeters>");
|
||||
pw.print(location.getAltitude());
|
||||
pw.println("</AltitudeMeters>");
|
||||
|
||||
if (location instanceof MyTracksLocation) {
|
||||
SensorDataSet sensorData = ((MyTracksLocation) location).getSensorDataSet();
|
||||
if (sensorData != null) {
|
||||
if (sensorData.hasHeartRate()
|
||||
&& sensorData.getHeartRate().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorData.getHeartRate().hasValue()) {
|
||||
pw.print(" <HeartRateBpm>");
|
||||
pw.print("<Value>");
|
||||
pw.print(sensorData.getHeartRate().getValue());
|
||||
pw.print("</Value>");
|
||||
pw.println("</HeartRateBpm>");
|
||||
}
|
||||
|
||||
boolean cadenceAvailable = sensorData.hasCadence()
|
||||
&& sensorData.getCadence().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorData.getCadence().hasValue();
|
||||
|
||||
// TCX Trackpoint_t contains a sequence. Thus, the legacy XML element
|
||||
// <Cadence> needs to be put before <Extensions>.
|
||||
// This field should only be used for the case that activity was marked as biking.
|
||||
// Otherwise cadence is interpreted as running cadence data which
|
||||
// is written in the <Extensions> as <RunCadence>.
|
||||
if (sportIsCycling && cadenceAvailable) {
|
||||
pw.print(" <Cadence>");
|
||||
pw.print(Math.min(254, sensorData.getCadence().getValue()));
|
||||
pw.println("</Cadence>");
|
||||
}
|
||||
|
||||
boolean powerAvailable = sensorData.hasPower()
|
||||
&& sensorData.getPower().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorData.getPower().hasValue();
|
||||
|
||||
if(powerAvailable || (!sportIsCycling && cadenceAvailable)) {
|
||||
pw.print(" <Extensions>");
|
||||
pw.print("<TPX xmlns=\"http://www.garmin.com/xmlschemas/ActivityExtension/v2\">");
|
||||
|
||||
// RunCadence needs to be put before power in order to be understood
|
||||
// by Garmin Training Center.
|
||||
if (!sportIsCycling && cadenceAvailable) {
|
||||
pw.print("<RunCadence>");
|
||||
pw.print(Math.min(254, sensorData.getCadence().getValue()));
|
||||
pw.print("</RunCadence>");
|
||||
}
|
||||
|
||||
if (powerAvailable) {
|
||||
pw.print("<Watts>");
|
||||
pw.print(sensorData.getPower().getValue());
|
||||
pw.print("</Watts>");
|
||||
}
|
||||
|
||||
pw.println("</TPX></Extensions>");
|
||||
}
|
||||
}
|
||||
}
|
||||
pw.println(" </Trackpoint>");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeCloseSegment() {
|
||||
if (pw != null) {
|
||||
pw.println(" </Track>");
|
||||
printWriter.println("<Activities>");
|
||||
printWriter.println("<Activity Sport=\"" + sportType.getName() + "\">");
|
||||
printWriter.println("<Id>" + startTime + "</Id>");
|
||||
printWriter.println("<Lap StartTime=\"" + startTime + "\">");
|
||||
printWriter.println("<TotalTimeSeconds>" + totalTimeInSeconds + "</TotalTimeSeconds>");
|
||||
printWriter.println("<DistanceMeters>" + track.getStatistics().getTotalDistance()
|
||||
+ "</DistanceMeters>");
|
||||
// <Calories> is required, just put in 0.
|
||||
printWriter.println("<Calories>0</Calories>");
|
||||
printWriter.println("<Intensity>Active</Intensity>");
|
||||
printWriter.println("<TriggerMethod>Manual</TriggerMethod>");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeEndTrack(Location lastPoint) {
|
||||
if (pw == null) {
|
||||
return;
|
||||
if (printWriter != null) {
|
||||
printWriter.println("</Lap>");
|
||||
printWriter.println("<Creator xsi:type=\"Device_t\">");
|
||||
printWriter.println("<Name>"
|
||||
+ StringUtils.formatCData(context.getString(R.string.send_google_by_my_tracks, "", ""))
|
||||
+ "</Name>");
|
||||
// <UnitId>, <ProductID>, and <Version> are required for type=Device_t.
|
||||
printWriter.println("<UnitId>0</UnitId>");
|
||||
printWriter.println("<ProductID>0</ProductID>");
|
||||
writeVersion();
|
||||
printWriter.println("</Creator>");
|
||||
printWriter.println("</Activity>");
|
||||
printWriter.println("</Activities>");
|
||||
}
|
||||
pw.println(" </Lap>");
|
||||
pw.print(" <Creator xsi:type=\"Device_t\">");
|
||||
pw.format("<Name>My Tracks running on %s</Name>\n", Build.MODEL);
|
||||
|
||||
// The following code is correct. ID is inconsistently capitalized in the
|
||||
// TCX schema.
|
||||
pw.println("<UnitId>0</UnitId>");
|
||||
pw.println("<ProductID>0</ProductID>");
|
||||
|
||||
writeVersion();
|
||||
|
||||
pw.println("</Creator>");
|
||||
pw.println(" </Activity>");
|
||||
pw.println(" </Activities>");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeFooter() {
|
||||
if (pw == null) {
|
||||
return;
|
||||
public void writeOpenSegment() {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<Track>");
|
||||
}
|
||||
pw.println(" <Author xsi:type=\"Application_t\">");
|
||||
}
|
||||
|
||||
// We put the version in the name because there isn't a better place for
|
||||
// it. The TCX schema tightly defined the Version tag, so we can't put it
|
||||
// there. They've similarly constrained the PartNumber tag, so it can't go
|
||||
// there either.
|
||||
pw.format("<Name>My Tracks %s by Google</Name>\n", SystemUtils.getMyTracksVersion(context));
|
||||
@Override
|
||||
public void writeCloseSegment() {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("</Track>");
|
||||
}
|
||||
}
|
||||
|
||||
pw.println("<Build>");
|
||||
@Override
|
||||
public void writeLocation(Location location) {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<Trackpoint>");
|
||||
printWriter.println("<Time>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</Time>");
|
||||
printWriter.println("<Position>");
|
||||
printWriter.println("<LatitudeDegrees>" + location.getLatitude() + "</LatitudeDegrees>");
|
||||
printWriter.println("<LongitudeDegrees>" + location.getLongitude() + "</LongitudeDegrees>");
|
||||
printWriter.println("</Position>");
|
||||
printWriter.println("<AltitudeMeters>" + location.getAltitude() + "</AltitudeMeters>");
|
||||
|
||||
writeVersion();
|
||||
if (location instanceof MyTracksLocation) {
|
||||
SensorDataSet sensorDataSet = ((MyTracksLocation) location).getSensorDataSet();
|
||||
if (sensorDataSet != null) {
|
||||
boolean heartRateAvailable = sensorDataSet.hasHeartRate()
|
||||
&& sensorDataSet.getHeartRate().hasValue()
|
||||
&& sensorDataSet.getHeartRate().getState() == Sensor.SensorState.SENDING;
|
||||
boolean cadenceAvailable = sensorDataSet.hasCadence()
|
||||
&& sensorDataSet.getCadence().hasValue()
|
||||
&& sensorDataSet.getCadence().getState() == Sensor.SensorState.SENDING;
|
||||
boolean powerAvailable = sensorDataSet.hasPower()
|
||||
&& sensorDataSet.getPower().hasValue()
|
||||
&& sensorDataSet.getPower().getState() == Sensor.SensorState.SENDING;
|
||||
|
||||
if (heartRateAvailable) {
|
||||
printWriter.println("<HeartRateBpm>");
|
||||
printWriter.println("<Value>" + sensorDataSet.getHeartRate().getValue() + "</Value>");
|
||||
printWriter.println("</HeartRateBpm>");
|
||||
}
|
||||
|
||||
pw.format("<Type>%s</Type>\n", SystemUtils.isRelease(context) ? TCX_TYPE_RELEASE
|
||||
: TCX_TYPE_INTERNAL);
|
||||
pw.println("</Build>");
|
||||
pw.format("<LangID>%s</LangID>\n", Locale.getDefault().getLanguage());
|
||||
pw.println("<PartNumber>000-00000-00</PartNumber>");
|
||||
pw.println("</Author>");
|
||||
pw.println("</TrainingCenterDatabase>");
|
||||
// <Cadence> needs to be put before <Extensions>.
|
||||
// According to the TCX spec, <Cadence> is only for the biking sport
|
||||
// type. For others, use <RunCadence> in <Extensions>.
|
||||
if (cadenceAvailable && sportType == SportType.BIKING) {
|
||||
// The spec requires the max value be 254.
|
||||
printWriter.println(
|
||||
"<Cadence>" + Math.min(254, sensorDataSet.getCadence().getValue()) + "</Cadence>");
|
||||
}
|
||||
|
||||
if ((cadenceAvailable && sportType != SportType.BIKING) || powerAvailable) {
|
||||
printWriter.println("<Extensions>");
|
||||
printWriter.println(
|
||||
"<TPX xmlns=\"http://www.garmin.com/xmlschemas/ActivityExtension/v2\">");
|
||||
|
||||
// <RunCadence> needs to be put before <Watts>.
|
||||
if (cadenceAvailable && sportType != SportType.BIKING) {
|
||||
// The spec requires the max value to be 254.
|
||||
printWriter.println("<RunCadence>"
|
||||
+ Math.min(254, sensorDataSet.getCadence().getValue()) + "</RunCadence>");
|
||||
}
|
||||
|
||||
if (powerAvailable) {
|
||||
printWriter.println("<Watts>" + sensorDataSet.getPower().getValue() + "</Watts>");
|
||||
}
|
||||
printWriter.println("</TPX>");
|
||||
printWriter.println("</Extensions>");
|
||||
}
|
||||
}
|
||||
}
|
||||
printWriter.println("</Trackpoint>");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBeginWaypoints() {
|
||||
// Do nothing
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeEndWaypoints() {
|
||||
// Do nothing
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeWaypoint(Waypoint waypoint) {
|
||||
// TODO Write out the waypoints somewhere.
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the TCX Version.
|
||||
*/
|
||||
private void writeVersion() {
|
||||
if (pw == null) {
|
||||
return;
|
||||
}
|
||||
// Split the My Tracks version code into VersionMajor, VersionMinor, and,
|
||||
// BuildMajor to fit the integer type requirement for these fields in the
|
||||
// TCX spec.
|
||||
String[] versionComponents = SystemUtils.getMyTracksVersion(context).split("\\.");
|
||||
int versionMajor = versionComponents.length > 0 ? Integer.valueOf(versionComponents[0]) : 0;
|
||||
int versionMinor = versionComponents.length > 1 ? Integer.valueOf(versionComponents[1]) : 0;
|
||||
int buildMajor = versionComponents.length > 2 ? Integer.valueOf(versionComponents[2]) : 0;
|
||||
|
||||
// Splitting the myTracks version code into VersionMajor, VersionMinor and BuildMajor
|
||||
// to fit the integer type requirement for these fields in the TCX spec.
|
||||
// Putting a string like "x.x.x" into VersionMajor breaks XML validation.
|
||||
// We also set the BuildMinor version to 1 if this is a development build to
|
||||
// signify that this build is newer than the one associated with the
|
||||
// version code given in BuildMajor.
|
||||
|
||||
String[] myTracksVersionComponents = SystemUtils.getMyTracksVersion(context).split("\\.");
|
||||
|
||||
pw.println("<Version>");
|
||||
pw.format("<VersionMajor>%d</VersionMajor>\n", Integer.valueOf(myTracksVersionComponents[0]));
|
||||
pw.format("<VersionMinor>%d</VersionMinor>\n", Integer.valueOf(myTracksVersionComponents[1]));
|
||||
// TCX schema says these are optional but http://connect.garmin.com only accepts
|
||||
// the TCX file when they are present.
|
||||
pw.format("<BuildMajor>%d</BuildMajor>\n", Integer.valueOf(myTracksVersionComponents[2]));
|
||||
pw.format("<BuildMinor>%d</BuildMinor>\n", SystemUtils.isRelease(context) ? 0 : 1);
|
||||
pw.println("</Version>");
|
||||
printWriter.println("<Version>");
|
||||
printWriter.println("<VersionMajor>" + versionMajor + "</VersionMajor>");
|
||||
printWriter.println("<VersionMinor>" + versionMinor + "</VersionMinor>");
|
||||
// According to TCX spec, these are optional. But http://connect.garmin.com
|
||||
// requires them.
|
||||
printWriter.println("<BuildMajor>" + buildMajor + "</BuildMajor>");
|
||||
printWriter.println("<BuildMinor>0</BuildMinor>");
|
||||
printWriter.println("</Version>");
|
||||
}
|
||||
|
||||
private String categoryToTcxSport(String category) {
|
||||
/**
|
||||
* Gets the sport type from the category.
|
||||
*
|
||||
* @param category the category
|
||||
*/
|
||||
private SportType getSportType(String category) {
|
||||
category = category.trim();
|
||||
|
||||
// category is possibly localized here, so compare it to localized string resources
|
||||
// For tracks with localized category.
|
||||
for (int i : TCX_SPORT_RUNNING_IDS) {
|
||||
if (category.equalsIgnoreCase(context.getResources().getString(i))) {
|
||||
return TCX_SPORT_RUNNING;
|
||||
if (category.equalsIgnoreCase(context.getString(i))) {
|
||||
return SportType.RUNNING;
|
||||
}
|
||||
}
|
||||
for (int i : TCX_SPORT_BIKING_IDS) {
|
||||
if (category.equalsIgnoreCase(context.getResources().getString(i))) {
|
||||
return TCX_SPORT_BIKING;
|
||||
if (category.equalsIgnoreCase(context.getString(i))) {
|
||||
return SportType.BIKING;
|
||||
}
|
||||
}
|
||||
|
||||
// for tracks without localized activity type
|
||||
if (category.equalsIgnoreCase(TCX_SPORT_RUNNING)) {
|
||||
return TCX_SPORT_RUNNING;
|
||||
} else if (category.equalsIgnoreCase(TCX_SPORT_BIKING)) {
|
||||
return TCX_SPORT_BIKING;
|
||||
// For tracks without localized category.
|
||||
if (category.equalsIgnoreCase(SportType.RUNNING.getName())) {
|
||||
return SportType.RUNNING;
|
||||
} else if (category.equalsIgnoreCase(SportType.BIKING.getName())) {
|
||||
return SportType.BIKING;
|
||||
} else {
|
||||
return TCX_SPORT_OTHER;
|
||||
return SportType.OTHER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public class TrackWriterFactory {
|
||||
CSV {
|
||||
@Override
|
||||
public TrackFormatWriter newFormatWriter(Context context) {
|
||||
return new CsvTrackWriter();
|
||||
return new CsvTrackWriter(context);
|
||||
}
|
||||
},
|
||||
TCX {
|
||||
|
||||
+16
-10
@@ -21,6 +21,7 @@ import com.google.android.apps.mytracks.io.sendtogoogle.AbstractSendAsyncTask;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendRequest;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.UploadResultActivity;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
@@ -45,18 +46,23 @@ public class SendFusionTablesActivity extends AbstractSendActivity {
|
||||
protected void startNextActivity(boolean success, boolean isCancel) {
|
||||
sendRequest.setFusionTablesSuccess(success);
|
||||
|
||||
Class<?> next;
|
||||
if (isCancel) {
|
||||
next = UploadResultActivity.class;
|
||||
} else {
|
||||
if (sendRequest.isSendDocs()) {
|
||||
next = SendDocsActivity.class;
|
||||
} else {
|
||||
next = UploadResultActivity.class;
|
||||
}
|
||||
}
|
||||
Class<?> next = getNextClass(sendRequest, isCancel);
|
||||
|
||||
Intent intent = new Intent(this, next).putExtra(SendRequest.SEND_REQUEST_KEY, sendRequest);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Class<?> getNextClass(SendRequest request, boolean isCancel) {
|
||||
if (isCancel) {
|
||||
return UploadResultActivity.class;
|
||||
} else {
|
||||
if (request.isSendDocs()) {
|
||||
return SendDocsActivity.class;
|
||||
} else {
|
||||
return UploadResultActivity.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -454,7 +454,7 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
* @param total the number of total locations
|
||||
*/
|
||||
private void updateProgress(int uploaded, int total) {
|
||||
double totalPercentage = uploaded / total;
|
||||
double totalPercentage = (double) uploaded / total;
|
||||
double scaledPercentage = totalPercentage
|
||||
* (PROGRESS_UPLOAD_DATA_MAX - PROGRESS_UPLOAD_DATA_MIN) + PROGRESS_UPLOAD_DATA_MIN;
|
||||
publishProgress((int) scaledPercentage);
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.google.android.apps.mytracks.io.maps;
|
||||
import com.google.android.apps.mytracks.io.gdata.maps.MapsMapMetadata;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendRequest;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
@@ -46,12 +47,14 @@ import java.util.ArrayList;
|
||||
public class ChooseMapActivity extends Activity {
|
||||
|
||||
private static final int PROGRESS_DIALOG = 1;
|
||||
private static final int ERROR_DIALOG = 2;
|
||||
|
||||
@VisibleForTesting
|
||||
static final int ERROR_DIALOG = 2;
|
||||
|
||||
private SendRequest sendRequest;
|
||||
private ChooseMapAsyncTask asyncTask;
|
||||
private ProgressDialog progressDialog;
|
||||
private ArrayAdapter<ListItem> arrayAdapter;
|
||||
@VisibleForTesting
|
||||
ArrayAdapter<ListItem> arrayAdapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
@@ -166,7 +169,7 @@ public class ChooseMapActivity extends Activity {
|
||||
*/
|
||||
public void onAsyncTaskCompleted(
|
||||
boolean success, ArrayList<String> mapIds, ArrayList<MapsMapMetadata> mapData) {
|
||||
removeDialog(PROGRESS_DIALOG);
|
||||
removeProgressDialog();
|
||||
if (success) {
|
||||
arrayAdapter.clear();
|
||||
// To prevent displaying the emptyView message momentarily before the
|
||||
@@ -181,7 +184,7 @@ public class ChooseMapActivity extends Activity {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
showDialog(ERROR_DIALOG);
|
||||
showErrorDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +194,22 @@ public class ChooseMapActivity extends Activity {
|
||||
public void showProgressDialog() {
|
||||
showDialog(PROGRESS_DIALOG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the error dialog.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void showErrorDialog() {
|
||||
showDialog(ERROR_DIALOG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the progress dialog.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void removeProgressDialog() {
|
||||
removeDialog(PROGRESS_DIALOG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the next activity, {@link SendMapsActivity}.
|
||||
@@ -210,7 +229,8 @@ public class ChooseMapActivity extends Activity {
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private class ListItem {
|
||||
@VisibleForTesting
|
||||
class ListItem {
|
||||
private String mapId;
|
||||
private MapsMapMetadata mapData;
|
||||
|
||||
@@ -233,4 +253,4 @@ public class ChooseMapActivity extends Activity {
|
||||
return mapData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import com.google.android.apps.mytracks.io.sendtogoogle.AbstractSendAsyncTask;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendRequest;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.UploadResultActivity;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
@@ -47,20 +48,25 @@ public class SendMapsActivity extends AbstractSendActivity {
|
||||
protected void startNextActivity(boolean success, boolean isCancel) {
|
||||
sendRequest.setMapsSuccess(success);
|
||||
|
||||
Class<?> next;
|
||||
if (isCancel) {
|
||||
next = UploadResultActivity.class;
|
||||
} else {
|
||||
if (sendRequest.isSendFusionTables()) {
|
||||
next = SendFusionTablesActivity.class;
|
||||
} else if (sendRequest.isSendDocs()) {
|
||||
next = SendDocsActivity.class;
|
||||
} else {
|
||||
next = UploadResultActivity.class;
|
||||
}
|
||||
}
|
||||
Class<?> next = getNextClass(sendRequest, isCancel);
|
||||
|
||||
Intent intent = new Intent(this, next).putExtra(SendRequest.SEND_REQUEST_KEY, sendRequest);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Class<?> getNextClass(SendRequest request, boolean isCancel) {
|
||||
if (isCancel) {
|
||||
return UploadResultActivity.class;
|
||||
} else {
|
||||
if (request.isSendFusionTables()) {
|
||||
return SendFusionTablesActivity.class;
|
||||
} else if (request.isSendDocs()) {
|
||||
return SendDocsActivity.class;
|
||||
} else {
|
||||
return UploadResultActivity.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,8 +472,8 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
* @param total the number of total locations
|
||||
*/
|
||||
private void updateProgress(int uploaded, int total) {
|
||||
double totalPercentage = uploaded / total;
|
||||
double scaledPercentage = totalPercentage
|
||||
double totalPercentage = (double) uploaded / total;
|
||||
double scaledPercentage = totalPercentage
|
||||
* (PROGRESS_UPLOAD_DATA_MAX - PROGRESS_UPLOAD_DATA_MIN) + PROGRESS_UPLOAD_DATA_MIN;
|
||||
publishProgress((int) scaledPercentage);
|
||||
}
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ public class AccountChooserActivity extends Activity {
|
||||
super.onResume();
|
||||
if (accounts.length == 0) {
|
||||
showDialog(NO_ACCOUNT_DIALOG);
|
||||
} else {
|
||||
} else if (accounts.length > 1 ) {
|
||||
showDialog(CHOOSE_ACCOUNT_DIALOG);
|
||||
}
|
||||
}
|
||||
|
||||
+8
-10
@@ -15,10 +15,9 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io.sendtogoogle;
|
||||
|
||||
import com.google.android.apps.analytics.GoogleAnalyticsTracker;
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.util.AnalyticsUtils;
|
||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
@@ -39,6 +38,8 @@ import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.TableRow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A chooser to select the Google services to upload a track to.
|
||||
*
|
||||
@@ -246,20 +247,17 @@ public class UploadServiceChooserActivity extends Activity {
|
||||
* Sends stats to Google Analytics.
|
||||
*/
|
||||
private void sendStats() {
|
||||
GoogleAnalyticsTracker tracker = GoogleAnalyticsTracker.getInstance();
|
||||
tracker.start(getString(R.string.my_tracks_analytics_id), getApplicationContext());
|
||||
tracker.setProductVersion("android-mytracks", SystemUtils.getMyTracksVersion(this));
|
||||
ArrayList<String> pages = new ArrayList<String>();
|
||||
if (sendRequest.isSendMaps()) {
|
||||
tracker.trackPageView("/send/maps");
|
||||
pages.add("/send/maps");
|
||||
}
|
||||
if (sendRequest.isSendFusionTables()) {
|
||||
tracker.trackPageView("/send/fusion_tables");
|
||||
pages.add("/send/fusion_tables");
|
||||
}
|
||||
if (sendRequest.isSendDocs()) {
|
||||
tracker.trackPageView("/send/docs");
|
||||
pages.add("/send/docs");
|
||||
}
|
||||
tracker.dispatch();
|
||||
tracker.stop();
|
||||
AnalyticsUtils.sendPageViews(this, pages.toArray(new String[pages.size()]));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.util;
|
||||
|
||||
import com.google.android.apps.analytics.GoogleAnalyticsTracker;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Utitlites for sending pageviews to Google Analytics.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class AnalyticsUtils {
|
||||
|
||||
private AnalyticsUtils() {}
|
||||
|
||||
public static void sendPageViews(Context context, String ... pages) {
|
||||
GoogleAnalyticsTracker tracker = GoogleAnalyticsTracker.getInstance();
|
||||
tracker.start(context.getString(R.string.my_tracks_analytics_id), context);
|
||||
tracker.setProductVersion("android-mytracks", SystemUtils.getMyTracksVersion(context));
|
||||
for (String page : pages) {
|
||||
tracker.trackPageView(page);
|
||||
}
|
||||
tracker.dispatch();
|
||||
tracker.stop();
|
||||
}
|
||||
}
|
||||
@@ -21,8 +21,6 @@ import com.google.common.annotations.VisibleForTesting;
|
||||
import android.os.Environment;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Utilities for dealing with files.
|
||||
@@ -38,16 +36,6 @@ public class FileUtils {
|
||||
@VisibleForTesting
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -45,14 +45,17 @@ public class PlayTrackUtils {
|
||||
* KML mime type.
|
||||
*/
|
||||
public static final String KML_MIME_TYPE = "application/vnd.google-earth.kml+xml";
|
||||
|
||||
private static final String GOOGLE_EARTH_PACKAGE = "com.google.earth";
|
||||
public static final String TOUR_FEATURE_ID = "com.google.earth.EXTRA.tour_feature_id";
|
||||
|
||||
public static final String GOOGLE_EARTH_PACKAGE = "com.google.earth";
|
||||
public static final String GOOGLE_EARTH_CLASS = "com.google.earth.EarthActivity";
|
||||
private static final String EARTN_MARKET_URI = "market://details?id=" + GOOGLE_EARTH_PACKAGE;
|
||||
|
||||
private PlayTrackUtils() {}
|
||||
|
||||
/**
|
||||
* Returns true if Google Earth is installed.
|
||||
* Returns true if a Google Earth app that can handle KML mine type is
|
||||
* installed.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
@@ -75,6 +78,8 @@ public class PlayTrackUtils {
|
||||
* @param trackId the track id
|
||||
*/
|
||||
public static void playTrack(Context context, long trackId) {
|
||||
AnalyticsUtils.sendPageViews(context, "/action/play");
|
||||
|
||||
Uri uri = ContentUris.withAppendedId(TracksColumns.CONTENT_URI, trackId);
|
||||
Intent intent = new Intent(context, SaveActivity.class)
|
||||
.putExtra(SaveActivity.EXTRA_FILE_FORMAT, TrackFileFormat.KML.ordinal())
|
||||
|
||||
@@ -18,14 +18,13 @@ package com.google.android.apps.mytracks.util;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParsePosition;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.SimpleTimeZone;
|
||||
import java.util.TimeZone;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -37,19 +36,20 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
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}))?$");
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
DateFormat dateFormatter = android.text.format.DateFormat.getDateFormat(context);
|
||||
return dateFormatter.format(new Date(time)) + " " + formatTime(context, time);
|
||||
static {
|
||||
ISO_8601_DATE_TIME_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
ISO_8601_BASE.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
|
||||
private StringUtils() {}
|
||||
|
||||
/**
|
||||
* Formats the time based on user's phone date/time preferences.
|
||||
*
|
||||
@@ -57,8 +57,28 @@ public class StringUtils {
|
||||
* @param time the time in milliseconds
|
||||
*/
|
||||
public static String formatTime(Context context, long time) {
|
||||
DateFormat timeFormatter = android.text.format.DateFormat.getTimeFormat(context);
|
||||
return timeFormatter.format(new Date(time));
|
||||
return android.text.format.DateFormat.getTimeFormat(context).format(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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,103 +91,117 @@ public class StringUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the elapsed time and total distance.
|
||||
* Formats the elapsed time in the form "H:MM:SS".
|
||||
*
|
||||
* @param context the current context
|
||||
* @param totalDistance the total distance in meters
|
||||
* @param totalTime the total time in milliseconds
|
||||
* @param metric whether to use metric units
|
||||
* @return the formatted string
|
||||
* @param time the time in milliseconds
|
||||
*/
|
||||
public static String formatTimeDistance(Context context, double totalDistance, long totalTime, boolean metric) {
|
||||
String distanceUnit;
|
||||
public static String formatElapsedTimeWithHour(long time) {
|
||||
String value = formatElapsedTime(time);
|
||||
return TextUtils.split(value, ":").length == 2 ? "0:" + value : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the distance.
|
||||
*
|
||||
* @param context the context
|
||||
* @param distance the distance in meters
|
||||
* @param metric true to use metric. False to use imperial
|
||||
*/
|
||||
public static String formatDistance(Context context, double distance, boolean metric) {
|
||||
if (metric) {
|
||||
if (totalDistance > 2000.0) {
|
||||
totalDistance *= UnitConversions.M_TO_KM;
|
||||
distanceUnit = context.getString(R.string.unit_kilometer);
|
||||
if (distance > 2000.0) {
|
||||
distance *= UnitConversions.M_TO_KM;
|
||||
return context.getString(R.string.value_float_kilometer, distance);
|
||||
} else {
|
||||
distanceUnit = context.getString(R.string.unit_meter);
|
||||
return context.getString(R.string.value_float_meter, distance);
|
||||
}
|
||||
} else {
|
||||
if (totalDistance * UnitConversions.M_TO_MI > 2) {
|
||||
totalDistance *= UnitConversions.M_TO_MI;
|
||||
distanceUnit = context.getString(R.string.unit_mile);
|
||||
if (distance * UnitConversions.M_TO_MI > 2) {
|
||||
distance *= UnitConversions.M_TO_MI;
|
||||
return context.getString(R.string.value_float_mile, distance);
|
||||
} else {
|
||||
totalDistance *= UnitConversions.M_TO_FT;
|
||||
distanceUnit = context.getString(R.string.unit_feet);
|
||||
distance *= UnitConversions.M_TO_FT;
|
||||
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
|
||||
* place.
|
||||
*
|
||||
* @param number A double precision number
|
||||
* @return A string representation of a decimal number, derived from the input
|
||||
* double, with a single decimal place
|
||||
* Formats the speed.
|
||||
*
|
||||
* @param context the context
|
||||
* @param speed the speed in meters per second
|
||||
* @param metric true to use metric. False to use imperial
|
||||
* @param reportSpeed true to report as speed. False to report as pace
|
||||
*/
|
||||
public static final String formatSingleDecimalPlace(double number) {
|
||||
return SINGLE_DECIMAL_PLACE_FORMAT.format(number);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() + ")");
|
||||
public static String formatSpeed(
|
||||
Context context, double speed, boolean metric, boolean reportSpeed) {
|
||||
if (Double.isNaN(speed) || Double.isInfinite(speed)) {
|
||||
return context.getString(R.string.value_unknown);
|
||||
}
|
||||
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 =
|
||||
XML_DATE_EXTRAS_PATTERN.matcher(xmlTime.substring(position.getIndex()));
|
||||
/**
|
||||
* Formats the given text as a XML CDATA element. This includes adding the
|
||||
* 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()) {
|
||||
// This will match even an empty string as all groups are optional,
|
||||
// so a non-match means some other garbage was there
|
||||
throw new IllegalArgumentException("Invalid XML dateTime value: " + xmlTime);
|
||||
// This will match even an empty string as all groups are optional. Thus a
|
||||
// non-match means invalid content.
|
||||
throw new IllegalArgumentException("Invalid XML dateTime value: " + xmlDateTime);
|
||||
}
|
||||
|
||||
long time = date.getTime();
|
||||
@@ -175,7 +209,7 @@ public class StringUtils {
|
||||
// Account for fractional seconds
|
||||
String fractional = matcher.group(1);
|
||||
if (fractional != null) {
|
||||
// Regex ensures fractional part is in (0,1(
|
||||
// Regex ensures fractional part is in (0,1)
|
||||
float fractionalSeconds = Float.parseFloat(fractional);
|
||||
long fractionalMillis = (long) (fractionalSeconds * 1000.0f);
|
||||
time += fractionalMillis;
|
||||
@@ -193,24 +227,28 @@ public class StringUtils {
|
||||
|
||||
// Regex ensures values are >= 0
|
||||
if (offsetHours > 14 || offsetMins > 59) {
|
||||
throw new IllegalArgumentException("Bad timezone in " + xmlTime);
|
||||
throw new IllegalArgumentException("Bad timezone: " + xmlDateTime);
|
||||
}
|
||||
|
||||
long totalOffsetMillis = (offsetMins + offsetHours * 60L) * 60000L;
|
||||
|
||||
// Make time go back to UTC
|
||||
// Convert to UTC
|
||||
if (plusSign) {
|
||||
time -= totalOffsetMillis;
|
||||
} else {
|
||||
time += totalOffsetMillis;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (time < 0) {
|
||||
@@ -224,10 +262,9 @@ public class StringUtils {
|
||||
|
||||
long seconds = time / 1000;
|
||||
parts[0] = (int) (seconds % 60);
|
||||
int tmp = (int) (seconds / 60);
|
||||
parts[1] = tmp % 60;
|
||||
parts[2] = tmp / 60;
|
||||
|
||||
int minutes = (int) (seconds / 60);
|
||||
parts[1] = minutes % 60;
|
||||
parts[2] = minutes / 60;
|
||||
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.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
@@ -68,9 +67,6 @@ public class TrackWidgetProvider
|
||||
private MyTracksProviderUtils providerUtils;
|
||||
private Context context;
|
||||
private String unknown;
|
||||
private String distanceLabel;
|
||||
private String speedLabel;
|
||||
private String paceLabel;
|
||||
private TrackObserver trackObserver;
|
||||
private boolean isMetric;
|
||||
private boolean reportSpeed;
|
||||
@@ -213,32 +209,10 @@ public class TrackWidgetProvider
|
||||
}
|
||||
|
||||
TripStatistics stats = track.getStatistics();
|
||||
|
||||
// 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 distance = StringUtils.formatDistance(context, stats.getTotalDistance(), isMetric);
|
||||
String time = StringUtils.formatElapsedTime(stats.getMovingTime());
|
||||
String speed = unknown;
|
||||
if (!Double.isNaN(stats.getAverageMovingSpeed())) {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
String speed = StringUtils.formatSpeed(
|
||||
context, stats.getAverageMovingSpeed(), isMetric, reportSpeed);
|
||||
|
||||
views.setTextViewText(R.id.appwidget_distance_text, distance);
|
||||
views.setTextViewText(R.id.appwidget_time_text, time);
|
||||
@@ -250,11 +224,6 @@ public class TrackWidgetProvider
|
||||
String metricUnitsKey = context.getString(R.string.metric_units_key);
|
||||
if (key == null || key.equals(metricUnitsKey)) {
|
||||
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);
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
</classpathentry>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/MyTracks"/>
|
||||
<classpathentry kind="lib" path="libs/AndroidMockGenerator.jar"/>
|
||||
<classpathentry kind="lib" path="libs/robotium-solo-2.1.jar" />
|
||||
<classpathentry kind="lib" path="libs/robotium-solo-3.1.jar"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -43,13 +43,6 @@ public class ChartActivityTest extends ActivityInstrumentationTestCase2<ChartAct
|
||||
private View zoomOut;
|
||||
private int currentZoomLevel;
|
||||
|
||||
private final String LOCATION_PROVIDER = "gps";
|
||||
private final double INITIAL_LONGTITUDE = 22;
|
||||
private final double INITIAL_LATITUDE = 22;
|
||||
private final double INITIAL_ALTITUDE = 22;
|
||||
private final float INITIAL_ACCURACY = 5;
|
||||
private final float INITIAL_SPEED = 10;
|
||||
private final float INITIAL_BEARING = 3.0f;
|
||||
// 10 is same with the default value in ChartView
|
||||
private final int MAX_ZOOM_LEVEL = 10;
|
||||
private final int MIN_ZOOM_LEVEL = 1;
|
||||
@@ -113,7 +106,7 @@ public class ChartActivityTest extends ActivityInstrumentationTestCase2<ChartAct
|
||||
|
||||
/**
|
||||
* There are two parts in this test. Tests
|
||||
* {@link ChartActivity#OnCreateDialog()} which includes the logic to create
|
||||
* {@link ChartActivity#onCreateDialog(int)} which includes the logic to create
|
||||
* {@link ChartSettingsDialog}.
|
||||
*/
|
||||
public void testCreateSettingDialog() {
|
||||
@@ -134,10 +127,10 @@ public class ChartActivityTest extends ActivityInstrumentationTestCase2<ChartAct
|
||||
|
||||
/**
|
||||
* Tests the logic to get the incorrect values of sensor in {@link
|
||||
* ChartActivity#fillDataPoint(Location location, double result[])}.
|
||||
* ChartActivity#fillDataPoint(Location, double[])}.
|
||||
*/
|
||||
public void testFillDataPoint_sensorIncorrect() {
|
||||
MyTracksLocation myTracksLocation = getMyTracksLocation();
|
||||
MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation();
|
||||
// No input.
|
||||
double[] point = fillDataPointTestHelper(myTracksLocation, false);
|
||||
assertEquals(Double.NaN, point[3]);
|
||||
@@ -166,10 +159,10 @@ public class ChartActivityTest extends ActivityInstrumentationTestCase2<ChartAct
|
||||
|
||||
/**
|
||||
* Tests the logic to get the correct values of sensor in {@link
|
||||
* ChartActivity#fillDataPoint(Location location, double result[])}.
|
||||
* ChartActivity#fillDataPoint(Location, double[])}.
|
||||
*/
|
||||
public void testFillDataPoint_sensorCorrect() {
|
||||
MyTracksLocation myTracksLocation = getMyTracksLocation();
|
||||
MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation();
|
||||
// No input.
|
||||
double[] point = fillDataPointTestHelper(myTracksLocation, false);
|
||||
assertEquals(Double.NaN, point[3]);
|
||||
@@ -197,23 +190,23 @@ public class ChartActivityTest extends ActivityInstrumentationTestCase2<ChartAct
|
||||
|
||||
/**
|
||||
* Tests the logic to get the value of metric Distance in
|
||||
* {@link #fillDataPoint}.
|
||||
* {@link ChartActivity#fillDataPoint(Location, double[])}.
|
||||
*/
|
||||
public void testFillDataPoint_distanceMetric() {
|
||||
// By distance.
|
||||
chartActivity.getChartView().setMode(ChartView.Mode.BY_DISTANCE);
|
||||
// Resets last location and writes first location.
|
||||
MyTracksLocation myTracksLocation1 = getMyTracksLocation();
|
||||
MyTracksLocation myTracksLocation1 = TrackStubUtils.createMyTracksLocation();
|
||||
double[] point = fillDataPointTestHelper(myTracksLocation1, true);
|
||||
assertEquals(0.0, point[0]);
|
||||
|
||||
// The second is a same location, just different time.
|
||||
MyTracksLocation myTracksLocation2 = getMyTracksLocation();
|
||||
MyTracksLocation myTracksLocation2 = TrackStubUtils.createMyTracksLocation();
|
||||
point = fillDataPointTestHelper(myTracksLocation2, false);
|
||||
assertEquals(0.0, point[0]);
|
||||
|
||||
// The third location is a new location, and use metric.
|
||||
MyTracksLocation myTracksLocation3 = getMyTracksLocation();
|
||||
MyTracksLocation myTracksLocation3 = TrackStubUtils.createMyTracksLocation();
|
||||
myTracksLocation3.setLatitude(23);
|
||||
point = fillDataPointTestHelper(myTracksLocation3, false);
|
||||
// Computes the distance between Latitude 22 and 23.
|
||||
@@ -224,7 +217,7 @@ public class ChartActivityTest extends ActivityInstrumentationTestCase2<ChartAct
|
||||
assertEquals(distance1 / KILOMETER_TO_METER, point[0]);
|
||||
|
||||
// The fourth location is a new location, and use metric.
|
||||
MyTracksLocation myTracksLocation4 = getMyTracksLocation();
|
||||
MyTracksLocation myTracksLocation4 = TrackStubUtils.createMyTracksLocation();
|
||||
myTracksLocation4.setLatitude(24);
|
||||
point = fillDataPointTestHelper(myTracksLocation4, false);
|
||||
// Computes the distance between Latitude 23 and 24.
|
||||
@@ -237,19 +230,19 @@ public class ChartActivityTest extends ActivityInstrumentationTestCase2<ChartAct
|
||||
|
||||
/**
|
||||
* Tests the logic to get the value of imperial Distance in
|
||||
* {@link #fillDataPoint}.
|
||||
* {@link ChartActivity#fillDataPoint(Location, double[])}.
|
||||
*/
|
||||
public void testFillDataPoint_distanceImperial() {
|
||||
// Setups to use imperial.
|
||||
chartActivity.onUnitsChanged(false);
|
||||
|
||||
// The first is a same location, just different time.
|
||||
MyTracksLocation myTracksLocation1 = getMyTracksLocation();
|
||||
MyTracksLocation myTracksLocation1 = TrackStubUtils.createMyTracksLocation();
|
||||
double[] point = fillDataPointTestHelper(myTracksLocation1, true);
|
||||
assertEquals(0.0, point[0]);
|
||||
|
||||
// The second location is a new location, and use imperial.
|
||||
MyTracksLocation myTracksLocation2 = getMyTracksLocation();
|
||||
MyTracksLocation myTracksLocation2 = TrackStubUtils.createMyTracksLocation();
|
||||
myTracksLocation2.setLatitude(23);
|
||||
point = fillDataPointTestHelper(myTracksLocation2, false);
|
||||
/*
|
||||
@@ -263,7 +256,7 @@ public class ChartActivityTest extends ActivityInstrumentationTestCase2<ChartAct
|
||||
assertEquals(distance1 / KILOMETER_TO_METER, point[0]);
|
||||
|
||||
// The third location is a new location, and use imperial.
|
||||
MyTracksLocation myTracksLocation3 = getMyTracksLocation();
|
||||
MyTracksLocation myTracksLocation3 = TrackStubUtils.createMyTracksLocation();
|
||||
myTracksLocation3.setLatitude(24);
|
||||
point = fillDataPointTestHelper(myTracksLocation3, false);
|
||||
/*
|
||||
@@ -274,20 +267,20 @@ public class ChartActivityTest extends ActivityInstrumentationTestCase2<ChartAct
|
||||
Location.distanceBetween(myTracksLocation2.getLatitude(), myTracksLocation2.getLongitude(),
|
||||
myTracksLocation3.getLatitude(), myTracksLocation3.getLongitude(), results);
|
||||
double distance2 = results[0] * UnitConversions.KM_TO_MI;
|
||||
assertEquals((distance1 + distance2) / KILOMETER_TO_METER, point[0]);
|
||||
assertEquals(distance1 / KILOMETER_TO_METER + distance2 / KILOMETER_TO_METER , point[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the logic to get the values of time in {@link #fillDataPoint}.
|
||||
* Tests the logic to get the values of time in {@link ChartActivity#fillDataPoint(Location, double[])}.
|
||||
*/
|
||||
public void testFillDataPoint_time() {
|
||||
// By time
|
||||
chartActivity.getChartView().setMode(ChartView.Mode.BY_TIME);
|
||||
MyTracksLocation myTracksLocation1 = getMyTracksLocation();
|
||||
MyTracksLocation myTracksLocation1 = TrackStubUtils.createMyTracksLocation();
|
||||
double[] point = fillDataPointTestHelper(myTracksLocation1, true);
|
||||
assertEquals(0.0, point[0]);
|
||||
long timeSpan = 222;
|
||||
MyTracksLocation myTracksLocation2 = getMyTracksLocation();
|
||||
MyTracksLocation myTracksLocation2 = TrackStubUtils.createMyTracksLocation();
|
||||
myTracksLocation2.setTime(myTracksLocation1.getTime() + timeSpan);
|
||||
point = fillDataPointTestHelper(myTracksLocation2, false);
|
||||
assertEquals((double) timeSpan, point[0]);
|
||||
@@ -295,29 +288,29 @@ public class ChartActivityTest extends ActivityInstrumentationTestCase2<ChartAct
|
||||
|
||||
/**
|
||||
* Tests the logic to get the value of elevation in
|
||||
* {@link ChartActivity#fillDataPoint} by one and two points.
|
||||
* {@link ChartActivity#fillDataPoint(Location, double[])} by one and two points.
|
||||
*/
|
||||
public void testFillDataPoint_elevation() {
|
||||
MyTracksLocation myTracksLocation1 = getMyTracksLocation();
|
||||
MyTracksLocation myTracksLocation1 = TrackStubUtils.createMyTracksLocation();
|
||||
/*
|
||||
* At first, clear old points of elevation, so give true to the second
|
||||
* parameter. Then only one value INITIALLONGTITUDE in buffer.
|
||||
*/
|
||||
double[] point = fillDataPointTestHelper(myTracksLocation1, true);
|
||||
assertEquals(INITIAL_ALTITUDE, point[1]);
|
||||
assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point[1]);
|
||||
/*
|
||||
* Send another value to buffer, now there are two values, INITIALALTITUDE
|
||||
* and INITIALALTITUDE * 2.
|
||||
*/
|
||||
MyTracksLocation myTracksLocation2 = getMyTracksLocation();
|
||||
myTracksLocation2.setAltitude(INITIAL_ALTITUDE * 2);
|
||||
MyTracksLocation myTracksLocation2 = TrackStubUtils.createMyTracksLocation();
|
||||
myTracksLocation2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
|
||||
point = fillDataPointTestHelper(myTracksLocation2, false);
|
||||
assertEquals((INITIAL_ALTITUDE + INITIAL_ALTITUDE * 2) / 2.0, point[1]);
|
||||
assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, point[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the logic to get the value of speed in
|
||||
* {@link ChartActivity#fillDataPoint}. In this test, firstly remove all
|
||||
* {@link ChartActivity#fillDataPoint(Location, double[])}. In this test, firstly remove all
|
||||
* points in memory, and then fill in two points one by one. The speed values
|
||||
* of these points are 129, 130.
|
||||
*/
|
||||
@@ -328,7 +321,7 @@ public class ChartActivityTest extends ActivityInstrumentationTestCase2<ChartAct
|
||||
* At first, clear old points of speed, so give true to the second
|
||||
* parameter. It will not be filled in to the speed buffer.
|
||||
*/
|
||||
MyTracksLocation myTracksLocation1 = getMyTracksLocation();
|
||||
MyTracksLocation myTracksLocation1 = TrackStubUtils.createMyTracksLocation();
|
||||
myTracksLocation1.setSpeed(129);
|
||||
double[] point = fillDataPointTestHelper(myTracksLocation1, true);
|
||||
assertEquals(0.0, point[2]);
|
||||
@@ -337,7 +330,7 @@ public class ChartActivityTest extends ActivityInstrumentationTestCase2<ChartAct
|
||||
* Tests the logic when both metricUnits and reportSpeed are true.This
|
||||
* location will be filled into speed buffer.
|
||||
*/
|
||||
MyTracksLocation myTracksLocation2 = getMyTracksLocation();
|
||||
MyTracksLocation myTracksLocation2 = TrackStubUtils.createMyTracksLocation();
|
||||
// Add a time span here to make sure the second point is valid, the value
|
||||
// 222 here is doesn't matter.
|
||||
myTracksLocation2.setTime(myTracksLocation1.getTime() + 222);
|
||||
@@ -352,7 +345,7 @@ public class ChartActivityTest extends ActivityInstrumentationTestCase2<ChartAct
|
||||
public void testFillDataPoint_speedImperial() {
|
||||
// Setups to use imperial.
|
||||
chartActivity.onUnitsChanged(false);
|
||||
MyTracksLocation myTracksLocation = getMyTracksLocation();
|
||||
MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation();
|
||||
myTracksLocation.setSpeed(132);
|
||||
double[] point = fillDataPointTestHelper(myTracksLocation, true);
|
||||
assertEquals(132.0 * METER_PER_SECOND_TO_KILOMETER_PER_HOUR * UnitConversions.KM_TO_MI,
|
||||
@@ -365,7 +358,7 @@ public class ChartActivityTest extends ActivityInstrumentationTestCase2<ChartAct
|
||||
public void testFillDataPoint_pace_nonZeroSpeed() {
|
||||
// Setups reportSpeed to false.
|
||||
chartActivity.onReportSpeedChanged(false);
|
||||
MyTracksLocation myTracksLocation = getMyTracksLocation();
|
||||
MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation();
|
||||
myTracksLocation.setSpeed(134);
|
||||
double[] point = fillDataPointTestHelper(myTracksLocation, true);
|
||||
assertEquals(HOURS_PER_UNIT / (134.0 * METER_PER_SECOND_TO_KILOMETER_PER_HOUR), point[2]);
|
||||
@@ -379,33 +372,12 @@ public class ChartActivityTest extends ActivityInstrumentationTestCase2<ChartAct
|
||||
public void testFillDataPoint_pace_zeroSpeed() {
|
||||
// Setups reportSpeed to false.
|
||||
chartActivity.onReportSpeedChanged(false);
|
||||
MyTracksLocation myTracksLocation = getMyTracksLocation();
|
||||
MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation();
|
||||
myTracksLocation.setSpeed(0);
|
||||
double[] point = fillDataPointTestHelper(myTracksLocation, true);
|
||||
assertEquals(Double.NaN, point[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates a MyTracksLocation for test.
|
||||
*
|
||||
* @return a simulated location.
|
||||
*/
|
||||
private MyTracksLocation getMyTracksLocation() {
|
||||
// Initial Location
|
||||
Location loc = new Location(LOCATION_PROVIDER);
|
||||
loc.setLongitude(INITIAL_LONGTITUDE);
|
||||
loc.setLatitude(INITIAL_LATITUDE);
|
||||
loc.setAltitude(INITIAL_ALTITUDE);
|
||||
loc.setAccuracy(INITIAL_ACCURACY);
|
||||
loc.setSpeed(INITIAL_SPEED);
|
||||
loc.setTime(System.currentTimeMillis());
|
||||
loc.setBearing(INITIAL_BEARING);
|
||||
SensorDataSet sd = SensorDataSet.newBuilder().build();
|
||||
MyTracksLocation myTracksLocation = new MyTracksLocation(loc, sd);
|
||||
|
||||
return myTracksLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to test fillDataPoint.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
/**
|
||||
* Commons utilities for creating stubs of track, location. The class will be
|
||||
* enriched if needs more similar stubs for test.
|
||||
*
|
||||
* @author Youtao Liu
|
||||
*/
|
||||
public class TrackStubUtils {
|
||||
|
||||
static final String LOCATION_PROVIDER = "gps";
|
||||
static final double INITIAL_LATITUDE = 22;
|
||||
static final double INITIAL_LONGITUDE = 22;
|
||||
static final double INITIAL_ALTITUDE = 22;
|
||||
static final float INITIAL_ACCURACY = 5;
|
||||
static final float INITIAL_SPEED = 10;
|
||||
static final float INITIAL_BEARING = 3.0f;
|
||||
// Used to change the value of latitude, longitude, and altitude.
|
||||
static final double DIFFERENCE = 0.01;
|
||||
|
||||
/**
|
||||
* Gets a a {@link Track} stub with specified number of locations.
|
||||
*
|
||||
* @param numberOfLocations the number of locations for the track
|
||||
* @return a track stub.
|
||||
*/
|
||||
public static Track createTrack(int numberOfLocations) {
|
||||
Track track = new Track();
|
||||
for (int i = 0; i < numberOfLocations; i++) {
|
||||
track.addLocation(createMyTracksLocation(INITIAL_LATITUDE + i * DIFFERENCE, INITIAL_LONGITUDE
|
||||
+ i * DIFFERENCE, INITIAL_ALTITUDE + i * DIFFERENCE));
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MyTracks location with default values.
|
||||
*
|
||||
* @return a track stub.
|
||||
*/
|
||||
public static MyTracksLocation createMyTracksLocation() {
|
||||
return createMyTracksLocation(INITIAL_LATITUDE, INITIAL_LONGITUDE, INITIAL_ALTITUDE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link MyTracksLocation} stub with specified values.
|
||||
*
|
||||
* @return a MyTracksLocation stub.
|
||||
*/
|
||||
public static MyTracksLocation createMyTracksLocation(double latitude, double longitude,
|
||||
double altitude) {
|
||||
// Initial Location
|
||||
Location loc = new Location(LOCATION_PROVIDER);
|
||||
loc.setLatitude(latitude);
|
||||
loc.setLongitude(longitude);
|
||||
loc.setAltitude(altitude);
|
||||
loc.setAccuracy(INITIAL_ACCURACY);
|
||||
loc.setSpeed(INITIAL_SPEED);
|
||||
loc.setTime(System.currentTimeMillis());
|
||||
loc.setBearing(INITIAL_BEARING);
|
||||
SensorDataSet sd = SensorDataSet.newBuilder().build();
|
||||
MyTracksLocation myTracksLocation = new MyTracksLocation(loc, sd);
|
||||
|
||||
return myTracksLocation;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,81 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io.file;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for the CSV track exporter.
|
||||
* Tests for {@link CsvTrackWriter}.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class CsvTrackWriterTest extends TrackFormatWriterTest {
|
||||
|
||||
private static final String BEGIN_TAG = "\"";
|
||||
private static final String END_TAG = "\"\n";
|
||||
private static final String SEPARATOR = "\",\"";
|
||||
|
||||
public void testCsvOutput() throws Exception {
|
||||
String expectedTrackHeader = getExpectedLine(
|
||||
"Track name", "Activity type", "Track description");
|
||||
String expectedTrack = getExpectedLine(TRACK_NAME, TRACK_CATEGORY, TRACK_DESCRIPTION);
|
||||
String expectedMarkerHeader = getExpectedLine("Marker name", "Marker type",
|
||||
"Marker description", "Latitude (deg)", "Longitude (deg)", "Altitude (m)", "Bearing (deg)",
|
||||
"Accuracy (m)", "Speed (m/s)", "Time");
|
||||
String expectedMarker1 = getExpectedLine(WAYPOINT1_NAME, WAYPOINT1_CATEGORY,
|
||||
WAYPOINT1_DESCRIPTION, "1.0", "-1.0", "10.0", "100.0", "1,000", "10,000",
|
||||
"1970-01-01T00:01:40Z");
|
||||
String expectedMarker2 = getExpectedLine(WAYPOINT2_NAME, WAYPOINT2_CATEGORY,
|
||||
WAYPOINT2_DESCRIPTION, "2.0", "-2.0", "20.0", "200.0", "2,000", "20,000",
|
||||
"1970-01-01T00:03:20Z");
|
||||
String expectedPointHeader = getExpectedLine("Segment", "Point", "Latitude (deg)",
|
||||
"Longitude (deg)", "Altitude (m)", "Bearing (deg)", "Accuracy (m)", "Speed (m/s)", "Time",
|
||||
"Power (W)", "Cadence (rpm)", "Heart rate (bpm)", "Battery level (%)");
|
||||
String expectedPoint1 = getExpectedLine("1", "1", "0.0", "0.0", "0.0", "0.0", "0", "0",
|
||||
"1970-01-01T00:00:00Z", "100.0", "200.0", "300.0", "400.0");
|
||||
String expectedPoint2 = getExpectedLine("1", "2", "1.0", "-1.0", "10.0", "100.0", "1,000",
|
||||
"10,000", "1970-01-01T00:01:40Z", "101.0", "201.0", "301.0", "401.0");
|
||||
String expectedPoint3 = getExpectedLine("2", "1", "2.0", "-2.0", "20.0", "200.0", "2,000",
|
||||
"20,000", "1970-01-01T00:03:20Z", "102.0", "202.0", "302.0", "402.0");
|
||||
String expectedPoint4 = getExpectedLine("2", "2", "3.0", "-3.0", "30.0", "300.0", "3,000",
|
||||
"30,000", "1970-01-01T00:05:00Z", "103.0", "203.0", "303.0", "403.0");
|
||||
String expected = expectedTrackHeader + expectedTrack + "\n"
|
||||
+ expectedMarkerHeader + expectedMarker1 + expectedMarker2 + "\n"
|
||||
+ expectedPointHeader + expectedPoint1 + expectedPoint2 + expectedPoint3 + expectedPoint4;
|
||||
|
||||
CsvTrackWriter writer = new CsvTrackWriter(getContext());
|
||||
assertEquals(expected, writeTrack(writer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expected CSV line from a list of expected values.
|
||||
*
|
||||
* @param values expected values
|
||||
*/
|
||||
private String getExpectedLine(String... values) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(BEGIN_TAG);
|
||||
boolean first = true;
|
||||
for (String value : values) {
|
||||
if (!first) {
|
||||
builder.append(SEPARATOR);
|
||||
}
|
||||
first = false;
|
||||
builder.append(value);
|
||||
}
|
||||
builder.append(END_TAG);
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,23 +38,23 @@ public class GpxTrackWriterTest extends TrackFormatWriterTest {
|
||||
assertEquals(TRACK_DESCRIPTION, getChildTextValue(trackTag, "desc"));
|
||||
List<Element> segmentTags = getChildElements(trackTag, "trkseg", 2);
|
||||
List<Element> segPointTags = getChildElements(segmentTags.get(0), "trkpt", 2);
|
||||
assertTagMatchesLocation(segPointTags.get(0), "0", "0", "1970-01-01T02:46:40Z", "0");
|
||||
assertTagMatchesLocation(segPointTags.get(1), "1", "-1", "1970-01-01T02:46:41Z", "5000000");
|
||||
assertTagMatchesLocation(segPointTags.get(0), "0", "0", "1970-01-01T00:00:00Z", "0");
|
||||
assertTagMatchesLocation(segPointTags.get(1), "1", "-1", "1970-01-01T00:01:40Z", "10");
|
||||
|
||||
segPointTags = getChildElements(segmentTags.get(1), "trkpt", 2);
|
||||
assertTagMatchesLocation(segPointTags.get(0), "2", "-2", "1970-01-01T02:46:42Z", "10000000");
|
||||
assertTagMatchesLocation(segPointTags.get(1), "3", "-3", "1970-01-01T02:46:43Z", "15000000");
|
||||
assertTagMatchesLocation(segPointTags.get(0), "2", "-2", "1970-01-01T00:03:20Z", "20");
|
||||
assertTagMatchesLocation(segPointTags.get(1), "3", "-3", "1970-01-01T00:05:00Z", "30");
|
||||
|
||||
List<Element> waypointTags = getChildElements(gpxTag, "wpt", 2);
|
||||
Element wptTag = waypointTags.get(0);
|
||||
assertEquals(WAYPOINT1_NAME, getChildTextValue(wptTag, "name"));
|
||||
assertEquals(WAYPOINT1_DESCRIPTION, getChildTextValue(wptTag, "desc"));
|
||||
assertTagMatchesLocation(wptTag, "1", "-1", "1970-01-01T02:46:41Z", "5000000");
|
||||
assertTagMatchesLocation(wptTag, "1", "-1", "1970-01-01T00:01:40Z", "10");
|
||||
|
||||
wptTag = waypointTags.get(1);
|
||||
assertEquals(WAYPOINT2_NAME, getChildTextValue(wptTag, "name"));
|
||||
assertEquals(WAYPOINT2_DESCRIPTION, getChildTextValue(wptTag, "desc"));
|
||||
assertTagMatchesLocation(wptTag, "2", "-2", "1970-01-01T02:46:42Z", "10000000");
|
||||
assertTagMatchesLocation(wptTag, "2", "-2", "1970-01-01T00:03:20Z", "20");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -63,14 +63,19 @@ public class KmlTrackWriterTest extends TrackFormatWriterTest {
|
||||
assertEquals(TRACK_NAME, getChildTextValue(docTag, "name"));
|
||||
assertEquals(TRACK_DESCRIPTION, getChildTextValue(docTag, "description"));
|
||||
|
||||
// There are 5 placemarks - start, segments, end, waypoint1, waypoint2
|
||||
List<Element> placemarkTags = getChildElements(docTag, "Placemark", 5);
|
||||
// There are 3 placemarks - start, track, and end
|
||||
List<Element> placemarkTags = getChildElements(docTag, "Placemark", 3);
|
||||
assertTagIsPlacemark(
|
||||
placemarkTags.get(0), TRACK_NAME + " (Start)", TRACK_DESCRIPTION, location1);
|
||||
assertTagIsPlacemark(
|
||||
placemarkTags.get(2), TRACK_NAME + " (End)", FULL_TRACK_DESCRIPTION, location4);
|
||||
assertTagIsPlacemark(placemarkTags.get(3), WAYPOINT1_NAME, WAYPOINT1_DESCRIPTION, location2);
|
||||
assertTagIsPlacemark(placemarkTags.get(4), WAYPOINT2_NAME, WAYPOINT2_DESCRIPTION, location3);
|
||||
|
||||
List<Element> folderTag = getChildElements(docTag, "Folder", 1);
|
||||
List<Element> folderPlacemarkTags = getChildElements(folderTag.get(0), "Placemark", 2);
|
||||
assertTagIsPlacemark(
|
||||
folderPlacemarkTags.get(0), WAYPOINT1_NAME, WAYPOINT1_DESCRIPTION, location2);
|
||||
assertTagIsPlacemark(
|
||||
folderPlacemarkTags.get(1), WAYPOINT2_NAME, WAYPOINT2_DESCRIPTION, location3);
|
||||
|
||||
Element trackPlacemarkTag = placemarkTags.get(1);
|
||||
assertEquals(TRACK_NAME, getChildTextValue(trackPlacemarkTag, "name"));
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io.file;
|
||||
|
||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||
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;
|
||||
|
||||
@@ -11,7 +25,7 @@ import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Tests for the GPX track exporter.
|
||||
* Tests for {@link TcxTrackWriter}.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
@@ -37,42 +51,46 @@ public class TcxTrackWriterTest extends TrackFormatWriterTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given tags describe the given points, in the same order.
|
||||
* Asserts that the given tags describe the given locations in the same order.
|
||||
*
|
||||
* @param tags list of tags
|
||||
* @param locations list of locations
|
||||
*/
|
||||
private void assertTagsMatchPoints(List<Element> tags, MyTracksLocation... locs) {
|
||||
assertEquals(locs.length, tags.size());
|
||||
for (int i = 0; i < locs.length; i++) {
|
||||
Element tag = tags.get(i);
|
||||
MyTracksLocation loc = locs[i];
|
||||
|
||||
assertTagMatchesLocation(tag, loc);
|
||||
private void assertTagsMatchPoints(List<Element> tags, MyTracksLocation... locations) {
|
||||
assertEquals(locations.length, tags.size());
|
||||
for (int i = 0; i < locations.length; i++) {
|
||||
assertTagMatchesLocation(tags.get(i), locations[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given tag describes the given location.
|
||||
*
|
||||
* @param tag the tag
|
||||
* @param location the location
|
||||
*/
|
||||
private void assertTagMatchesLocation(Element tag, MyTracksLocation loc) {
|
||||
Element posTag = getChildElement(tag, "Position");
|
||||
assertEquals(Double.toString(loc.getLatitude()),
|
||||
getChildTextValue(posTag, "LatitudeDegrees"));
|
||||
assertEquals(Double.toString(loc.getLongitude()),
|
||||
getChildTextValue(posTag, "LongitudeDegrees"));
|
||||
private void assertTagMatchesLocation(Element tag, MyTracksLocation location) {
|
||||
assertEquals(StringUtils.formatDateTimeIso8601(location.getTime()), getChildTextValue(tag, "Time"));
|
||||
|
||||
assertEquals(FileUtils.FILE_TIMESTAMP_FORMAT.format(loc.getTime()),
|
||||
getChildTextValue(tag, "Time"));
|
||||
assertEquals(Double.toString(loc.getAltitude()),
|
||||
getChildTextValue(tag, "AltitudeMeters"));
|
||||
assertTrue(loc.getSensorDataSet() != null);
|
||||
Sensor.SensorDataSet sds = loc.getSensorDataSet();
|
||||
Element positionTag = getChildElement(tag, "Position");
|
||||
assertEquals(
|
||||
Double.toString(location.getLatitude()), getChildTextValue(positionTag, "LatitudeDegrees"));
|
||||
assertEquals(Double.toString(location.getLongitude()),
|
||||
getChildTextValue(positionTag, "LongitudeDegrees"));
|
||||
|
||||
List<Element> bpm = getChildElements(tag, "HeartRateBpm", 1);
|
||||
assertEquals(Double.toString(location.getAltitude()), getChildTextValue(tag, "AltitudeMeters"));
|
||||
assertTrue(location.getSensorDataSet() != null);
|
||||
Sensor.SensorDataSet sds = location.getSensorDataSet();
|
||||
|
||||
List<Element> heartRate = getChildElements(tag, "HeartRateBpm", 1);
|
||||
assertEquals(Integer.toString(sds.getHeartRate().getValue()),
|
||||
getChildTextValue(bpm.get(0), "Value"));
|
||||
getChildTextValue(heartRate.get(0), "Value"));
|
||||
|
||||
List<Element> ext = getChildElements(tag, "Extensions", 1);
|
||||
List<Element> tpx = getChildElements(ext.get(0), "TPX", 1);
|
||||
assertEquals(Integer.toString(sds.getPower().getValue()),
|
||||
getChildTextValue(tpx.get(0), "Watts"));
|
||||
List<Element> extensions = getChildElements(tag, "Extensions", 1);
|
||||
List<Element> tpx = getChildElements(extensions.get(0), "TPX", 1);
|
||||
assertEquals(
|
||||
Integer.toString(sds.getCadence().getValue()), getChildTextValue(tpx.get(0), "RunCadence"));
|
||||
assertEquals(
|
||||
Integer.toString(sds.getPower().getValue()), getChildTextValue(tpx.get(0), "Watts"));
|
||||
}
|
||||
}
|
||||
|
||||
+34
-21
@@ -39,10 +39,13 @@ public abstract class TrackFormatWriterTest extends AndroidTestCase {
|
||||
// All the user-provided strings have "]]>" to ensure that proper escaping is
|
||||
// being done.
|
||||
protected static final String TRACK_NAME = "Home]]>";
|
||||
protected static final String TRACK_CATEGORY = "Hiking";
|
||||
protected static final String TRACK_DESCRIPTION = "The long ]]> journey home";
|
||||
protected static final String WAYPOINT1_NAME = "point]]>1";
|
||||
protected static final String WAYPOINT1_CATEGORY = "Statistics";
|
||||
protected static final String WAYPOINT1_DESCRIPTION = "point 1]]>description";
|
||||
protected static final String WAYPOINT2_NAME = "point]]>2";
|
||||
protected static final String WAYPOINT2_CATEGORY = "Waypoint";
|
||||
protected static final String WAYPOINT2_DESCRIPTION = "point 2]]>description";
|
||||
private static final int BUFFER_SIZE = 10240;
|
||||
protected Track track;
|
||||
@@ -55,6 +58,7 @@ public abstract class TrackFormatWriterTest extends AndroidTestCase {
|
||||
|
||||
track = new Track();
|
||||
track.setName(TRACK_NAME);
|
||||
track.setCategory(TRACK_CATEGORY);
|
||||
track.setDescription(TRACK_DESCRIPTION);
|
||||
|
||||
location1 = new MyTracksLocation("mock");
|
||||
@@ -67,33 +71,40 @@ public abstract class TrackFormatWriterTest extends AndroidTestCase {
|
||||
wp2 = new Waypoint();
|
||||
wp1.setLocation(location2);
|
||||
wp1.setName(WAYPOINT1_NAME);
|
||||
wp1.setCategory(WAYPOINT1_CATEGORY);
|
||||
wp1.setDescription(WAYPOINT1_DESCRIPTION);
|
||||
wp2.setLocation(location3);
|
||||
wp2.setName(WAYPOINT2_NAME);
|
||||
wp2.setCategory(WAYPOINT2_CATEGORY);
|
||||
wp2.setDescription(WAYPOINT2_DESCRIPTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the given locations with coordinates and time.
|
||||
* Populates a list of locations with values.
|
||||
*
|
||||
* @param locations a list of locations
|
||||
*/
|
||||
protected void populateLocations(MyTracksLocation... locs) {
|
||||
for (int i = 0; i < locs.length; i++) {
|
||||
MyTracksLocation loc = locs[i];
|
||||
loc.setAltitude(i * 5000000);
|
||||
loc.setLatitude(i);
|
||||
loc.setLongitude(-i);
|
||||
loc.setTime(10000000 + i * 1000);
|
||||
Sensor.SensorData.Builder hr = Sensor.SensorData.newBuilder()
|
||||
.setValue(100 + i)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
Sensor.SensorData.Builder power = Sensor.SensorData.newBuilder()
|
||||
.setValue(400 + i)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
Sensor.SensorDataSet sds =
|
||||
Sensor.SensorDataSet.newBuilder().setHeartRate(hr.build())
|
||||
.setPower(power)
|
||||
.build();
|
||||
loc.setSensorData(sds);
|
||||
private void populateLocations(MyTracksLocation... locations) {
|
||||
for (int i = 0; i < locations.length; i++) {
|
||||
MyTracksLocation location = locations[i];
|
||||
location.setLatitude(i);
|
||||
location.setLongitude(-i);
|
||||
location.setAltitude(i * 10);
|
||||
location.setBearing(i * 100);
|
||||
location.setAccuracy(i * 1000);
|
||||
location.setSpeed(i * 10000);
|
||||
location.setTime(i * 100000);
|
||||
Sensor.SensorData.Builder power = Sensor.SensorData.newBuilder().setValue(100 + i)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
Sensor.SensorData.Builder cadence = Sensor.SensorData.newBuilder().setValue(200 + i)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
Sensor.SensorData.Builder heartRate = Sensor.SensorData.newBuilder().setValue(300 + i)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
Sensor.SensorData.Builder batteryLevel = Sensor.SensorData.newBuilder().setValue(400 + i)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
Sensor.SensorDataSet sensorDataSet = Sensor.SensorDataSet.newBuilder().setPower(power)
|
||||
.setCadence(cadence).setHeartRate(heartRate).setBatteryLevel(batteryLevel).build();
|
||||
location.setSensorData(sensorDataSet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +119,10 @@ public abstract class TrackFormatWriterTest extends AndroidTestCase {
|
||||
OutputStream output = new ByteArrayOutputStream(BUFFER_SIZE);
|
||||
writer.prepare(track, output);
|
||||
writer.writeHeader();
|
||||
writer.writeBeginWaypoints();
|
||||
writer.writeWaypoint(wp1);
|
||||
writer.writeWaypoint(wp2);
|
||||
writer.writeEndWaypoints();
|
||||
writer.writeBeginTrack(location1);
|
||||
writer.writeOpenSegment();
|
||||
writer.writeLocation(location1);
|
||||
@@ -118,8 +133,6 @@ public abstract class TrackFormatWriterTest extends AndroidTestCase {
|
||||
writer.writeLocation(location4);
|
||||
writer.writeCloseSegment();
|
||||
writer.writeEndTrack(location4);
|
||||
writer.writeWaypoint(wp1);
|
||||
writer.writeWaypoint(wp2);
|
||||
writer.writeFooter();
|
||||
writer.close();
|
||||
return output.toString();
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io.fusiontables;
|
||||
|
||||
import com.google.android.apps.mytracks.io.docs.SendDocsActivity;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendRequest;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.UploadResultActivity;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
/**
|
||||
* Tests the {@link SendFusionTablesActivity}.
|
||||
*
|
||||
* @author Youtao Liu
|
||||
*/
|
||||
public class SendFusionTablesActivityTest extends AndroidTestCase {
|
||||
|
||||
private SendFusionTablesActivity sendFusionTablesActivity;
|
||||
private SendRequest sendRequest;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
sendRequest = new SendRequest(1L, true, false, true);
|
||||
sendFusionTablesActivity = new SendFusionTablesActivity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link SendFusionTablesActivity#getNextClass(SendRequest, boolean)}. Sets
|
||||
* the flags of "sendDocs" and "cancel" to false and false.
|
||||
*/
|
||||
public void testGetNextClass_notCancelNotSendDocs() {
|
||||
sendRequest.setSendDocs(false);
|
||||
Class<?> next = sendFusionTablesActivity.getNextClass(sendRequest, false);
|
||||
assertEquals(UploadResultActivity.class, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link SendFusionTablesActivity#getNextClass(SendRequest, boolean)}. Sets
|
||||
* the flags of "sendDocs" and "cancel" to true and false.
|
||||
*/
|
||||
public void testGetNextClass_notCancelSendDocs() {
|
||||
sendRequest.setSendDocs(true);
|
||||
Class<?> next = sendFusionTablesActivity.getNextClass(sendRequest, false);
|
||||
assertEquals(SendDocsActivity.class, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link SendFusionTablesActivity#getNextClass(SendRequest,boolean)}. Sets
|
||||
* the flags of "sendDocs" and "cancel" to true and true.
|
||||
*/
|
||||
public void testGetNextClass_cancelSendDocs() {
|
||||
sendRequest.setSendDocs(true);
|
||||
Class<?> next = sendFusionTablesActivity.getNextClass(sendRequest, true);
|
||||
assertEquals(UploadResultActivity.class, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link SendFusionTablesActivity#getNextClass(SendRequest,boolean)}. Sets
|
||||
* the flags of "sendDocs" and "cancel" to false and true.
|
||||
*/
|
||||
public void testGetNextClass_cancelNotSendDocs() {
|
||||
sendRequest.setSendDocs(false);
|
||||
Class<?> next = sendFusionTablesActivity.getNextClass(sendRequest, true);
|
||||
assertEquals(UploadResultActivity.class, next);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io.maps;
|
||||
|
||||
import com.google.android.apps.mytracks.io.gdata.maps.MapsMapMetadata;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Tests the {@link ChooseMapActivity}.
|
||||
*
|
||||
* @author Youtao Liu
|
||||
*/
|
||||
public class ChooseMapActivityTest extends AndroidTestCase {
|
||||
|
||||
private static final String MAP_ID = "mapid";
|
||||
private static final String MAP_TITLE = "title";
|
||||
private static final String MAP_DESC = "desc";
|
||||
|
||||
private ArrayList<String> mapIds = new ArrayList<String>();
|
||||
private ArrayList<MapsMapMetadata> mapDatas = new ArrayList<MapsMapMetadata>();
|
||||
private boolean errorDialogShown = false;
|
||||
private boolean progressDialogRemoved = false;
|
||||
|
||||
/**
|
||||
* Creates a class to override some methods of {@link ChooseMapActivity} to
|
||||
* makes it testable.
|
||||
*
|
||||
* @author youtaol
|
||||
*/
|
||||
public class ChooseMapActivityMock extends ChooseMapActivity {
|
||||
/**
|
||||
* By overriding this method, avoids to start next activity.
|
||||
*/
|
||||
@Override
|
||||
public void startActivity(Intent intent) {}
|
||||
|
||||
/**
|
||||
* By overriding this method, avoids to show an error dialog and set the
|
||||
* show flag to true.
|
||||
*/
|
||||
@Override
|
||||
public void showErrorDialog() {
|
||||
errorDialogShown = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* By overriding this method, avoids to show an error dialog and set the
|
||||
* show flag to true.
|
||||
*/
|
||||
@Override
|
||||
public void removeProgressDialog() {
|
||||
progressDialogRemoved = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link ChooseMapActivity#onAsyncTaskCompleted(boolean, ArrayList, ArrayList)}
|
||||
* . An alert dialog should be shown when there is no map.
|
||||
*/
|
||||
public void testOnAsyncTaskCompleted_fail() {
|
||||
ChooseMapActivityMock chooseMapActivityMock = new ChooseMapActivityMock();
|
||||
errorDialogShown = false;
|
||||
progressDialogRemoved = false;
|
||||
chooseMapActivityMock.onAsyncTaskCompleted(false, null, null);
|
||||
assertTrue(progressDialogRemoved);
|
||||
assertTrue(errorDialogShown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link ChooseMapActivity#onAsyncTaskCompleted(boolean, ArrayList, ArrayList)}
|
||||
* . Check the logic when there is only map.
|
||||
*/
|
||||
public void testOnAsyncTaskCompleted_success_oneMap() {
|
||||
ChooseMapActivityMock chooseMapActivityMock = new ChooseMapActivityMock();
|
||||
chooseMapActivityMock.arrayAdapter = new ArrayAdapter<ChooseMapActivity.ListItem>(getContext(),
|
||||
R.layout.choose_map_item);
|
||||
simulateMaps(1);
|
||||
chooseMapActivityMock.onAsyncTaskCompleted(true, mapIds, mapDatas);
|
||||
assertEquals(1, chooseMapActivityMock.arrayAdapter.getCount());
|
||||
assertEquals(MAP_ID + "0", chooseMapActivityMock.arrayAdapter.getItem(0).getMapId());
|
||||
assertEquals(MAP_TITLE + "0", chooseMapActivityMock.arrayAdapter.getItem(0).getMapData()
|
||||
.getTitle());
|
||||
assertEquals(MAP_DESC + "0", chooseMapActivityMock.arrayAdapter.getItem(0).getMapData()
|
||||
.getDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link ChooseMapActivity#onAsyncTaskCompleted(boolean, ArrayList, ArrayList)}
|
||||
* . Check the logic when there are 10 maps.
|
||||
*/
|
||||
public void testOnAsyncTaskCompleted_success_twoMaps() {
|
||||
ChooseMapActivityMock chooseMapActivityMock = new ChooseMapActivityMock();
|
||||
chooseMapActivityMock.arrayAdapter = new ArrayAdapter<ChooseMapActivity.ListItem>(getContext(),
|
||||
R.layout.choose_map_item);
|
||||
simulateMaps(10);
|
||||
chooseMapActivityMock.onAsyncTaskCompleted(true, mapIds, mapDatas);
|
||||
assertEquals(10, chooseMapActivityMock.arrayAdapter.getCount());
|
||||
assertEquals(MAP_ID + "9", chooseMapActivityMock.arrayAdapter.getItem(9).getMapId());
|
||||
assertEquals(MAP_TITLE + "9", chooseMapActivityMock.arrayAdapter.getItem(9).getMapData()
|
||||
.getTitle());
|
||||
assertEquals(MAP_DESC + "9", chooseMapActivityMock.arrayAdapter.getItem(9).getMapData()
|
||||
.getDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates map data for the test.
|
||||
*
|
||||
* @param number of data should be simulated.
|
||||
*/
|
||||
private void simulateMaps(int number) {
|
||||
mapIds = new ArrayList<String>();
|
||||
mapDatas = new ArrayList<MapsMapMetadata>();
|
||||
for (int i = 0; i < number; i++) {
|
||||
mapIds.add(MAP_ID + i);
|
||||
MapsMapMetadata metaData = new MapsMapMetadata();
|
||||
metaData.setTitle(MAP_TITLE + i);
|
||||
metaData.setDescription(MAP_DESC + i);
|
||||
metaData.setSearchable(true);
|
||||
mapDatas.add(metaData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io.maps;
|
||||
|
||||
import com.google.android.apps.mytracks.io.docs.SendDocsActivity;
|
||||
import com.google.android.apps.mytracks.io.fusiontables.SendFusionTablesActivity;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendRequest;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.UploadResultActivity;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
/**
|
||||
* Tests the {@link SendMapsActivity}.
|
||||
*
|
||||
* @author Youtao Liu
|
||||
*/
|
||||
public class SendMapsActivityTest extends AndroidTestCase {
|
||||
|
||||
private SendMapsActivity sendMapsActivity;
|
||||
private SendRequest sendRequest;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
sendRequest = new SendRequest(1L, true, false, true);
|
||||
sendMapsActivity = new SendMapsActivity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link SendMapsActivity#getNextClass(SendRequest, boolean)}. Sets the flags
|
||||
* of "sendFusionTables","sendDocs" and "cancel" to true, true and false.
|
||||
*/
|
||||
public void testGetNextClass_notCancelSendFusionTables() {
|
||||
sendRequest.setSendFusionTables(true);
|
||||
sendRequest.setSendDocs(true);
|
||||
Class<?> next = sendMapsActivity.getNextClass(sendRequest, false);
|
||||
assertEquals(SendFusionTablesActivity.class, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link SendMapsActivity#getNextClass(SendRequest, boolean)}. Sets the flags
|
||||
* of "sendFusionTables","sendDocs" and "cancel" to false, true and false.
|
||||
*/
|
||||
public void testGetNextClass_notCancelSendDocs() {
|
||||
sendRequest.setSendFusionTables(false);
|
||||
sendRequest.setSendDocs(true);
|
||||
Class<?> next = sendMapsActivity.getNextClass(sendRequest, false);
|
||||
assertEquals(SendDocsActivity.class, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link SendMapsActivity#getNextClass(SendRequest, boolean)}. Sets the flags
|
||||
* of "sendFusionTables","sendDocs" and "cancel" to false, false and false.
|
||||
*/
|
||||
public void testGetNextClass_notCancelNotSend() {
|
||||
sendRequest.setSendFusionTables(false);
|
||||
sendRequest.setSendDocs(false);
|
||||
Class<?> next = sendMapsActivity.getNextClass(sendRequest, false);
|
||||
assertEquals(UploadResultActivity.class, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link SendMapsActivity#getNextClass(SendRequest, boolean)}. Sets the flags
|
||||
* of "sendFusionTables","sendDocs" and "cancel" to true, true and true.
|
||||
*/
|
||||
public void testGetNextClass_cancelSendDocs() {
|
||||
sendRequest.setSendFusionTables(true);
|
||||
sendRequest.setSendDocs(true);
|
||||
Class<?> next = sendMapsActivity.getNextClass(sendRequest, true);
|
||||
assertEquals(UploadResultActivity.class, next);
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -245,7 +245,7 @@ public class SendRequestTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link SendRequest#writeToParcel(Parcel in)} when all input values
|
||||
* Tests {@link SendRequest#writeToParcel(Parcel, int)} when all input values
|
||||
* are true or affirmative.
|
||||
*/
|
||||
public void testWriteToParcel_allTrue() {
|
||||
@@ -283,7 +283,7 @@ public class SendRequestTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link SendRequest#writeToParcel(Parcel in)} when all input values
|
||||
* Tests {@link SendRequest#writeToParcel(Parcel, int)} when all input values
|
||||
* are false or negative.
|
||||
*/
|
||||
public void testWriteToParcel_allFalse() {
|
||||
|
||||
+5
-5
@@ -51,7 +51,7 @@ public class UploadServiceChooserActivityTest extends
|
||||
|
||||
/**
|
||||
* Tests the logic to control display all send options. This test cover code
|
||||
* in method {@link UploadServiceChooserActivity#onCreateDialog()},
|
||||
* in method {@link UploadServiceChooserActivity#onCreateDialog(int)},
|
||||
* {@link UploadServiceChooserActivity#updateStateBySendRequest()} and
|
||||
* {@link UploadServiceChooserActivity#updateStateBySelection()}.
|
||||
*/
|
||||
@@ -91,7 +91,7 @@ public class UploadServiceChooserActivityTest extends
|
||||
|
||||
/**
|
||||
* Tests the logic to check the send to Google Maps option. This test cover
|
||||
* code in method {@link UploadServiceChooserActivity#onCreateDialog()},
|
||||
* code in method {@link UploadServiceChooserActivity#onCreateDialog(int)},
|
||||
* {@link UploadServiceChooserActivity#updateStateBySendRequest()} and
|
||||
* {@link UploadServiceChooserActivity#updateStateBySelection()}.
|
||||
*/
|
||||
@@ -120,7 +120,7 @@ public class UploadServiceChooserActivityTest extends
|
||||
|
||||
/**
|
||||
* Tests the logic to control display none. This test cover code in method
|
||||
* {@link UploadServiceChooserActivity#onCreateDialog()},
|
||||
* {@link UploadServiceChooserActivity#onCreateDialog(int)},
|
||||
* {@link UploadServiceChooserActivity#updateStateBySendRequest()} and
|
||||
* {@link UploadServiceChooserActivity#updateStateBySelection()}.
|
||||
*/
|
||||
@@ -135,7 +135,7 @@ public class UploadServiceChooserActivityTest extends
|
||||
|
||||
/**
|
||||
* Tests the logic to initial state of check box to unchecked. This test cover
|
||||
* code in method {@link UploadServiceChooserActivity#onCreateDialog()},
|
||||
* code in method {@link UploadServiceChooserActivity#onCreateDialog(int)},
|
||||
* {@link UploadServiceChooserActivity#initState()}.
|
||||
*/
|
||||
public void testOnCreateDialog_initStateUnchecked() {
|
||||
@@ -162,7 +162,7 @@ public class UploadServiceChooserActivityTest extends
|
||||
|
||||
/**
|
||||
* Tests the logic to initial state of check box to checked. This test cover
|
||||
* code in method {@link UploadServiceChooserActivity#onCreateDialog()},
|
||||
* code in method {@link UploadServiceChooserActivity#onCreateDialog(int)},
|
||||
* {@link UploadServiceChooserActivity#initState()}.
|
||||
*/
|
||||
public void testOnCreateDialog_initStateChecked() {
|
||||
|
||||
@@ -15,68 +15,217 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks.util;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests for {@link StringUtils}.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class StringUtilsTest extends TestCase {
|
||||
public void testParseXmlDateTime() {
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01",
|
||||
2010, 5, 4, 3, 2, 1, 0);
|
||||
public class StringUtilsTest extends AndroidTestCase {
|
||||
|
||||
/**
|
||||
* 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",
|
||||
2010, 5, 4, 3, 2, 1, 300);
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01.35",
|
||||
2010, 5, 4, 3, 2, 1, 350);
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01.352",
|
||||
2010, 5, 4, 3, 2, 1, 352);
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01.3525",
|
||||
2010, 5, 4, 3, 2, 1, 352);
|
||||
/**
|
||||
* Tests {@link StringUtils#formatDateTime(android.content.Context, long)}.
|
||||
*/
|
||||
public void testFormatDateTime() {
|
||||
// Unix time 0 in UTC is "12/31/1969 4:00 PM" in Pacific Standard time zone.
|
||||
// This test can break if run on a different time zone or with a different
|
||||
// date/time format preference.
|
||||
assertEquals("12/31/1969 4:00 PM", StringUtils.formatDateTime(getContext(), 0L));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link StringUtils#formatDateTimeIso8601(long)}.
|
||||
*/
|
||||
public void testFormatDateTimeIso8601() {
|
||||
assertEquals("1970-01-01T00:00:12.345Z", StringUtils.formatDateTimeIso8601(12345));
|
||||
}
|
||||
|
||||
public void testParseXmlDateTime_timezone() {
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01Z",
|
||||
2010, 5, 4, 3, 2, 1, 0);
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01+00:00",
|
||||
2010, 5, 4, 3, 2, 1, 0);
|
||||
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);
|
||||
/**
|
||||
* Tests {@link StringUtils#formatElapsedTime(long)}.
|
||||
*/
|
||||
public void testformatElapsedTime() {
|
||||
// 1 second
|
||||
assertEquals("00:01", StringUtils.formatElapsedTime(1000));
|
||||
// 10 seconds
|
||||
assertEquals("00:10", StringUtils.formatElapsedTime(10000));
|
||||
// 1 minute
|
||||
assertEquals("01:00", StringUtils.formatElapsedTime(60000));
|
||||
// 10 minutes
|
||||
assertEquals("10:00", StringUtils.formatElapsedTime(600000));
|
||||
// 1 hour
|
||||
assertEquals("1:00:00", StringUtils.formatElapsedTime(3600000));
|
||||
// 10 hours
|
||||
assertEquals("10:00:00", StringUtils.formatElapsedTime(36000000));
|
||||
// 100 hours
|
||||
assertEquals("100:00:00", StringUtils.formatElapsedTime(360000000));
|
||||
}
|
||||
|
||||
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);
|
||||
/**
|
||||
* Tests {@link StringUtils#formatElapsedTimeWithHour(long)}.
|
||||
*/
|
||||
public void testformatElapsedTimeWithHour() {
|
||||
// 1 second
|
||||
assertEquals("0:00:01", StringUtils.formatElapsedTimeWithHour(1000));
|
||||
// 10 seconds
|
||||
assertEquals("0:00:10", StringUtils.formatElapsedTimeWithHour(10000));
|
||||
// 1 minute
|
||||
assertEquals("0:01:00", StringUtils.formatElapsedTimeWithHour(60000));
|
||||
// 10 minutes
|
||||
assertEquals("0:10:00", StringUtils.formatElapsedTimeWithHour(600000));
|
||||
// 1 hour
|
||||
assertEquals("1:00:00", StringUtils.formatElapsedTimeWithHour(3600000));
|
||||
// 10 hours
|
||||
assertEquals("10:00:00", StringUtils.formatElapsedTimeWithHour(36000000));
|
||||
// 100 hours
|
||||
assertEquals("100:00:00", StringUtils.formatElapsedTimeWithHour(360000000));
|
||||
}
|
||||
|
||||
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);
|
||||
/**
|
||||
* 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