diff --git a/MyTracks/libs/README.txt b/MyTracks/libs/README.txt index b303dcf14..35a849f96 100644 --- a/MyTracks/libs/README.txt +++ b/MyTracks/libs/README.txt @@ -7,7 +7,7 @@ Android Support Package: android-support-v4.jar (version 13) 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-api-client-1.12.0-beta.jar diff --git a/MyTracks/libs/antlib.jar b/MyTracks/libs/antlib.jar index b4ff8fe04..21d141ba9 100644 Binary files a/MyTracks/libs/antlib.jar and b/MyTracks/libs/antlib.jar differ diff --git a/MyTracks/res/values/strings.xml b/MyTracks/res/values/strings.xml index 61c35da82..90f50c968 100644 --- a/MyTracks/res/values/strings.xml +++ b/MyTracks/res/values/strings.xml @@ -384,9 +384,9 @@ limitations under the License. - Average moving pace: %1$.2f min/km (%2$.1f min/mi) + %1$s pace in minutes per kilometer + %2$s pace in minutes per mile"> + Average moving pace: %1$s min/km (%2$s min/mi) - Average pace: %1$.2f min/km (%2$.1f min/mi) + %1$s pace in minutes per kilometer + %2$s pace in minutes per mile"> + Average pace: %1$s min/km (%2$s min/mi) - Fastest pace: %1$.2f min/km (%2$.1f min/mi) + %1$s pace in minutes per kilometer + %2$s pace in minutes per mile"> + Fastest pace: %1$s min/km (%2$s min/mi) @@ -497,12 +497,12 @@ limitations under the License. Moving time: %1$s - Pace (hr/mi) + translation_description="Pace in imperial units, minutes per mile."> + Pace (min/mi) - Pace (hr/km) + translation_description="Pace in metric units, minutes per kilometer."> + Pace (min/km) Preferred rate @@ -2131,7 +2131,7 @@ limitations under the License. Moving time Pace diff --git a/MyTracks/src/com/google/android/apps/mytracks/ChartView.java b/MyTracks/src/com/google/android/apps/mytracks/ChartView.java index 86b12877d..a3db6dae2 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/ChartView.java +++ b/MyTracks/src/com/google/android/apps/mytracks/ChartView.java @@ -653,13 +653,14 @@ public class ChartView extends View { canvas.drawLine(x, y, x + effectiveWidth, y, axisPaint); String label = getXAxisLabel(); 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(); ArrayList markerPositions = getXAxisMarkerPositions(interval); NumberFormat numberFormat = interval < 1 ? X_FRACTION_FORMAT : X_NUMBER_FORMAT; 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 value value * @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) : StringUtils.formatElapsedTime((long) value); 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); } @@ -906,8 +909,11 @@ public class ChartView extends View { leftBorder = (int) (density * BORDER + markerLength); int[] titleDimensions = getTitleDimenions(); topBorder = (int) (density * BORDER + titleDimensions[0] * (titleDimensions[1] + spacer)); - bottomBorder = (int) (density * BORDER + getRect(xAxisMarkerPaint, "1").height() + spacer); - rightBorder = (int) (density * BORDER + getRect(axisPaint, getXAxisLabel()).width() + spacer); + Rect xAxisLabelRect = getRect(axisPaint, getXAxisLabel()); + // 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(); } diff --git a/MyTracks/src/com/google/android/apps/mytracks/SensorStateActivity.java b/MyTracks/src/com/google/android/apps/mytracks/SensorStateActivity.java index 5edb7c59d..0c5848c07 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/SensorStateActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/SensorStateActivity.java @@ -27,14 +27,12 @@ import com.google.android.maps.mytracks.R; import com.google.protobuf.InvalidProtocolBufferException; import android.os.Bundle; +import android.os.Handler; import android.os.RemoteException; import android.text.format.DateFormat; import android.util.Log; import android.widget.TextView; -import java.util.Timer; -import java.util.TimerTask; - /** * An activity that displays information about sensors. * @@ -43,12 +41,14 @@ import java.util.TimerTask; public class SensorStateActivity extends AbstractMyTracksActivity { private static final String TAG = SensorStateActivity.class.getName(); - private static final long REFRESH_PERIOD_MS = 250; + private static final long ONE_SECOND = 1000; - /** - * A Runnable to update the UI. - */ - private final Runnable updateRunnable = new Runnable() { + private TrackRecordingServiceConnection trackRecordingServiceConnection; + private Handler handler; + private SensorManager tempSensorManager; + + private final Runnable updateUiRunnable = new Runnable() { + @Override public void run() { ITrackRecordingService trackRecordingService = trackRecordingServiceConnection .getServiceIfBound(); @@ -69,30 +69,15 @@ public class SensorStateActivity extends AbstractMyTracksActivity { stopTempSensorManager(); 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 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null); + handler = new Handler(); } @Override @@ -104,18 +89,13 @@ public class SensorStateActivity extends AbstractMyTracksActivity { @Override protected void onResume() { super.onResume(); - isVisible = true; - timer = new Timer(); - timer.schedule(new UpdateTimerTask(), 0, REFRESH_PERIOD_MS); + handler.post(updateUiRunnable); } @Override protected void onPause() { super.onPause(); - isVisible = false; - timer.cancel(); - timer.purge(); - timer = null; + handler.removeCallbacks(updateUiRunnable); stopTempSensorManager(); } diff --git a/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java b/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java index e1b16c239..18fdb13cf 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java @@ -367,7 +367,8 @@ public class TrackListActivity extends AbstractSendToGoogleActivity @Override public Loader onCreateLoader(int arg0, Bundle arg1) { 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 diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/DescriptionGeneratorImpl.java b/MyTracks/src/com/google/android/apps/mytracks/content/DescriptionGeneratorImpl.java index e647dc4f2..42cbeae64 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/content/DescriptionGeneratorImpl.java +++ b/MyTracks/src/com/google/android/apps/mytracks/content/DescriptionGeneratorImpl.java @@ -24,7 +24,6 @@ import com.google.android.maps.mytracks.R; import com.google.common.annotations.VisibleForTesting; import android.content.Context; -import android.util.Pair; import java.util.Vector; @@ -117,25 +116,24 @@ public class DescriptionGeneratorImpl implements DescriptionGenerator { writeTime(stats.getMovingTime(), builder, R.string.description_moving_time, lineBreak); // Average speed - Pair averageSpeed = writeSpeed( - stats.getAverageSpeed(), builder, R.string.description_average_speed, lineBreak); + writeSpeed(stats.getAverageSpeed(), builder, R.string.description_average_speed, lineBreak); // Average moving speed - Pair averageMovingSpeed = writeSpeed(stats.getAverageMovingSpeed(), builder, - R.string.description_average_moving_speed, lineBreak); + writeSpeed(stats.getAverageMovingSpeed(), builder, R.string.description_average_moving_speed, + lineBreak); // Max speed - Pair maxSpeed = writeSpeed( - stats.getMaxSpeed(), builder, R.string.description_max_speed, lineBreak); + writeSpeed(stats.getMaxSpeed(), builder, R.string.description_max_speed, lineBreak); // Average pace - writePace(averageSpeed, builder, R.string.description_average_pace, lineBreak); + writePace(stats.getAverageSpeed(), builder, R.string.description_average_pace, lineBreak); // 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 - writePace(maxSpeed, builder, R.string.description_fastest_pace, lineBreak); + writePace(stats.getMaxSpeed(), builder, R.string.description_fastest_pace, lineBreak); // Max elevation 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 resId resource id of speed string * @param lineBreak line break string - * @return a pair of speed, first in kilometers per hour, second in miles per - * hour. */ @VisibleForTesting - Pair writeSpeed( + void writeSpeed( double speed, StringBuilder builder, int resId, String lineBreak) { double speedInKmHr = speed * UnitConversions.MS_TO_KMH; double speedInMiHr = speedInKmHr * UnitConversions.KM_TO_MI; builder.append(context.getString(resId, speedInKmHr, speedInMiHr)); builder.append(lineBreak); - return Pair.create(speedInKmHr, speedInMiHr); } /** * Writes pace. - * - * @param speed a pair of speed, first in kilometers per hour, second in miles - * per hour + * + * @param speed speed in meters per second * @param builder StringBuilder to append pace * @param resId resource id of pace string * @param lineBreak line break string */ @VisibleForTesting - void writePace( - Pair speed, StringBuilder builder, int resId, String lineBreak) { - double paceInMinKm = getPace(speed.first); - double paceInMinMi = getPace(speed.second); - builder.append(context.getString(resId, paceInMinKm, paceInMinMi)); + void writePace(double speed, StringBuilder builder, int resId, String lineBreak) { + String[] paceInMetrics = StringUtils.getSpeedParts(context, speed, true, false); + String[] paceInImperial = StringUtils.getSpeedParts(context, speed, false, false); + + 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); } @@ -262,14 +264,4 @@ public class DescriptionGeneratorImpl implements DescriptionGenerator { builder.append(context.getString(resId, gradeInPercent)); 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 - } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/TrackDataHub.java b/MyTracks/src/com/google/android/apps/mytracks/content/TrackDataHub.java index 8ba075d6a..d596e4221 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/content/TrackDataHub.java +++ b/MyTracks/src/com/google/android/apps/mytracks/content/TrackDataHub.java @@ -134,9 +134,10 @@ public class TrackDataHub implements DataSourceListener { started = false; dataSourceManager.unregisterAllListeners(); - handlerThread.getLooper().quit(); - - handlerThread = null; + if (handlerThread != null) { + handlerThread.getLooper().quit(); + handlerThread = null; + } handler = null; dataSource = null; dataSourceManager = null; diff --git a/MyTracks/src/com/google/android/apps/mytracks/fragments/EulaDialogFragment.java b/MyTracks/src/com/google/android/apps/mytracks/fragments/EulaDialogFragment.java index 02e9316e8..737dcc4e5 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/fragments/EulaDialogFragment.java +++ b/MyTracks/src/com/google/android/apps/mytracks/fragments/EulaDialogFragment.java @@ -27,7 +27,11 @@ import android.content.DialogInterface; import android.os.Bundle; import android.support.v4.app.DialogFragment; 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.widget.TextView; import java.util.Locale; @@ -89,8 +93,12 @@ public class EulaDialogFragment extends DialogFragment { fragmentActivity = getActivity(); 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) { builder.setPositiveButton(R.string.generic_ok, null); @@ -117,10 +125,17 @@ public class EulaDialogFragment extends DialogFragment { caller.onEulaDone(); } }); - } + } return builder.create(); } + @Override + public void onStart() { + super.onStart(); + TextView textView = (TextView) getDialog().findViewById(android.R.id.message); + textView.setMovementMethod(LinkMovementMethod.getInstance()); + } + @Override public void onCancel(DialogInterface arg0) { caller.onEulaDone(); diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/fusiontables/SendFusionTablesAsyncTask.java b/MyTracks/src/com/google/android/apps/mytracks/io/fusiontables/SendFusionTablesAsyncTask.java index bf35f2a40..e49ace669 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/fusiontables/SendFusionTablesAsyncTask.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/fusiontables/SendFusionTablesAsyncTask.java @@ -237,7 +237,8 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask { int count = cursor.getCount(); List locations = new ArrayList(MAX_POINTS_PER_UPLOAD); - Location lastLocation = null; + Location lastValidLocation = null; + boolean sentStartMarker = false; for (int i = 0; i < count; i++) { cursor.moveToPosition(i); @@ -245,14 +246,15 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask { Location location = myTracksProviderUtils.createTrackPoint(cursor); locations.add(location); - if (i == 0) { - // Create a start marker - String name = context.getString(R.string.marker_label_start, track.getName()); - createNewPoint(fusiontables, tableId, name, "", location, MARKER_TYPE_START); + if (LocationUtils.isValidLocation(location)) { + lastValidLocation = location; } - if (LocationUtils.isValidLocation(location)) { - lastLocation = location; + if (!sentStartMarker && lastValidLocation != null) { + // 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 @@ -274,11 +276,12 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask { } // Create an end marker - if (lastLocation != null) { + if (lastValidLocation != null) { String name = context.getString(R.string.marker_label_end, track.getName()); DescriptionGenerator descriptionGenerator = new DescriptionGeneratorImpl(context); 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; } finally { diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/maps/SendMapsAsyncTask.java b/MyTracks/src/com/google/android/apps/mytracks/io/maps/SendMapsAsyncTask.java index 11a5fb040..f0d8503c9 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/maps/SendMapsAsyncTask.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/maps/SendMapsAsyncTask.java @@ -244,7 +244,8 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask { int count = cursor.getCount(); List locations = new ArrayList(MAX_POINTS_PER_UPLOAD); - Location lastLocation = null; + Location lastValidLocation = null; + boolean sentStartMarker = false; // For chart server, limit the number of elevation readings to 250. int elevationSamplingFrequency = Math.max(1, (int) (count / 250.0)); @@ -261,13 +262,18 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask { Location location = myTracksProviderUtils.createTrackPoint(cursor); locations.add(location); - if (i == 0) { + if (LocationUtils.isValidLocation(location)) { + lastValidLocation = location; + } + + if (!sentStartMarker && lastValidLocation != null) { // Create a start marker 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"); return false; } + sentStartMarker = true; } tripStatisticsUpdater.addLocation(location, minRecordingDistance); @@ -275,9 +281,6 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask { distances.add(tripStatisticsUpdater.getTripStatistics().getTotalDistance()); elevations.add(tripStatisticsUpdater.getSmoothedElevation()); } - if (LocationUtils.isValidLocation(location)) { - lastLocation = location; - } // Upload periodically int readCount = i + 1; @@ -298,14 +301,14 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask { } // Create an end marker - if (lastLocation != null) { + if (lastValidLocation != null) { distances.add(tripStatisticsUpdater.getTripStatistics().getTotalDistance()); elevations.add(tripStatisticsUpdater.getSmoothedElevation()); DescriptionGenerator descriptionGenerator = new DescriptionGeneratorImpl(context); track.setDescription( descriptionGenerator.generateTrackDescription(track, distances, elevations, true)); 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"); return false; } diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java index 78bff4bbc..6e49b2564 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java @@ -133,8 +133,9 @@ public class TrackRecordingService extends Service { private Location lastLocation; private boolean currentSegmentHasLocation; - // Timer to periodically invoke checkLocationListener - private final Timer timer = new Timer(); + // TimerTask to register location listener + private TimerTask timerTask; + private Timer timer; // Handler for the timer to post a runnable to the main thread 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() { @Override public void onDisconnected() {} @@ -301,10 +293,19 @@ public class TrackRecordingService extends Service { // onSharedPreferenceChanged might not set recordingTrackId. recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; - // Require announcementExecutor and splitExecutor to be created. + // Require voiceExecutor and splitExecutor to be created. 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 @@ -357,10 +358,15 @@ public class TrackRecordingService extends Service { showNotification(false); sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); - checkLocationListener.cancel(); - checkLocationListener = null; - timer.cancel(); - timer.purge(); + if (timerTask != null) { + timerTask.cancel(); + timerTask = null; + } + if (timer != null) { + timer.cancel(); + timer.purge(); + timer = null; + } unregisterLocationListener(); try { diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/SensorManager.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/SensorManager.java index 8b3787abf..36bcacde3 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/SensorManager.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/SensorManager.java @@ -39,30 +39,8 @@ public abstract class SensorManager { private SensorState sensorState = SensorState.NONE; private long sensorStateTimestamp = System.currentTimeMillis(); - /** - * A time task to check sensor connection. - */ - 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(); + private TimerTask timerTask; + private Timer timer; /** * Returns true if the sensor is enabled. @@ -89,14 +67,43 @@ public abstract class SensorManager { */ public void startSensor() { 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. */ public void stopSensor() { - timer.cancel(); + if (timerTask != null) { + timerTask.cancel(); + timerTask = null; + } + if (timer != null) { + timer.cancel(); + timer.purge(); + timer = null; + } tearDownChannel(); } diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/tasks/TimerTaskExecutor.java b/MyTracks/src/com/google/android/apps/mytracks/services/tasks/TimerTaskExecutor.java index 50bcd85af..90021a233 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/tasks/TimerTaskExecutor.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/tasks/TimerTaskExecutor.java @@ -33,10 +33,7 @@ public class TimerTaskExecutor { private final PeriodicTask periodicTask; private final TrackRecordingService trackRecordingService; - /** - * A timer to schedule the announcements. This is non-null if the task is in - * started (scheduled) state. - */ + private TimerTask timerTask; private Timer timer; public TimerTaskExecutor(PeriodicTask periodicTask, TrackRecordingService trackRecordingService) { @@ -50,6 +47,10 @@ public class TimerTaskExecutor { * @param interval the interval in milliseconds */ public void scheduleTask(long interval) { + if (interval <= 0) { + return; + } + if (!trackRecordingService.isRecording() || trackRecordingService.isPaused()) { return; } @@ -59,43 +60,32 @@ public class TimerTaskExecutor { return; } - if (timer != null) { - timer.cancel(); - timer.purge(); - } else { - // First start, or we were previously shut down. - periodicTask.start(); - } - - timer = new Timer(); - if (interval <= 0) { - return; - } - + shutdown(); + periodicTask.start(); + timerTask = new TimerTask() { + @Override + public void run() { + periodicTask.run(trackRecordingService); + } + }; + timer = new Timer("TimerTaskExecutorTimer"); 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. */ public void shutdown() { + if (timerTask != null) { + timerTask.cancel(); + timerTask = null; + } if (timer != null) { timer.cancel(); timer.purge(); 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(); } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java index 0e09a817e..9771fbbf0 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java @@ -213,19 +213,18 @@ public class StringUtils { return result; } speed *= UnitConversions.MS_TO_KMH; - if (metricUnits) { - if (!reportSpeed) { - // convert from hours to minutes - speed = speed == 0 ? 0.0 : 60.0 / speed; - } - } else { + if (!metricUnits) { 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; } diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/content/DescriptionGeneratorImplTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/content/DescriptionGeneratorImplTest.java index 245e5f3bd..d2d672693 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/content/DescriptionGeneratorImplTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/content/DescriptionGeneratorImplTest.java @@ -68,9 +68,9 @@ public class DescriptionGeneratorImplTest extends AndroidTestCase { + "Average speed: 120.00 km/h (74.6 mi/h)
" + "Average moving speed: 240.00 km/h (149.1 mi/h)
" + "Max speed: 360.00 km/h (223.7 mi/h)
" - + "Average pace: 0.50 min/km (0.8 min/mi)
" - + "Average moving pace: 0.25 min/km (0.4 min/mi)
" - + "Fastest pace: 0.17 min/km (0.3 min/mi)
" + + "Average pace: 0:30 min/km (0:48 min/mi)
" + + "Average moving pace: 0:15 min/km (0:24 min/mi)
" + + "Fastest pace: 0:10 min/km (0:16 min/mi)
" + "Max elevation: 550 m (1804 ft)
" + "Min elevation: -500 m (-1640 ft)
" + "Elevation gain: 6000 m (19685 ft)
" @@ -103,9 +103,9 @@ public class DescriptionGeneratorImplTest extends AndroidTestCase { + "Average speed: 120.00 km/h (74.6 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" - + "Average pace: 0.50 min/km (0.8 min/mi)\n" - + "Average moving pace: 0.25 min/km (0.4 min/mi)\n" - + "Fastest pace: 0.17 min/km (0.3 min/mi)\n" + + "Average pace: 0:30 min/km (0:48 min/mi)\n" + + "Average moving pace: 0:15 min/km (0:24 min/mi)\n" + + "Fastest pace: 0:10 min/km (0:16 min/mi)\n" + "Max elevation: 550 m (1804 ft)\n" + "Min elevation: -500 m (-1640 ft)\n" + "Elevation gain: 6000 m (19685 ft)\n" @@ -162,9 +162,8 @@ public class DescriptionGeneratorImplTest extends AndroidTestCase { */ public void testWritePace() { StringBuilder builder = new StringBuilder(); - descriptionGenerator.writePace( - new Pair(1.1, 2.2), builder, R.string.description_average_pace, "\n"); - assertEquals("Average pace: 54.55 min/km (27.3 min/mi)\n", builder.toString()); + descriptionGenerator.writePace(1.1, builder, R.string.description_average_pace, "\n"); + assertEquals("Average pace: 15:09 min/km (24:23 min/mi)\n", builder.toString()); } /** @@ -197,18 +196,4 @@ public class DescriptionGeneratorImplTest extends AndroidTestCase { Double.POSITIVE_INFINITY, builder, R.string.description_max_grade, "
"); assertEquals("Max grade: 0 %
", 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)); - } }