Update the ui thread in StatsFragment to support pause/resume

for the total time.
This commit is contained in:
Jimmy Shih
2012-08-22 13:16:18 -07:00
parent ccaf652c8b
commit b3ad974bbb
10 changed files with 147 additions and 106 deletions
@@ -82,9 +82,11 @@ public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
myTracksProviderUtils = MyTracksProviderUtils.Factory.get(importActivity);
// Get the wake lock if not recording
// Get the wake lock if not recording or paused
if (PreferencesUtils.getLong(importActivity, R.string.recording_track_id_key)
== PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) {
== PreferencesUtils.RECORDING_TRACK_ID_DEFAULT || PreferencesUtils.getBoolean(
importActivity, R.string.recording_track_paused_key,
PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT)) {
wakeLock = SystemUtils.acquireWakeLock(importActivity, wakeLock);
}
@@ -70,6 +70,8 @@ public class TrackDataHub implements DataSourceListener {
// Preference values
private long selectedTrackId;
private long recordingTrackId;
private boolean recordingTrackPaused;
private boolean metricUnits;
private boolean reportSpeed;
private int minRequiredAccuracy;
@@ -132,17 +134,11 @@ public class TrackDataHub implements DataSourceListener {
dataSource = newDataSource();
dataSourceManager = new DataSourceManager(dataSource, this);
notifyPreferenceChanged(null);
runInHanderThread(new Runnable() {
@Override
public void run() {
dataSourceManager.updateListeners(trackDataManager.getRegisteredTrackDataTypes());
selectedTrackId = PreferencesUtils.getLong(context, R.string.selected_track_id_key);
metricUnits = PreferencesUtils.getBoolean(
context, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT);
reportSpeed = PreferencesUtils.getBoolean(
context, R.string.report_speed_key, PreferencesUtils.REPORT_SPEED_DEFAULT);
minRequiredAccuracy = PreferencesUtils.getInt(context, R.string.min_required_accuracy_key,
PreferencesUtils.MIN_REQUIRED_ACCURACY_DEFAULT);
loadDataForAll();
}
});
@@ -251,9 +247,15 @@ public class TrackDataHub implements DataSourceListener {
* Returns true if the selected track is recording.
*/
public boolean isSelectedTrackRecording() {
long recordingTrackId = PreferencesUtils.getLong(context, R.string.recording_track_id_key);
return recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT
&& recordingTrackId == selectedTrackId;
return selectedTrackId == recordingTrackId
&& recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
}
/**
* Returns true if the selected track is paused.
*/
public boolean isSelectedTrackPaused() {
return selectedTrackId == recordingTrackId && recordingTrackPaused;
}
@Override
@@ -293,37 +295,57 @@ public class TrackDataHub implements DataSourceListener {
runInHanderThread(new Runnable() {
@Override
public void run() {
if (PreferencesUtils.getKey(context, R.string.min_required_accuracy_key).equals(key)) {
minRequiredAccuracy = PreferencesUtils.getInt(context, R.string.min_required_accuracy_key,
PreferencesUtils.MIN_REQUIRED_ACCURACY_DEFAULT);
} else if (PreferencesUtils.getKey(context, R.string.metric_units_key).equals(key)) {
if (key == null
|| key.equals(PreferencesUtils.getKey(context, R.string.selected_track_id_key))) {
long trackId = PreferencesUtils.getLong(context, R.string.selected_track_id_key);
boolean hasChanged = trackId != selectedTrackId;
selectedTrackId = trackId;
if (key != null) {
if (hasChanged) {
loadDataForAll();
}
}
}
if (key == null
|| key.equals(PreferencesUtils.getKey(context, R.string.recording_track_id_key))) {
recordingTrackId = PreferencesUtils.getLong(context, R.string.recording_track_id_key);
}
if (key == null
|| key.equals(PreferencesUtils.getKey(context, R.string.recording_track_paused_key))) {
recordingTrackPaused = PreferencesUtils.getBoolean(
context, R.string.recording_track_paused_key,
PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
}
if (key == null
|| key.equals(PreferencesUtils.getKey(context, R.string.metric_units_key))) {
metricUnits = PreferencesUtils.getBoolean(
context, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT);
Set<TrackDataListener> trackDataListeners = trackDataManager.getListeners(
TrackDataType.PREFERENCE);
for (TrackDataListener trackDataListener : trackDataListeners) {
if (trackDataListener.onMetricUnitsChanged(metricUnits)) {
loadDataForListener(trackDataListener);
if (key != null) {
for (TrackDataListener trackDataListener :
trackDataManager.getListeners(TrackDataType.PREFERENCE)) {
if (trackDataListener.onMetricUnitsChanged(metricUnits)) {
loadDataForListener(trackDataListener);
}
}
}
} else if (PreferencesUtils.getKey(context, R.string.report_speed_key).equals(key)) {
}
if (key == null
|| key.equals(PreferencesUtils.getKey(context, R.string.report_speed_key))) {
reportSpeed = PreferencesUtils.getBoolean(
context, R.string.report_speed_key, PreferencesUtils.REPORT_SPEED_DEFAULT);
Set<TrackDataListener> trackDataListeners = trackDataManager.getListeners(
TrackDataType.PREFERENCE);
for (TrackDataListener trackDataListener : trackDataListeners) {
if (trackDataListener.onReportSpeedChanged(reportSpeed)) {
loadDataForListener(trackDataListener);
if (key != null) {
for (TrackDataListener trackDataListener :
trackDataManager.getListeners(TrackDataType.PREFERENCE)) {
if (trackDataListener.onReportSpeedChanged(reportSpeed)) {
loadDataForListener(trackDataListener);
}
}
}
} else if (PreferencesUtils.getKey(context, R.string.selected_track_id_key).equals(key)) {
long trackId = PreferencesUtils.getLong(context, R.string.selected_track_id_key);
if (trackId == selectedTrackId) {
Log.i(TAG, "Not reloading track " + trackId);
return;
}
selectedTrackId = trackId;
loadDataForAll();
}
if (key == null
|| key.equals(PreferencesUtils.getKey(context, R.string.min_required_accuracy_key))) {
minRequiredAccuracy = PreferencesUtils.getInt(context, R.string.min_required_accuracy_key,
PreferencesUtils.MIN_REQUIRED_ACCURACY_DEFAULT);
}
}
});
@@ -487,7 +509,7 @@ public class TrackDataHub implements DataSourceListener {
}
Track track = myTracksProviderUtils.getTrack(selectedTrackId);
for (TrackDataListener trackDataListener : trackDataListeners) {
trackDataListener.onSelectedTrackChanged(track, isSelectedTrackRecording());
trackDataListener.onSelectedTrackChanged(track);
}
}
@@ -64,9 +64,8 @@ public interface TrackDataListener {
* calls to clear the current data).
*
* @param track the selected track or null if no track is selected
* @param isRecording true if the selected track is recording
*/
public void onSelectedTrackChanged(Track track, boolean isRecording);
public void onSelectedTrackChanged(Track track);
/**
* Called when the track or its statistics has been updated.
@@ -179,7 +179,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
}
@Override
public void onSelectedTrackChanged(Track track, boolean isRecording) {
public void onSelectedTrackChanged(Track track) {
// We don't care.
}
@@ -334,7 +334,7 @@ public class MapFragment extends Fragment
}
@Override
public void onSelectedTrackChanged(final Track track, final boolean isRecording) {
public void onSelectedTrackChanged(final Track track) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
@@ -350,7 +350,7 @@ public class MapFragment extends Fragment
currentSelectedTrackId = track.getId();
updateMap(track);
}
mapOverlay.setShowEndMarker(!isRecording);
mapOverlay.setShowEndMarker(!isSelectedTrackRecording());
}
mapView.invalidate();
}
@@ -452,6 +452,14 @@ public class MapFragment extends Fragment
}
}
/**
* Returns true if the selected track is recording. Needs to be synchronized
* because trackDataHub can be accessed by multiple threads.
*/
private synchronized boolean isSelectedTrackRecording() {
return trackDataHub != null && trackDataHub.isSelectedTrackRecording();
}
/**
* Updates the map by either zooming to the requested marker or showing the track.
*
@@ -23,7 +23,6 @@ import com.google.android.apps.mytracks.content.TrackDataListener;
import com.google.android.apps.mytracks.content.TrackDataType;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.StatsUtils;
import com.google.android.maps.mytracks.R;
@@ -39,30 +38,28 @@ import java.util.EnumSet;
/**
* A fragment to display track statistics to the user.
*
*
* @author Sandor Dornbush
* @author Rodrigo Damazio
*/
public class StatsFragment extends Fragment implements TrackDataListener {
public static final String STATS_FRAGMENT_TAG = "statsFragment";
private static final String TAG = StatsFragment.class.getSimpleName();
private TrackDataHub trackDataHub;
private UiUpdateThread uiUpdateThread;
// The start time of the current track.
private long startTime = -1L;
private Location lastLocation = null;
private TripStatistics lastTripStatistics = null;
// A runnable to update the total time field.
private final Runnable updateTotalTime = new Runnable() {
public void run() {
if (isSelectedTrackRecording()) {
StatsUtils.setTotalTimeValue(getActivity(), System.currentTimeMillis() - startTime);
if (isSelectedTrackRecording() && !isSelectedTrackPaused() && lastTripStatistics != null) {
StatsUtils.setTotalTimeValue(getActivity(), System.currentTimeMillis()
- lastTripStatistics.getStopTime() + lastTripStatistics.getTotalTime());
}
}
};
@@ -74,8 +71,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
@Override
public void run() {
Log.d(TAG, "UI update thread started");
while (PreferencesUtils.getLong(getActivity(), R.string.recording_track_id_key)
!= PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) {
while (true) {
getActivity().runOnUiThread(updateTotalTime);
try {
Thread.sleep(1000L);
@@ -133,7 +129,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
public void onLocationChanged(final Location location) {
if (isResumed() && isSelectedTrackRecording()) {
getActivity().runOnUiThread(new Runnable() {
@Override
@Override
public void run() {
lastLocation = location;
StatsUtils.setLocationValues(getActivity(), lastLocation, true);
@@ -148,8 +144,9 @@ public class StatsFragment extends Fragment implements TrackDataListener {
}
@Override
public void onSelectedTrackChanged(Track track, boolean isRecording) {
public void onSelectedTrackChanged(Track track) {
if (isResumed()) {
boolean isRecording = isSelectedTrackRecording();
if (uiUpdateThread == null && isRecording) {
uiUpdateThread = new UiUpdateThread();
uiUpdateThread.start();
@@ -164,7 +161,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
public void onTrackUpdated(final Track track) {
if (isResumed()) {
getActivity().runOnUiThread(new Runnable() {
@Override
@Override
public void run() {
if (track == null || track.getTripStatistics() == null) {
lastLocation = null;
@@ -173,8 +170,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
return;
}
lastTripStatistics = track.getTripStatistics();
startTime = track.getTripStatistics().getStartTime();
if (!isSelectedTrackRecording()) {
lastLocation = null;
}
@@ -228,7 +224,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
public boolean onMetricUnitsChanged(final boolean metric) {
if (isResumed()) {
getActivity().runOnUiThread(new Runnable() {
@Override
@Override
public void run() {
updateUi();
}
@@ -280,6 +276,14 @@ public class StatsFragment extends Fragment implements TrackDataListener {
return trackDataHub != null && trackDataHub.isSelectedTrackRecording();
}
/**
* Returns true if the selected track is paused. Needs to be synchronized
* because trackDataHub can be accessed by multiple threads.
*/
private synchronized boolean isSelectedTrackPaused() {
return trackDataHub != null && trackDataHub.isSelectedTrackPaused();
}
/**
* Updates the UI.
*/
@@ -81,9 +81,10 @@ public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
myTracksProviderUtils = MyTracksProviderUtils.Factory.get(saveActivity);
// Get the wake lock if not recording
// Get the wake lock if not recording or paused
if (PreferencesUtils.getLong(saveActivity, R.string.recording_track_id_key)
== PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) {
== PreferencesUtils.RECORDING_TRACK_ID_DEFAULT || PreferencesUtils.getBoolean(saveActivity,
R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT)) {
wakeLock = SystemUtils.acquireWakeLock(saveActivity, wakeLock);
}
@@ -570,8 +570,8 @@ public class TrackRecordingService extends Service {
PreferencesUtils.setInt(this, R.string.auto_resume_track_current_retry_key, 0);
// Update database
track.setId(recordingTrackId);
track.setName(TrackNameUtils.getTrackName(this, recordingTrackId, now, null));
track.setId(trackId);
track.setName(TrackNameUtils.getTrackName(this, trackId, now, null));
track.setCategory(PreferencesUtils.getString(
this, R.string.default_activity_key, PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT));
track.setTripStatistics(trackTripStatisticsBuilder.getTripStatistics());
@@ -579,7 +579,7 @@ public class TrackRecordingService extends Service {
insertWaypoint(WaypointCreationRequest.DEFAULT_START_TRACK);
startRecording(true);
return recordingTrackId;
return trackId;
}
/**
@@ -644,11 +644,14 @@ public class TrackRecordingService extends Service {
PreferencesUtils.setBoolean(this, R.string.recording_track_paused_key, false);
// Update database
Location resume = new Location(LocationManager.GPS_PROVIDER);
resume.setLongitude(0);
resume.setLatitude(RESUME_LATITUDE);
resume.setTime(System.currentTimeMillis());
insertLocation(resume, null, null);
Track track = myTracksProviderUtils.getTrack(recordingTrackId);
if (track != null) {
Location resume = new Location(LocationManager.GPS_PROVIDER);
resume.setLongitude(0);
resume.setLatitude(RESUME_LATITUDE);
resume.setTime(System.currentTimeMillis());
insertLocation(track, resume, null);
}
startRecording(false);
}
@@ -696,13 +699,10 @@ public class TrackRecordingService extends Service {
updateRecordingState(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, true);
// Update database
insertLocation(lastLocation, getSensorDataSet(),
myTracksProviderUtils.getLastTrackLocation(recordingTrackId));
Track track = myTracksProviderUtils.getTrack(recordingTrackId);
Track track = myTracksProviderUtils.getTrack(trackId);
if (track != null) {
updateRecordingTrack(
track, myTracksProviderUtils.getLastTrackLocationId(recordingTrackId), false);
insertLocation(track, lastLocation, myTracksProviderUtils.getLastTrackLocation(trackId));
updateRecordingTrack(track, myTracksProviderUtils.getLastTrackLocationId(trackId), false);
}
endRecording(true, trackId);
@@ -723,14 +723,17 @@ public class TrackRecordingService extends Service {
PreferencesUtils.setBoolean(this, R.string.recording_track_paused_key, true);
// Update database
insertLocation(lastLocation, getSensorDataSet(),
myTracksProviderUtils.getLastTrackLocation(recordingTrackId));
Track track = myTracksProviderUtils.getTrack(recordingTrackId);
if (track != null) {
insertLocation(
track, lastLocation, myTracksProviderUtils.getLastTrackLocation(track.getId()));
Location pause = new Location(LocationManager.GPS_PROVIDER);
pause.setLongitude(0);
pause.setLatitude(PAUSE_LATITUDE);
pause.setTime(System.currentTimeMillis());
insertLocation(pause, null, null);
Location pause = new Location(LocationManager.GPS_PROVIDER);
pause.setLongitude(0);
pause.setLatitude(PAUSE_LATITUDE);
pause.setTime(System.currentTimeMillis());
insertLocation(track, pause, null);
}
endRecording(false, recordingTrackId);
}
@@ -739,7 +742,7 @@ public class TrackRecordingService extends Service {
* Common code for ending a track or pausing a track.
*
* @param trackStopped true if track is stopped, false if track is paused
* @param trackId
* @param trackId the track id
*/
private void endRecording(boolean trackStopped, long trackId) {
@@ -773,7 +776,7 @@ public class TrackRecordingService extends Service {
*/
private void updateRecordingState(long trackId, boolean paused) {
recordingTrackId = trackId;
PreferencesUtils.setLong(this, R.string.recording_track_id_key, recordingTrackId);
PreferencesUtils.setLong(this, R.string.recording_track_id_key, trackId);
recordingTrackPaused = paused;
PreferencesUtils.setBoolean(this, R.string.recording_track_paused_key, recordingTrackPaused);
}
@@ -790,6 +793,12 @@ public class TrackRecordingService extends Service {
return;
}
Track track = myTracksProviderUtils.getTrack(recordingTrackId);
if (track == null) {
Log.w(TAG, "Ignore onLocationChangedAsync. No track.");
return;
}
if (!LocationUtils.isValidLocation(location)) {
Log.w(TAG, "Ignore onLocationChangedAsync. location is invalid.");
return;
@@ -800,20 +809,23 @@ public class TrackRecordingService extends Service {
return;
}
Location lastTrackLocation = myTracksProviderUtils.getLastTrackLocation(recordingTrackId);
SensorDataSet sensorDataSet = getSensorDataSet();
Location lastTrackLocation = myTracksProviderUtils.getLastTrackLocation(track.getId());
long idleTime = currentSegmentHasLocation && LocationUtils.isValidLocation(lastTrackLocation) ?
location.getTime() - lastTrackLocation.getTime()
: 0;
: 0L;
locationListenerPolicy.updateIdleTime(idleTime);
if (currentRecordingInterval != locationListenerPolicy.getDesiredPollingInterval()) {
registerLocationListener();
}
SensorDataSet sensorDataSet = getSensorDataSet();
if (sensorDataSet != null) {
location = new MyTracksLocation(location, sensorDataSet);
}
// Always insert the first segment location
if (!currentSegmentHasLocation) {
insertLocation(location, sensorDataSet, lastTrackLocation);
insertLocation(track, location, lastTrackLocation);
currentSegmentHasLocation = true;
lastLocation = location;
return;
@@ -824,7 +836,7 @@ public class TrackRecordingService extends Service {
* Should not happen. The current segment should have a location. Just
* insert the current location.
*/
insertLocation(location, sensorDataSet, lastTrackLocation);
insertLocation(track, location, lastTrackLocation);
lastLocation = location;
return;
}
@@ -833,21 +845,21 @@ public class TrackRecordingService extends Service {
if (distanceToLastTrackLocation < minRecordingDistance && sensorDataSet == null) {
// skip
} else if (distanceToLastTrackLocation > maxRecordingDistance) {
insertLocation(lastLocation, sensorDataSet, lastTrackLocation);
insertLocation(track, lastLocation, lastTrackLocation);
Location pause = new Location(LocationManager.GPS_PROVIDER);
pause.setLongitude(0);
pause.setLatitude(PAUSE_LATITUDE);
pause.setTime(lastLocation.getTime());
insertLocation(pause, null, null);
insertLocation(track, pause, null);
insertLocation(location, sensorDataSet, lastTrackLocation);
insertLocation(track, location, lastTrackLocation);
} else {
/*
* (distanceToLastTrackLocation >= minRecordingDistance ||
* hasSensorData) && distanceToLastTrackLocation <= maxRecordingDistance
*/
insertLocation(lastLocation, sensorDataSet, lastTrackLocation);
insertLocation(location, sensorDataSet, lastTrackLocation);
insertLocation(track, lastLocation, lastTrackLocation);
insertLocation(track, location, lastTrackLocation);
}
lastLocation = location;
} catch (Error e) {
@@ -859,12 +871,7 @@ public class TrackRecordingService extends Service {
}
}
private void insertLocation(
Location location, SensorDataSet sensorDataSet, Location lastRecordedLocation) {
Track track = myTracksProviderUtils.getTrack(recordingTrackId);
if (track == null) {
return;
}
private void insertLocation(Track track, Location location, Location lastRecordedLocation) {
if (location == null) {
return;
}
@@ -873,10 +880,7 @@ public class TrackRecordingService extends Service {
}
try {
if (sensorDataSet != null) {
location = new MyTracksLocation(location, sensorDataSet);
}
Uri uri = myTracksProviderUtils.insertTrackPoint(location, recordingTrackId);
Uri uri = myTracksProviderUtils.insertTrackPoint(location, track.getId());
long trackPointId = Long.parseLong(uri.getLastPathSegment());
trackTripStatisticsBuilder.addLocation(location, minRecordingDistance);
markerTripStatisticsBuilder.addLocation(location, minRecordingDistance);
@@ -159,7 +159,8 @@ public class PeriodicTaskExecutor {
* Calculates the next distance for the distance periodic task.
*/
private void calculateNextTaskDistance() {
if (!trackRecordingService.isRecording() || trackRecordingService.isPaused() || periodicTask == null) {
if (!trackRecordingService.isRecording() || trackRecordingService.isPaused()
|| periodicTask == null) {
return;
}
@@ -798,7 +798,7 @@ public class TrackDataHubTest extends AndroidTestCase {
expect(myTracksProviderUtils.getTrack(capture(new Capture<Long>()))).andReturn(track);
// Make the track id is unique.
PreferencesUtils.setLong(context, R.string.recording_track_id_key, System.currentTimeMillis());
trackDataListener1.onSelectedTrackChanged(track, false);
trackDataListener1.onSelectedTrackChanged(track);
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.SELECTED_TRACK));