Merge heads

This commit is contained in:
youtaol
2013-06-14 10:20:59 +08:00
16 changed files with 206 additions and 218 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ Android Support Package:
android-support-v4.jar (version 13) android-support-v4.jar (version 13)
Ant+: Ant+:
antlib.jar - see http://www.thisisant.com/pages/developer-zone/android-api antlib.jar - from Android_ANT_SDK_20120625.zip
Google APIs Client Library for Java: Google APIs Client Library for Java:
google-api-client-1.12.0-beta.jar google-api-client-1.12.0-beta.jar
Binary file not shown.
+15 -15
View File
@@ -384,9 +384,9 @@ limitations under the License.
<string name="description_average_moving_pace" <string name="description_average_moving_pace"
translation_description="Average moving pace description. translation_description="Average moving pace description.
%1$.2f pace in minutes per kilometer %1$s pace in minutes per kilometer
%2$.1f pace in minutes per mile"> %2$s pace in minutes per mile">
Average moving pace: %1$.2f min/km (%2$.1f min/mi) Average moving pace: %1$s min/km (%2$s min/mi)
</string> </string>
<string name="description_average_moving_speed" <string name="description_average_moving_speed"
translation_description="Average moving speed description. translation_description="Average moving speed description.
@@ -398,9 +398,9 @@ limitations under the License.
<string name="description_average_pace" <string name="description_average_pace"
translation_description="Average pace description. translation_description="Average pace description.
%1$.2f pace in minutes per kilometer %1$s pace in minutes per kilometer
%2$.1f pace in minutes per mile"> %2$s pace in minutes per mile">
Average pace: %1$.2f min/km (%2$.1f min/mi) Average pace: %1$s min/km (%2$s min/mi)
</string> </string>
<string name="description_average_speed" <string name="description_average_speed"
translation_description="Average speed description. translation_description="Average speed description.
@@ -427,9 +427,9 @@ limitations under the License.
<string name="description_fastest_pace" <string name="description_fastest_pace"
translation_description="Fastest pace description. translation_description="Fastest pace description.
%1$.2f pace in minutes per kilometer %1$s pace in minutes per kilometer
%2$.1f pace in minutes per mile"> %2$s pace in minutes per mile">
Fastest pace: %1$.2f min/km (%2$.1f min/mi) Fastest pace: %1$s min/km (%2$s min/mi)
</string> </string>
<string name="description_location_accuracy" <string name="description_location_accuracy"
translation_description="Label for location accuracy in meters."> translation_description="Label for location accuracy in meters.">
@@ -497,12 +497,12 @@ limitations under the License.
Moving time: %1$s Moving time: %1$s
</string> </string>
<string name="description_pace_imperial" <string name="description_pace_imperial"
translation_description="Pace in imperial units, hours per mile."> translation_description="Pace in imperial units, minutes per mile.">
Pace (hr/mi) Pace (min/mi)
</string> </string>
<string name="description_pace_metric" <string name="description_pace_metric"
translation_description="Pace in metric units, hours per kilometer."> translation_description="Pace in metric units, minutes per kilometer.">
Pace (hr/km) Pace (min/km)
</string> </string>
<string name="description_recorded_time" <string name="description_recorded_time"
translation_description="Recorded time description. translation_description="Recorded time description.
@@ -1974,7 +1974,7 @@ limitations under the License.
</string> </string>
<string name="settings_stats_rate_title" <string name="settings_stats_rate_title"
translation_description="In the 'Stats' settings, the option to change the preferred rate, translation_description="In the 'Stats' settings, the option to change the preferred rate,
either speed (mi/hr) or pace (hr/mi). either speed (mi/hr) or pace (min/mi).
Character limit: ~23 (1 line)"> Character limit: ~23 (1 line)">
Preferred rate Preferred rate
@@ -2131,7 +2131,7 @@ limitations under the License.
Moving time Moving time
</string> </string>
<string name="stats_pace" <string name="stats_pace"
translation_description="In the 'Stats' page, the pace like hours per mile. translation_description="In the 'Stats' page, the pace like minutes per mile.
Character limit: ~23 (1 line)"> Character limit: ~23 (1 line)">
Pace Pace
@@ -653,13 +653,14 @@ public class ChartView extends View {
canvas.drawLine(x, y, x + effectiveWidth, y, axisPaint); canvas.drawLine(x, y, x + effectiveWidth, y, axisPaint);
String label = getXAxisLabel(); String label = getXAxisLabel();
Rect rect = getRect(axisPaint, label); Rect rect = getRect(axisPaint, label);
canvas.drawText(label, x + effectiveWidth + spacer, y + ((int) rect.height() / 2), axisPaint); int yOffset = (int) rect.height() / 2;
canvas.drawText(label, x + effectiveWidth + spacer, y + yOffset, axisPaint);
double interval = getXAxisInterval(); double interval = getXAxisInterval();
ArrayList<Double> markerPositions = getXAxisMarkerPositions(interval); ArrayList<Double> markerPositions = getXAxisMarkerPositions(interval);
NumberFormat numberFormat = interval < 1 ? X_FRACTION_FORMAT : X_NUMBER_FORMAT; NumberFormat numberFormat = interval < 1 ? X_FRACTION_FORMAT : X_NUMBER_FORMAT;
for (int i = 0; i < markerPositions.size(); i++) { for (int i = 0; i < markerPositions.size(); i++) {
drawXAxisMarker(canvas, markerPositions.get(i), numberFormat); drawXAxisMarker(canvas, markerPositions.get(i), numberFormat, spacer + yOffset);
} }
} }
@@ -682,12 +683,14 @@ public class ChartView extends View {
* @param canvas * @param canvas
* @param value value * @param value value
* @param numberFormat the number format * @param numberFormat the number format
* @param spacing the spacing between x axis and marker
*/ */
private void drawXAxisMarker(Canvas canvas, double value, NumberFormat numberFormat) { private void drawXAxisMarker(
Canvas canvas, double value, NumberFormat numberFormat, int spacing) {
String marker = chartByDistance ? numberFormat.format(value) String marker = chartByDistance ? numberFormat.format(value)
: StringUtils.formatElapsedTime((long) value); : StringUtils.formatElapsedTime((long) value);
Rect rect = getRect(xAxisMarkerPaint, marker); Rect rect = getRect(xAxisMarkerPaint, marker);
canvas.drawText(marker, getX(value), topBorder + effectiveHeight + spacer + rect.height(), canvas.drawText(marker, getX(value), topBorder + effectiveHeight + spacing + rect.height(),
xAxisMarkerPaint); xAxisMarkerPaint);
} }
@@ -906,8 +909,11 @@ public class ChartView extends View {
leftBorder = (int) (density * BORDER + markerLength); leftBorder = (int) (density * BORDER + markerLength);
int[] titleDimensions = getTitleDimenions(); int[] titleDimensions = getTitleDimenions();
topBorder = (int) (density * BORDER + titleDimensions[0] * (titleDimensions[1] + spacer)); topBorder = (int) (density * BORDER + titleDimensions[0] * (titleDimensions[1] + spacer));
bottomBorder = (int) (density * BORDER + getRect(xAxisMarkerPaint, "1").height() + spacer); Rect xAxisLabelRect = getRect(axisPaint, getXAxisLabel());
rightBorder = (int) (density * BORDER + getRect(axisPaint, getXAxisLabel()).width() + spacer); // border + x axis marker + spacer + .5 x axis label
bottomBorder = (int) (density * BORDER + getRect(xAxisMarkerPaint, "1").height() + spacer
+ (int) (xAxisLabelRect.height() / 2));
rightBorder = (int) (density * BORDER + xAxisLabelRect.width() + spacer);
updateEffectiveDimensions(); updateEffectiveDimensions();
} }
@@ -27,14 +27,12 @@ import com.google.android.maps.mytracks.R;
import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.InvalidProtocolBufferException;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException; import android.os.RemoteException;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.util.Log; import android.util.Log;
import android.widget.TextView; import android.widget.TextView;
import java.util.Timer;
import java.util.TimerTask;
/** /**
* An activity that displays information about sensors. * An activity that displays information about sensors.
* *
@@ -43,12 +41,14 @@ import java.util.TimerTask;
public class SensorStateActivity extends AbstractMyTracksActivity { public class SensorStateActivity extends AbstractMyTracksActivity {
private static final String TAG = SensorStateActivity.class.getName(); private static final String TAG = SensorStateActivity.class.getName();
private static final long REFRESH_PERIOD_MS = 250; private static final long ONE_SECOND = 1000;
/** private TrackRecordingServiceConnection trackRecordingServiceConnection;
* A Runnable to update the UI. private Handler handler;
*/ private SensorManager tempSensorManager;
private final Runnable updateRunnable = new Runnable() {
private final Runnable updateUiRunnable = new Runnable() {
@Override
public void run() { public void run() {
ITrackRecordingService trackRecordingService = trackRecordingServiceConnection ITrackRecordingService trackRecordingService = trackRecordingServiceConnection
.getServiceIfBound(); .getServiceIfBound();
@@ -69,30 +69,15 @@ public class SensorStateActivity extends AbstractMyTracksActivity {
stopTempSensorManager(); stopTempSensorManager();
updateFromSystemSensorManager(); updateFromSystemSensorManager();
} }
handler.postDelayed(this, ONE_SECOND);
} }
}; };
/**
* A TimeTask to update the UI.
*/
private class UpdateTimerTask extends TimerTask {
@Override
public void run() {
if (isVisible) {
runOnUiThread(updateRunnable);
}
}
};
private TrackRecordingServiceConnection trackRecordingServiceConnection;
private boolean isVisible = false;
private Timer timer = null;
private SensorManager tempSensorManager = null;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null); trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
handler = new Handler();
} }
@Override @Override
@@ -104,18 +89,13 @@ public class SensorStateActivity extends AbstractMyTracksActivity {
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
isVisible = true; handler.post(updateUiRunnable);
timer = new Timer();
timer.schedule(new UpdateTimerTask(), 0, REFRESH_PERIOD_MS);
} }
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
isVisible = false; handler.removeCallbacks(updateUiRunnable);
timer.cancel();
timer.purge();
timer = null;
stopTempSensorManager(); stopTempSensorManager();
} }
@@ -367,7 +367,8 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
@Override @Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) { public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
return new CursorLoader(TrackListActivity.this, TracksColumns.CONTENT_URI, PROJECTION, null, return new CursorLoader(TrackListActivity.this, TracksColumns.CONTENT_URI, PROJECTION, null,
null, TracksColumns.SHAREDWITHME + " ASC, " + TracksColumns.STARTTIME + " DESC"); null, "IFNULL(" + TracksColumns.SHAREDWITHME + ",0) ASC, " + TracksColumns.STARTTIME
+ " DESC");
} }
@Override @Override
@@ -24,7 +24,6 @@ import com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import android.content.Context; import android.content.Context;
import android.util.Pair;
import java.util.Vector; import java.util.Vector;
@@ -117,25 +116,24 @@ public class DescriptionGeneratorImpl implements DescriptionGenerator {
writeTime(stats.getMovingTime(), builder, R.string.description_moving_time, lineBreak); writeTime(stats.getMovingTime(), builder, R.string.description_moving_time, lineBreak);
// Average speed // Average speed
Pair<Double, Double> averageSpeed = writeSpeed( writeSpeed(stats.getAverageSpeed(), builder, R.string.description_average_speed, lineBreak);
stats.getAverageSpeed(), builder, R.string.description_average_speed, lineBreak);
// Average moving speed // Average moving speed
Pair<Double, Double> averageMovingSpeed = writeSpeed(stats.getAverageMovingSpeed(), builder, writeSpeed(stats.getAverageMovingSpeed(), builder, R.string.description_average_moving_speed,
R.string.description_average_moving_speed, lineBreak); lineBreak);
// Max speed // Max speed
Pair<Double, Double> maxSpeed = writeSpeed( writeSpeed(stats.getMaxSpeed(), builder, R.string.description_max_speed, lineBreak);
stats.getMaxSpeed(), builder, R.string.description_max_speed, lineBreak);
// Average pace // Average pace
writePace(averageSpeed, builder, R.string.description_average_pace, lineBreak); writePace(stats.getAverageSpeed(), builder, R.string.description_average_pace, lineBreak);
// Average moving pace // Average moving pace
writePace(averageMovingSpeed, builder, R.string.description_average_moving_pace, lineBreak); writePace(stats.getAverageMovingSpeed(), builder, R.string.description_average_moving_pace,
lineBreak);
// Fastest pace // Fastest pace
writePace(maxSpeed, builder, R.string.description_fastest_pace, lineBreak); writePace(stats.getMaxSpeed(), builder, R.string.description_fastest_pace, lineBreak);
// Max elevation // Max elevation
writeElevation(stats.getMaxElevation(), builder, R.string.description_max_elevation, lineBreak); writeElevation(stats.getMaxElevation(), builder, R.string.description_max_elevation, lineBreak);
@@ -199,34 +197,38 @@ public class DescriptionGeneratorImpl implements DescriptionGenerator {
* @param builder StringBuilder to append speed * @param builder StringBuilder to append speed
* @param resId resource id of speed string * @param resId resource id of speed string
* @param lineBreak line break string * @param lineBreak line break string
* @return a pair of speed, first in kilometers per hour, second in miles per
* hour.
*/ */
@VisibleForTesting @VisibleForTesting
Pair<Double, Double> writeSpeed( void writeSpeed(
double speed, StringBuilder builder, int resId, String lineBreak) { double speed, StringBuilder builder, int resId, String lineBreak) {
double speedInKmHr = speed * UnitConversions.MS_TO_KMH; double speedInKmHr = speed * UnitConversions.MS_TO_KMH;
double speedInMiHr = speedInKmHr * UnitConversions.KM_TO_MI; double speedInMiHr = speedInKmHr * UnitConversions.KM_TO_MI;
builder.append(context.getString(resId, speedInKmHr, speedInMiHr)); builder.append(context.getString(resId, speedInKmHr, speedInMiHr));
builder.append(lineBreak); builder.append(lineBreak);
return Pair.create(speedInKmHr, speedInMiHr);
} }
/** /**
* Writes pace. * Writes pace.
* *
* @param speed a pair of speed, first in kilometers per hour, second in miles * @param speed speed in meters per second
* per hour
* @param builder StringBuilder to append pace * @param builder StringBuilder to append pace
* @param resId resource id of pace string * @param resId resource id of pace string
* @param lineBreak line break string * @param lineBreak line break string
*/ */
@VisibleForTesting @VisibleForTesting
void writePace( void writePace(double speed, StringBuilder builder, int resId, String lineBreak) {
Pair<Double, Double> speed, StringBuilder builder, int resId, String lineBreak) { String[] paceInMetrics = StringUtils.getSpeedParts(context, speed, true, false);
double paceInMinKm = getPace(speed.first); String[] paceInImperial = StringUtils.getSpeedParts(context, speed, false, false);
double paceInMinMi = getPace(speed.second);
builder.append(context.getString(resId, paceInMinKm, paceInMinMi)); if (paceInMetrics[0] == null) {
paceInMetrics[0] = context.getString(R.string.value_unknown);
}
if (paceInImperial[0] == null) {
paceInImperial[0] = context.getString(R.string.value_unknown);
}
builder.append(context.getString(resId, paceInMetrics[0], paceInImperial[0]));
builder.append(lineBreak); builder.append(lineBreak);
} }
@@ -262,14 +264,4 @@ public class DescriptionGeneratorImpl implements DescriptionGenerator {
builder.append(context.getString(resId, gradeInPercent)); builder.append(context.getString(resId, gradeInPercent));
builder.append(lineBreak); builder.append(lineBreak);
} }
/**
* Gets pace (in minutes) from speed.
*
* @param speed speed in hours
*/
@VisibleForTesting
double getPace(double speed) {
return speed == 0 ? 0.0 : 60.0 / speed; // convert from hours to minutes
}
} }
@@ -134,9 +134,10 @@ public class TrackDataHub implements DataSourceListener {
started = false; started = false;
dataSourceManager.unregisterAllListeners(); dataSourceManager.unregisterAllListeners();
handlerThread.getLooper().quit(); if (handlerThread != null) {
handlerThread.getLooper().quit();
handlerThread = null; handlerThread = null;
}
handler = null; handler = null;
dataSource = null; dataSource = null;
dataSourceManager = null; dataSourceManager = null;
@@ -27,7 +27,11 @@ import android.content.DialogInterface;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.DialogFragment; import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentActivity;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.widget.TextView;
import java.util.Locale; import java.util.Locale;
@@ -89,8 +93,12 @@ public class EulaDialogFragment extends DialogFragment {
fragmentActivity = getActivity(); fragmentActivity = getActivity();
boolean hasAccepted = getArguments().getBoolean(KEY_HAS_ACCEPTED); boolean hasAccepted = getArguments().getBoolean(KEY_HAS_ACCEPTED);
AlertDialog.Builder builder = new AlertDialog.Builder(fragmentActivity).setMessage(
getEulaText()).setTitle(R.string.eula_title); SpannableString message = new SpannableString(getEulaText());
Linkify.addLinks(message, Linkify.WEB_URLS);
AlertDialog.Builder builder = new AlertDialog.Builder(fragmentActivity).setMessage(message)
.setTitle(R.string.eula_title);
if (hasAccepted) { if (hasAccepted) {
builder.setPositiveButton(R.string.generic_ok, null); builder.setPositiveButton(R.string.generic_ok, null);
@@ -121,6 +129,13 @@ public class EulaDialogFragment extends DialogFragment {
return builder.create(); return builder.create();
} }
@Override
public void onStart() {
super.onStart();
TextView textView = (TextView) getDialog().findViewById(android.R.id.message);
textView.setMovementMethod(LinkMovementMethod.getInstance());
}
@Override @Override
public void onCancel(DialogInterface arg0) { public void onCancel(DialogInterface arg0) {
caller.onEulaDone(); caller.onEulaDone();
@@ -237,7 +237,8 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
int count = cursor.getCount(); int count = cursor.getCount();
List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD); List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD);
Location lastLocation = null; Location lastValidLocation = null;
boolean sentStartMarker = false;
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
cursor.moveToPosition(i); cursor.moveToPosition(i);
@@ -245,14 +246,15 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
Location location = myTracksProviderUtils.createTrackPoint(cursor); Location location = myTracksProviderUtils.createTrackPoint(cursor);
locations.add(location); locations.add(location);
if (i == 0) { if (LocationUtils.isValidLocation(location)) {
// Create a start marker lastValidLocation = location;
String name = context.getString(R.string.marker_label_start, track.getName());
createNewPoint(fusiontables, tableId, name, "", location, MARKER_TYPE_START);
} }
if (LocationUtils.isValidLocation(location)) { if (!sentStartMarker && lastValidLocation != null) {
lastLocation = location; // Create a start marker
String name = context.getString(R.string.marker_label_start, track.getName());
createNewPoint(fusiontables, tableId, name, "", lastValidLocation, MARKER_TYPE_START);
sentStartMarker = true;
} }
// Upload periodically // Upload periodically
@@ -274,11 +276,12 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
} }
// Create an end marker // Create an end marker
if (lastLocation != null) { if (lastValidLocation != null) {
String name = context.getString(R.string.marker_label_end, track.getName()); String name = context.getString(R.string.marker_label_end, track.getName());
DescriptionGenerator descriptionGenerator = new DescriptionGeneratorImpl(context); DescriptionGenerator descriptionGenerator = new DescriptionGeneratorImpl(context);
String description = descriptionGenerator.generateTrackDescription(track, null, null, true); String description = descriptionGenerator.generateTrackDescription(track, null, null, true);
createNewPoint(fusiontables, tableId, name, description, lastLocation, MARKER_TYPE_END); createNewPoint(
fusiontables, tableId, name, description, lastValidLocation, MARKER_TYPE_END);
} }
return true; return true;
} finally { } finally {
@@ -244,7 +244,8 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
int count = cursor.getCount(); int count = cursor.getCount();
List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD); List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD);
Location lastLocation = null; Location lastValidLocation = null;
boolean sentStartMarker = false;
// For chart server, limit the number of elevation readings to 250. // For chart server, limit the number of elevation readings to 250.
int elevationSamplingFrequency = Math.max(1, (int) (count / 250.0)); int elevationSamplingFrequency = Math.max(1, (int) (count / 250.0));
@@ -261,13 +262,18 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
Location location = myTracksProviderUtils.createTrackPoint(cursor); Location location = myTracksProviderUtils.createTrackPoint(cursor);
locations.add(location); locations.add(location);
if (i == 0) { if (LocationUtils.isValidLocation(location)) {
lastValidLocation = location;
}
if (!sentStartMarker && lastValidLocation != null) {
// Create a start marker // Create a start marker
if (!uploadMarker(context.getString(R.string.marker_label_start, track.getName()), "", if (!uploadMarker(context.getString(R.string.marker_label_start, track.getName()), "",
START_ICON_URL, location)) { START_ICON_URL, lastValidLocation)) {
Log.d(TAG, "Unable to create a start marker"); Log.d(TAG, "Unable to create a start marker");
return false; return false;
} }
sentStartMarker = true;
} }
tripStatisticsUpdater.addLocation(location, minRecordingDistance); tripStatisticsUpdater.addLocation(location, minRecordingDistance);
@@ -275,9 +281,6 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
distances.add(tripStatisticsUpdater.getTripStatistics().getTotalDistance()); distances.add(tripStatisticsUpdater.getTripStatistics().getTotalDistance());
elevations.add(tripStatisticsUpdater.getSmoothedElevation()); elevations.add(tripStatisticsUpdater.getSmoothedElevation());
} }
if (LocationUtils.isValidLocation(location)) {
lastLocation = location;
}
// Upload periodically // Upload periodically
int readCount = i + 1; int readCount = i + 1;
@@ -298,14 +301,14 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
} }
// Create an end marker // Create an end marker
if (lastLocation != null) { if (lastValidLocation != null) {
distances.add(tripStatisticsUpdater.getTripStatistics().getTotalDistance()); distances.add(tripStatisticsUpdater.getTripStatistics().getTotalDistance());
elevations.add(tripStatisticsUpdater.getSmoothedElevation()); elevations.add(tripStatisticsUpdater.getSmoothedElevation());
DescriptionGenerator descriptionGenerator = new DescriptionGeneratorImpl(context); DescriptionGenerator descriptionGenerator = new DescriptionGeneratorImpl(context);
track.setDescription( track.setDescription(
descriptionGenerator.generateTrackDescription(track, distances, elevations, true)); descriptionGenerator.generateTrackDescription(track, distances, elevations, true));
if (!uploadMarker(context.getString(R.string.marker_label_end, track.getName()), if (!uploadMarker(context.getString(R.string.marker_label_end, track.getName()),
track.getDescription(), END_ICON_URL, lastLocation)) { track.getDescription(), END_ICON_URL, lastValidLocation)) {
Log.d(TAG, "Unable to create an end marker"); Log.d(TAG, "Unable to create an end marker");
return false; return false;
} }
@@ -133,8 +133,9 @@ public class TrackRecordingService extends Service {
private Location lastLocation; private Location lastLocation;
private boolean currentSegmentHasLocation; private boolean currentSegmentHasLocation;
// Timer to periodically invoke checkLocationListener // TimerTask to register location listener
private final Timer timer = new Timer(); private TimerTask timerTask;
private Timer timer;
// Handler for the timer to post a runnable to the main thread // Handler for the timer to post a runnable to the main thread
private final Handler handler = new Handler(); private final Handler handler = new Handler();
@@ -247,15 +248,6 @@ public class TrackRecordingService extends Service {
} }
}; };
private TimerTask checkLocationListener = new TimerTask() {
@Override
public void run() {
if (isRecording() && !isPaused()) {
registerLocationListener();
}
}
};
private final ConnectionCallbacks activityRecognitionCallbacks = new ConnectionCallbacks() { private final ConnectionCallbacks activityRecognitionCallbacks = new ConnectionCallbacks() {
@Override @Override
public void onDisconnected() {} public void onDisconnected() {}
@@ -301,10 +293,19 @@ public class TrackRecordingService extends Service {
// onSharedPreferenceChanged might not set recordingTrackId. // onSharedPreferenceChanged might not set recordingTrackId.
recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
// Require announcementExecutor and splitExecutor to be created. // Require voiceExecutor and splitExecutor to be created.
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null); sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
timer.schedule(checkLocationListener, 0, ONE_MINUTE); timerTask = new TimerTask() {
@Override
public void run() {
if (isRecording() && !isPaused()) {
registerLocationListener();
}
}
};
timer = new Timer("TrackRecordingServiceTimer");
timer.schedule(timerTask, 0, ONE_MINUTE);
/* /*
* Try to restart the previous recording track in case the service has been * Try to restart the previous recording track in case the service has been
@@ -357,10 +358,15 @@ public class TrackRecordingService extends Service {
showNotification(false); showNotification(false);
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
checkLocationListener.cancel(); if (timerTask != null) {
checkLocationListener = null; timerTask.cancel();
timer.cancel(); timerTask = null;
timer.purge(); }
if (timer != null) {
timer.cancel();
timer.purge();
timer = null;
}
unregisterLocationListener(); unregisterLocationListener();
try { try {
@@ -39,30 +39,8 @@ public abstract class SensorManager {
private SensorState sensorState = SensorState.NONE; private SensorState sensorState = SensorState.NONE;
private long sensorStateTimestamp = System.currentTimeMillis(); private long sensorStateTimestamp = System.currentTimeMillis();
/** private TimerTask timerTask;
* A time task to check sensor connection. private Timer timer;
*/
private TimerTask checkSensorConnectionTimeTask = new TimerTask() {
@Override
public void run() {
switch (getSensorState()) {
case CONNECTING:
if (System.currentTimeMillis() - sensorStateTimestamp > MAX_SENSOR_STATE_AGE) {
Log.i(TAG, "Retry setUpChannel");
setUpChannel();
}
break;
case NONE:
case DISCONNECTED:
setUpChannel();
break;
default:
break;
}
}
};
private final Timer timer = new Timer();
/** /**
* Returns true if the sensor is enabled. * Returns true if the sensor is enabled.
@@ -89,14 +67,43 @@ public abstract class SensorManager {
*/ */
public void startSensor() { public void startSensor() {
setUpChannel(); setUpChannel();
timer.schedule(checkSensorConnectionTimeTask, RETRY_PERIOD, RETRY_PERIOD); timerTask = new TimerTask() {
@Override
public void run() {
switch (getSensorState()) {
case CONNECTING:
if (System.currentTimeMillis() - sensorStateTimestamp > MAX_SENSOR_STATE_AGE) {
Log.i(TAG, "Retry setUpChannel");
setUpChannel();
}
break;
case NONE:
case DISCONNECTED:
setUpChannel();
break;
default:
// CONNECTED or SENDING
break;
}
}
};
timer = new Timer("SensorManagerTimer");
timer.schedule(timerTask, RETRY_PERIOD, RETRY_PERIOD);
} }
/** /**
* Stops the sensor. * Stops the sensor.
*/ */
public void stopSensor() { public void stopSensor() {
timer.cancel(); if (timerTask != null) {
timerTask.cancel();
timerTask = null;
}
if (timer != null) {
timer.cancel();
timer.purge();
timer = null;
}
tearDownChannel(); tearDownChannel();
} }
@@ -33,10 +33,7 @@ public class TimerTaskExecutor {
private final PeriodicTask periodicTask; private final PeriodicTask periodicTask;
private final TrackRecordingService trackRecordingService; private final TrackRecordingService trackRecordingService;
/** private TimerTask timerTask;
* A timer to schedule the announcements. This is non-null if the task is in
* started (scheduled) state.
*/
private Timer timer; private Timer timer;
public TimerTaskExecutor(PeriodicTask periodicTask, TrackRecordingService trackRecordingService) { public TimerTaskExecutor(PeriodicTask periodicTask, TrackRecordingService trackRecordingService) {
@@ -50,6 +47,10 @@ public class TimerTaskExecutor {
* @param interval the interval in milliseconds * @param interval the interval in milliseconds
*/ */
public void scheduleTask(long interval) { public void scheduleTask(long interval) {
if (interval <= 0) {
return;
}
if (!trackRecordingService.isRecording() || trackRecordingService.isPaused()) { if (!trackRecordingService.isRecording() || trackRecordingService.isPaused()) {
return; return;
} }
@@ -59,43 +60,32 @@ public class TimerTaskExecutor {
return; return;
} }
if (timer != null) { shutdown();
timer.cancel(); periodicTask.start();
timer.purge(); timerTask = new TimerTask() {
} else { @Override
// First start, or we were previously shut down. public void run() {
periodicTask.start(); periodicTask.run(trackRecordingService);
} }
};
timer = new Timer(); timer = new Timer("TimerTaskExecutorTimer");
if (interval <= 0) {
return;
}
long next = System.currentTimeMillis() + interval - (tripStatistics.getTotalTime() % interval); long next = System.currentTimeMillis() + interval - (tripStatistics.getTotalTime() % interval);
timer.scheduleAtFixedRate(new PeriodicTimerTask(), new Date(next), interval); timer.scheduleAtFixedRate(timerTask, new Date(next), interval);
} }
/** /**
* Shuts down. * Shuts down.
*/ */
public void shutdown() { public void shutdown() {
if (timerTask != null) {
timerTask.cancel();
timerTask = null;
}
if (timer != null) { if (timer != null) {
timer.cancel(); timer.cancel();
timer.purge(); timer.purge();
timer = null; timer = null;
periodicTask.shutdown();
}
}
/**
* The timer task to announce the trip status.
*/
private class PeriodicTimerTask extends TimerTask {
@Override
public void run() {
periodicTask.run(trackRecordingService);
} }
periodicTask.shutdown();
} }
} }
@@ -213,19 +213,18 @@ public class StringUtils {
return result; return result;
} }
speed *= UnitConversions.MS_TO_KMH; speed *= UnitConversions.MS_TO_KMH;
if (metricUnits) { if (!metricUnits) {
if (!reportSpeed) {
// convert from hours to minutes
speed = speed == 0 ? 0.0 : 60.0 / speed;
}
} else {
speed *= UnitConversions.KM_TO_MI; speed *= UnitConversions.KM_TO_MI;
if (!reportSpeed) {
// convert from hours to minutes
speed = speed == 0 ? 0.0 : 60.0 / speed;
}
} }
result[0] = String.format("%.2f", speed); if (reportSpeed) {
result[0] = String.format("%.2f", speed);
} else {
// convert from hours to minutes
double pace = speed == 0 ? 0.0 : 60.0 / speed;
int minutes = (int) pace;
int seconds = (int) Math.round((pace - minutes) * 60.0);
result[0] = String.format("%d:%02d", minutes, seconds);
}
return result; return result;
} }
@@ -68,9 +68,9 @@ public class DescriptionGeneratorImplTest extends AndroidTestCase {
+ "Average speed: 120.00 km/h (74.6 mi/h)<br>" + "Average speed: 120.00 km/h (74.6 mi/h)<br>"
+ "Average moving speed: 240.00 km/h (149.1 mi/h)<br>" + "Average moving speed: 240.00 km/h (149.1 mi/h)<br>"
+ "Max speed: 360.00 km/h (223.7 mi/h)<br>" + "Max speed: 360.00 km/h (223.7 mi/h)<br>"
+ "Average pace: 0.50 min/km (0.8 min/mi)<br>" + "Average pace: 0:30 min/km (0:48 min/mi)<br>"
+ "Average moving pace: 0.25 min/km (0.4 min/mi)<br>" + "Average moving pace: 0:15 min/km (0:24 min/mi)<br>"
+ "Fastest pace: 0.17 min/km (0.3 min/mi)<br>" + "Fastest pace: 0:10 min/km (0:16 min/mi)<br>"
+ "Max elevation: 550 m (1804 ft)<br>" + "Max elevation: 550 m (1804 ft)<br>"
+ "Min elevation: -500 m (-1640 ft)<br>" + "Min elevation: -500 m (-1640 ft)<br>"
+ "Elevation gain: 6000 m (19685 ft)<br>" + "Elevation gain: 6000 m (19685 ft)<br>"
@@ -103,9 +103,9 @@ public class DescriptionGeneratorImplTest extends AndroidTestCase {
+ "Average speed: 120.00 km/h (74.6 mi/h)\n" + "Average speed: 120.00 km/h (74.6 mi/h)\n"
+ "Average moving speed: 240.00 km/h (149.1 mi/h)\n" + "Average moving speed: 240.00 km/h (149.1 mi/h)\n"
+ "Max speed: 360.00 km/h (223.7 mi/h)\n" + "Max speed: 360.00 km/h (223.7 mi/h)\n"
+ "Average pace: 0.50 min/km (0.8 min/mi)\n" + "Average pace: 0:30 min/km (0:48 min/mi)\n"
+ "Average moving pace: 0.25 min/km (0.4 min/mi)\n" + "Average moving pace: 0:15 min/km (0:24 min/mi)\n"
+ "Fastest pace: 0.17 min/km (0.3 min/mi)\n" + "Fastest pace: 0:10 min/km (0:16 min/mi)\n"
+ "Max elevation: 550 m (1804 ft)\n" + "Max elevation: 550 m (1804 ft)\n"
+ "Min elevation: -500 m (-1640 ft)\n" + "Min elevation: -500 m (-1640 ft)\n"
+ "Elevation gain: 6000 m (19685 ft)\n" + "Elevation gain: 6000 m (19685 ft)\n"
@@ -162,9 +162,8 @@ public class DescriptionGeneratorImplTest extends AndroidTestCase {
*/ */
public void testWritePace() { public void testWritePace() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
descriptionGenerator.writePace( descriptionGenerator.writePace(1.1, builder, R.string.description_average_pace, "\n");
new Pair<Double, Double>(1.1, 2.2), builder, R.string.description_average_pace, "\n"); assertEquals("Average pace: 15:09 min/km (24:23 min/mi)\n", builder.toString());
assertEquals("Average pace: 54.55 min/km (27.3 min/mi)\n", builder.toString());
} }
/** /**
@@ -197,18 +196,4 @@ public class DescriptionGeneratorImplTest extends AndroidTestCase {
Double.POSITIVE_INFINITY, builder, R.string.description_max_grade, "<br>"); Double.POSITIVE_INFINITY, builder, R.string.description_max_grade, "<br>");
assertEquals("Max grade: 0 %<br>", builder.toString()); assertEquals("Max grade: 0 %<br>", builder.toString());
} }
/**
* Tests {@link DescriptionGeneratorImpl#getPace(double)}.
*/
public void testGetPace() {
assertEquals(12.0, descriptionGenerator.getPace(5));
}
/**
* Tests {@link DescriptionGeneratorImpl#getPace(double)} with zero speed.
*/
public void testGetPace_zero() {
assertEquals(0.0, descriptionGenerator.getPace(0));
}
} }