forked from upstream-mirrors/OpenTracks
TrackRecordingService uses LiveData to transfer data.
Replaces pull from UI layer. Fixes #173.
This commit is contained in:
@@ -31,8 +31,6 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import de.dennisguse.opentracks.databinding.ControllerFragmentBinding;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
@@ -49,10 +47,6 @@ public class ControllerFragment extends Fragment implements View.OnTouchListener
|
||||
|
||||
private static final String TAG = ControllerFragment.class.getSimpleName();
|
||||
|
||||
@Deprecated
|
||||
//TODO TrackRecordingService should provide LifeData for time (replace internal timer)
|
||||
private static final Duration UI_UPDATE_INTERVAL = Duration.ofSeconds(1);
|
||||
@Deprecated
|
||||
private Handler handlerUpdateTotalTime;
|
||||
|
||||
private Runnable buttonDelay;
|
||||
@@ -63,7 +57,7 @@ public class ControllerFragment extends Fragment implements View.OnTouchListener
|
||||
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private TrackRecordingService.RecordingStatus recordingStatus;
|
||||
private Duration totalTime;
|
||||
private TrackRecordingService.RecordingData recordingData;
|
||||
|
||||
private final Runnable bindChangedCallback = new Runnable() {
|
||||
@Override
|
||||
@@ -76,18 +70,9 @@ public class ControllerFragment extends Fragment implements View.OnTouchListener
|
||||
|
||||
service.getRecordingStatusObservable()
|
||||
.observe(ControllerFragment.this, status -> onRecordingStatusChanged(status));
|
||||
}
|
||||
};
|
||||
|
||||
// A runnable to update the total time.
|
||||
@Deprecated
|
||||
private final Runnable updateTotalTimeRunnable = new Runnable() {
|
||||
public void run() {
|
||||
if (isResumed() && recordingStatus.isRecordingAndNotPaused()) {
|
||||
updateTotalTime();
|
||||
setTotalTime();
|
||||
handlerUpdateTotalTime.postDelayed(this, UI_UPDATE_INTERVAL.toMillis());
|
||||
}
|
||||
service.getRecordingDataObservable()
|
||||
.observe(ControllerFragment.this, recordingData -> onTotalTimeChanged(recordingData));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -129,12 +114,6 @@ public class ControllerFragment extends Fragment implements View.OnTouchListener
|
||||
trackRecordingServiceConnection.startConnection(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
@@ -239,19 +218,6 @@ public class ControllerFragment extends Fragment implements View.OnTouchListener
|
||||
viewBinding.controllerContainer.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void updateTotalTime() {
|
||||
TrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||
if (trackRecordingService != null) {
|
||||
totalTime = trackRecordingService.getTotalTime();
|
||||
}
|
||||
}
|
||||
|
||||
private void setTotalTime() {
|
||||
if (totalTime != null) {
|
||||
viewBinding.controllerTotalTime.setText(StringUtils.formatElapsedTimeWithHour(totalTime));
|
||||
}
|
||||
}
|
||||
|
||||
private void showStatusSetDefaultText() {
|
||||
viewBinding.controllerStatus.setText(recordingStatus.isPaused() ? R.string.generic_paused : R.string.generic_recording);
|
||||
}
|
||||
@@ -271,13 +237,6 @@ public class ControllerFragment extends Fragment implements View.OnTouchListener
|
||||
private void onRecordingStatusChanged(TrackRecordingService.RecordingStatus status) {
|
||||
recordingStatus = status;
|
||||
|
||||
if (!recordingStatus.isRecording()) {
|
||||
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
||||
}
|
||||
if (recordingStatus.isRecordingAndNotPaused()) {
|
||||
handlerUpdateTotalTime.postDelayed(updateTotalTimeRunnable, UI_UPDATE_INTERVAL.toMillis());
|
||||
}
|
||||
|
||||
viewBinding.controllerRecord.setImageResource(recordingStatus.isRecordingAndNotPaused() ? R.drawable.ic_button_pause : R.drawable.button_record);
|
||||
viewBinding.controllerRecord.setContentDescription(getContext().getString(recordingStatus.isRecordingAndNotPaused() ? R.string.image_pause : R.string.image_record));
|
||||
|
||||
@@ -290,10 +249,11 @@ public class ControllerFragment extends Fragment implements View.OnTouchListener
|
||||
}
|
||||
|
||||
viewBinding.controllerTotalTime.setVisibility(recordingStatus.isRecording() ? View.VISIBLE : View.INVISIBLE);
|
||||
}
|
||||
|
||||
if (recordingStatus.isRecording()) {
|
||||
updateTotalTime();
|
||||
setTotalTime();
|
||||
}
|
||||
private void onTotalTimeChanged(TrackRecordingService.RecordingData recordingData) {
|
||||
this.recordingData = recordingData;
|
||||
|
||||
viewBinding.controllerTotalTime.setText(StringUtils.formatElapsedTimeWithHour(this.recordingData.getTrackStatistics().getTotalTime()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ public class TrackDataHub {
|
||||
private EGM2008Utils.EGM2008Correction egm2008Correction;
|
||||
|
||||
private boolean started;
|
||||
//TODO Check if this is needed.
|
||||
private HandlerThread handlerThread;
|
||||
private Handler handler;
|
||||
|
||||
|
||||
@@ -49,6 +49,16 @@ public class Track {
|
||||
public Track() {
|
||||
}
|
||||
|
||||
public Track(Track track) {
|
||||
this.id = track.id;
|
||||
this.uuid = track.uuid;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.category = category;
|
||||
this.icon = icon;
|
||||
this.trackStatistics = trackStatistics;
|
||||
}
|
||||
|
||||
/**
|
||||
* May be null if the track was not loaded from the database.
|
||||
*/
|
||||
|
||||
+89
-248
@@ -2,7 +2,6 @@ package de.dennisguse.opentracks.fragments;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -22,10 +21,6 @@ import java.util.List;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TrackRecordingActivity;
|
||||
import de.dennisguse.opentracks.adapters.SensorsAdapter;
|
||||
import de.dennisguse.opentracks.content.TrackDataHub;
|
||||
import de.dennisguse.opentracks.content.TrackDataListener;
|
||||
import de.dennisguse.opentracks.content.data.Distance;
|
||||
import de.dennisguse.opentracks.content.data.Marker;
|
||||
import de.dennisguse.opentracks.content.data.Speed;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
@@ -34,7 +29,6 @@ import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.databinding.StatisticsRecordingBinding;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
@@ -46,25 +40,17 @@ import de.dennisguse.opentracks.viewmodels.SensorDataModel;
|
||||
* @author Sandor Dornbush
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class StatisticsRecordingFragment extends Fragment implements TrackDataListener {
|
||||
public class StatisticsRecordingFragment extends Fragment {
|
||||
|
||||
private static final String TAG = StatisticsRecordingFragment.class.getSimpleName();
|
||||
|
||||
private static final Duration UI_UPDATE_INTERVAL = Duration.ofSeconds(1);
|
||||
|
||||
public static Fragment newInstance() {
|
||||
return new StatisticsRecordingFragment();
|
||||
}
|
||||
|
||||
private TrackDataHub trackDataHub;
|
||||
|
||||
private Handler handlerUpdateUI;
|
||||
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection = new TrackRecordingServiceConnection();
|
||||
private TrackPoint lastTrackPoint;
|
||||
|
||||
@Nullable // Lazily loaded.
|
||||
private Track track;
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private TrackRecordingService.RecordingData recordingData = TrackRecordingService.NOT_RECORDING;
|
||||
private TrackPoint latestTrackPoint;
|
||||
|
||||
private StatisticsRecordingBinding viewBinding;
|
||||
private SensorsAdapter sensorsAdapter;
|
||||
@@ -83,9 +69,9 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
||||
preferenceMetricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getContext());
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key) && track != null) {
|
||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key) && recordingData != null) {
|
||||
updateUInecessary = true;
|
||||
preferenceReportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, getContext(), track.getCategory());
|
||||
preferenceReportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, getContext(), recordingData.getTrackCategory());
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_show_grade_altitude_key, key)) {
|
||||
@@ -99,39 +85,29 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
||||
}
|
||||
|
||||
if (key != null && updateUInecessary && isResumed()) {
|
||||
getActivity().runOnUiThread(() -> {
|
||||
if (isResumed()) {
|
||||
updateUI();
|
||||
}
|
||||
});
|
||||
getActivity().runOnUiThread(this::updateUI);
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable updateUIeachSecond = new Runnable() {
|
||||
private final Runnable bindChangedCallback = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isResumed() && isSelectedTrackRecording()) {
|
||||
if (!isSelectedTrackPaused() && track != null) {
|
||||
synchronized (this) {
|
||||
if (lastTrackPoint != null && lastTrackPoint.hasLocation() && !lastTrackPoint.isRecent()) {
|
||||
lastTrackPoint = null;
|
||||
setLocationValues();
|
||||
}
|
||||
}
|
||||
updateTotalTime();
|
||||
updateSensorDataUI();
|
||||
}
|
||||
|
||||
handlerUpdateUI.postDelayed(this, UI_UPDATE_INTERVAL.toMillis());
|
||||
TrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
|
||||
if (service == null) {
|
||||
Log.w(TAG, "could not get TrackRecordingService");
|
||||
return;
|
||||
}
|
||||
|
||||
service.getRecordingDataObservable()
|
||||
.observe(StatisticsRecordingFragment.this, recordingData -> onRecordingDataChanged(recordingData));
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
handlerUpdateUI = new Handler();
|
||||
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(getContext());
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
||||
|
||||
sensorsAdapter = new SensorsAdapter(getContext());
|
||||
}
|
||||
@@ -139,7 +115,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
viewBinding = StatisticsRecordingBinding.inflate(inflater, container, false);
|
||||
viewBinding.statsActivityTypeIcon.setOnClickListener(v -> ((TrackRecordingActivity) getActivity()).chooseActivityType(track != null ? track.getCategory() : "")); //TODO "" would be useless as Track was not yet loaded.
|
||||
viewBinding.statsActivityTypeIcon.setOnClickListener(v -> ((TrackRecordingActivity) getActivity()).chooseActivityType(recordingData.getTrackCategory()));
|
||||
|
||||
RecyclerView sensorsRecyclerView = viewBinding.statsSensorsRecyclerView;
|
||||
sensorsRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
|
||||
@@ -151,25 +127,18 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
resumeTrackDataHub();
|
||||
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
|
||||
trackRecordingServiceConnection.startConnection(getContext());
|
||||
|
||||
updateUIeachSecond.run();
|
||||
handlerUpdateUI.post(updateUIeachSecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
pauseTrackDataHub();
|
||||
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
|
||||
handlerUpdateUI.removeCallbacks(updateUIeachSecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -191,101 +160,12 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
||||
sharedPreferences = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackUpdated(final Track track) {
|
||||
if (isResumed()) {
|
||||
getActivity().runOnUiThread(() -> {
|
||||
if (isResumed()) {
|
||||
if ((this.track == null && track != null) || (this.track != null && track != null && !this.track.getCategory().equals(track.getCategory()))) {
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, getString(R.string.stats_rate_key));
|
||||
}
|
||||
this.track = track;
|
||||
updateUI();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearTrackPoints() {
|
||||
lastTrackPoint = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewTrackPointsDone(@NonNull TrackPoint newLastTrackPoint, @NonNull TrackStatistics trackStatistics) {
|
||||
if (isResumed()) {
|
||||
getActivity().runOnUiThread(() -> {
|
||||
if (isResumed()) {
|
||||
synchronized (this) {
|
||||
this.lastTrackPoint = newLastTrackPoint;
|
||||
|
||||
if (!isSelectedTrackRecording() || isSelectedTrackPaused()) {
|
||||
this.lastTrackPoint = null;
|
||||
}
|
||||
|
||||
if (this.lastTrackPoint != null && this.lastTrackPoint.hasLocation() && !this.lastTrackPoint.isRecent()) {
|
||||
this.lastTrackPoint = null;
|
||||
}
|
||||
setLocationValues();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearMarkers() {
|
||||
// We don't care.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewMarker(Marker marker) {
|
||||
// We don't care.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewMarkersDone() {
|
||||
// We don't care.
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes the trackDataHub.
|
||||
* Needs to be synchronized because trackDataHub can be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void resumeTrackDataHub() {
|
||||
trackDataHub = ((TrackRecordingActivity) getActivity()).getTrackDataHub();
|
||||
trackDataHub.registerTrackDataListener(this, true, false, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the trackDataHub.
|
||||
* Needs to be synchronized because trackDataHub can be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void pauseTrackDataHub() {
|
||||
trackDataHub.unregisterTrackDataListener(this);
|
||||
trackDataHub = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
updateStats();
|
||||
setLocationValues();
|
||||
updateSensorDataUI();
|
||||
if (isResumed()) {
|
||||
updateStats();
|
||||
setLocationValues();
|
||||
updateSensorDataUI();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,100 +173,45 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
||||
* Also sets altitude gain and loss.
|
||||
*/
|
||||
private void updateSensorDataUI() {
|
||||
TrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||
|
||||
if (trackRecordingService == null) {
|
||||
Log.d(TAG, "Cannot get the track recording service.");
|
||||
} else {
|
||||
SensorDataSet sensorDataSet = trackRecordingService.getSensorDataSet();
|
||||
if (sensorDataSet != null) {
|
||||
List<SensorDataModel> sensorDataList = new ArrayList<>();
|
||||
if (sensorDataSet.getHeartRate() != null) {
|
||||
sensorDataList.add(new SensorDataModel(sensorDataSet.getHeartRate()));
|
||||
}
|
||||
if (sensorDataSet.getCyclingCadence() != null) {
|
||||
sensorDataList.add(new SensorDataModel(sensorDataSet.getCyclingCadence()));
|
||||
}
|
||||
if (sensorDataSet.getCyclingPower() != null) {
|
||||
sensorDataList.add(new SensorDataModel(sensorDataSet.getCyclingPower()));
|
||||
}
|
||||
sensorsAdapter.swapData(sensorDataList);
|
||||
setSpeedSensorData(sensorDataSet);
|
||||
SensorDataSet sensorDataSet = recordingData.getSensorDataSet();
|
||||
if (sensorDataSet != null) {
|
||||
List<SensorDataModel> sensorDataList = new ArrayList<>();
|
||||
if (sensorDataSet.getHeartRate() != null) {
|
||||
sensorDataList.add(new SensorDataModel(sensorDataSet.getHeartRate()));
|
||||
}
|
||||
//TODO Check if we can get the total altitude gain and loss via trackStatistics instead of doing some computation in the UI layer.
|
||||
setTotalAltitudeGain(trackRecordingService.getAltitudeGain_m());
|
||||
setTotalAltitudeLoss(trackRecordingService.getAltitudeLoss_m());
|
||||
}
|
||||
}
|
||||
|
||||
// Set altitude gain
|
||||
private void setTotalAltitudeGain(Float altitudeGain_m) {
|
||||
Float totalAltitudeGain = altitudeGain_m;
|
||||
|
||||
if (track != null && track.getTrackStatistics().hasTotalAltitudeGain()) {
|
||||
if (altitudeGain_m == null) {
|
||||
totalAltitudeGain = 0f;
|
||||
if (sensorDataSet.getCyclingCadence() != null) {
|
||||
sensorDataList.add(new SensorDataModel(sensorDataSet.getCyclingCadence()));
|
||||
}
|
||||
|
||||
totalAltitudeGain += track.getTrackStatistics().getTotalAltitudeGain();
|
||||
if (sensorDataSet.getCyclingPower() != null) {
|
||||
sensorDataList.add(new SensorDataModel(sensorDataSet.getCyclingPower()));
|
||||
}
|
||||
sensorsAdapter.swapData(sensorDataList);
|
||||
}
|
||||
|
||||
Pair<String, String> parts = StringUtils.formatAltitude(getContext(), totalAltitudeGain, preferenceMetricUnits);
|
||||
viewBinding.statsAltitudeGainValue.setText(parts.first);
|
||||
viewBinding.statsAltitudeGainUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
// Set altitude loss
|
||||
private void setTotalAltitudeLoss(Float altitudeLoss_m) {
|
||||
Float totalAltitudeLoss = altitudeLoss_m;
|
||||
|
||||
if (track != null && track.getTrackStatistics().hasTotalAltitudeLoss()) {
|
||||
if (altitudeLoss_m == null) {
|
||||
totalAltitudeLoss = track.getTrackStatistics().getTotalAltitudeLoss();
|
||||
} else {
|
||||
totalAltitudeLoss += track.getTrackStatistics().getTotalAltitudeLoss();
|
||||
}
|
||||
{
|
||||
Pair<String, String> parts = StringUtils.formatAltitude(getContext(), recordingData.getTrackStatistics().getTotalAltitudeGain(), preferenceMetricUnits);
|
||||
viewBinding.statsAltitudeGainValue.setText(parts.first);
|
||||
viewBinding.statsAltitudeGainUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
Pair<String, String> parts = StringUtils.formatAltitude(getContext(), totalAltitudeLoss, preferenceMetricUnits);
|
||||
viewBinding.statsAltitudeLossValue.setText(parts.first);
|
||||
viewBinding.statsAltitudeLossUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
private void setSpeedSensorData(SensorDataSet sensorDataSet) {
|
||||
if (sensorDataSet != null && sensorDataSet.getCyclingDistanceSpeed() != null) {
|
||||
SensorDataCycling.DistanceSpeed data = sensorDataSet.getCyclingDistanceSpeed();
|
||||
|
||||
if (data.hasValue() && data.isRecent()) {
|
||||
setTotalDistance(data.getValue().distanceOverall);
|
||||
setSpeed(data.getValue().speed);
|
||||
}
|
||||
if (data.hasValue() && data.isRecent()) {
|
||||
setSpeed(data.getValue().speed);
|
||||
}
|
||||
{
|
||||
Pair<String, String> parts = StringUtils.formatAltitude(getContext(), recordingData.getTrackStatistics().getTotalAltitudeLoss(), preferenceMetricUnits);
|
||||
viewBinding.statsAltitudeLossValue.setText(parts.first);
|
||||
viewBinding.statsAltitudeLossUnit.setText(parts.second);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateStats() {
|
||||
|
||||
setTotalDistance(Distance.of(0)); //TODO Why?
|
||||
|
||||
// Set activity type
|
||||
if (track != null) {
|
||||
String trackIconValue = TrackIconUtils.getIconValue(getContext(), track.getCategory());
|
||||
viewBinding.statsActivityTypeIcon.setEnabled(isSelectedTrackRecording());
|
||||
viewBinding.statsActivityTypeIcon.setImageResource(TrackIconUtils.getIconDrawable(trackIconValue));
|
||||
}
|
||||
String trackIconValue = TrackIconUtils.getIconValue(getContext(), recordingData.getTrackCategory());
|
||||
viewBinding.statsActivityTypeIcon.setImageResource(TrackIconUtils.getIconDrawable(trackIconValue));
|
||||
|
||||
// Set time
|
||||
if (track != null) {
|
||||
viewBinding.statsMovingTimeValue.setText(StringUtils.formatElapsedTime(track.getTrackStatistics().getMovingTime()));
|
||||
updateTotalTime();
|
||||
}
|
||||
viewBinding.statsMovingTimeValue.setText(StringUtils.formatElapsedTime(recordingData.getTrackStatistics().getMovingTime()));
|
||||
updateTotalTime();
|
||||
|
||||
// Set average speed/pace
|
||||
{
|
||||
Speed speed = track != null ? track.getTrackStatistics().getAverageSpeed() : null;
|
||||
Speed speed = recordingData.getTrackStatistics().getAverageSpeed();
|
||||
viewBinding.statsAverageSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_average_speed : R.string.stats_average_pace);
|
||||
|
||||
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), speed, preferenceMetricUnits, preferenceReportSpeed);
|
||||
@@ -396,7 +221,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
||||
|
||||
// Set max speed/pace
|
||||
{
|
||||
Speed speed = track != null ? track.getTrackStatistics().getMaxSpeed() : null;
|
||||
Speed speed = recordingData.getTrackStatistics().getMaxSpeed();
|
||||
|
||||
viewBinding.statsMaxSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_max_speed : R.string.stats_fastest_pace);
|
||||
|
||||
@@ -407,7 +232,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
||||
|
||||
// Set moving speed/pace
|
||||
{
|
||||
Speed speed = track != null ? track.getTrackStatistics().getAverageMovingSpeed() : null;
|
||||
Speed speed = recordingData.getTrackStatistics().getAverageMovingSpeed();
|
||||
|
||||
viewBinding.statsMovingSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_average_moving_speed : R.string.stats_average_moving_pace);
|
||||
|
||||
@@ -417,34 +242,41 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
||||
}
|
||||
|
||||
// Set altitude gain and loss
|
||||
{
|
||||
viewBinding.statsAltitudeGroup.setVisibility(preferenceShowAltitude ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
viewBinding.statsAltitudeGroup.setVisibility(preferenceShowAltitude ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void updateTotalTime() {
|
||||
Duration totalTime = track.getTrackStatistics().getTotalTime();
|
||||
if (isSelectedTrackRecording()) {
|
||||
TrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||
if (trackRecordingService != null) {
|
||||
totalTime = trackRecordingService.getTotalTime();
|
||||
}
|
||||
}
|
||||
|
||||
Duration totalTime = recordingData.getTrackStatistics().getTotalTime();
|
||||
viewBinding.statsTotalTimeValue.setText(StringUtils.formatElapsedTime(totalTime));
|
||||
}
|
||||
|
||||
private void setLocationValues() {
|
||||
// Set speed/pace
|
||||
Speed speed = lastTrackPoint != null && lastTrackPoint.hasSpeed() ? lastTrackPoint.getSpeed() : null;
|
||||
Speed speed = latestTrackPoint != null && latestTrackPoint.hasSpeed() ? latestTrackPoint.getSpeed() : null;
|
||||
|
||||
SensorDataSet sensorDataSet = recordingData.getSensorDataSet();
|
||||
if (sensorDataSet != null && sensorDataSet.getCyclingDistanceSpeed() != null) {
|
||||
SensorDataCycling.DistanceSpeed data = sensorDataSet.getCyclingDistanceSpeed();
|
||||
if (data.hasValue() && data.isRecent()) {
|
||||
speed = data.getValue().speed;
|
||||
}
|
||||
}
|
||||
|
||||
setSpeed(speed);
|
||||
|
||||
// Set distance
|
||||
{
|
||||
Pair<String, String> parts = StringUtils.getDistanceParts(getContext(), recordingData.getTrackStatistics().getTotalDistance(), preferenceMetricUnits);
|
||||
|
||||
viewBinding.statsDistanceValue.setText(parts.first);
|
||||
viewBinding.statsDistanceUnit.setText(parts.second);
|
||||
}
|
||||
// Set altitude
|
||||
viewBinding.statsAltitudeGroup.setVisibility(preferenceShowAltitude ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (preferenceShowAltitude) {
|
||||
// Current altitude
|
||||
Float altitude = lastTrackPoint != null && lastTrackPoint.hasAltitude() ? (float) lastTrackPoint.getAltitude().toM() : null;
|
||||
Float altitude = latestTrackPoint != null && latestTrackPoint.hasAltitude() ? (float) latestTrackPoint.getAltitude().toM() : null;
|
||||
Pair<String, String> parts = StringUtils.formatAltitude(getContext(), altitude, preferenceMetricUnits);
|
||||
viewBinding.statsAltitudeCurrentValue.setText(parts.first);
|
||||
viewBinding.statsAltitudeCurrentUnit.setText(parts.second);
|
||||
@@ -455,23 +287,15 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
||||
if (preferenceShowCoordinate) {
|
||||
String latitudeText = getContext().getString(R.string.value_unknown);
|
||||
String longitudeText = getContext().getString(R.string.value_unknown);
|
||||
if (lastTrackPoint != null && lastTrackPoint.hasLocation()) {
|
||||
latitudeText = StringUtils.formatCoordinate(lastTrackPoint.getLatitude());
|
||||
longitudeText = StringUtils.formatCoordinate(lastTrackPoint.getLongitude());
|
||||
if (latestTrackPoint != null && latestTrackPoint.hasLocation()) {
|
||||
latitudeText = StringUtils.formatCoordinate(latestTrackPoint.getLatitude());
|
||||
longitudeText = StringUtils.formatCoordinate(latestTrackPoint.getLongitude());
|
||||
}
|
||||
viewBinding.statsLatitudeValue.setText(latitudeText);
|
||||
viewBinding.statsLongitudeValue.setText(longitudeText);
|
||||
}
|
||||
}
|
||||
|
||||
private void setTotalDistance(Distance sensorDistanceSinceLastTrackpoint) {
|
||||
Distance totalDistance = track != null ? (track.getTrackStatistics().getTotalDistance().plus(sensorDistanceSinceLastTrackpoint)) : Distance.invalid();
|
||||
Pair<String, String> parts = StringUtils.getDistanceParts(getContext(), totalDistance, preferenceMetricUnits);
|
||||
|
||||
viewBinding.statsDistanceValue.setText(parts.first);
|
||||
viewBinding.statsDistanceUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
private void setSpeed(Speed speed) {
|
||||
viewBinding.statsSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_speed : R.string.stats_pace);
|
||||
|
||||
@@ -479,4 +303,21 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
||||
viewBinding.statsSpeedValue.setText(parts.first);
|
||||
viewBinding.statsSpeedUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
private void onRecordingDataChanged(TrackRecordingService.RecordingData recordingData) {
|
||||
String oldCategory = this.recordingData.getTrackCategory();
|
||||
String newCategory = recordingData.getTrackCategory();
|
||||
|
||||
if (!oldCategory.equals(newCategory)) {
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, getString(R.string.stats_rate_key));
|
||||
}
|
||||
|
||||
this.recordingData = recordingData;
|
||||
this.latestTrackPoint = recordingData.getLatestTrackPoint();
|
||||
if (latestTrackPoint != null && latestTrackPoint.hasLocation() && !latestTrackPoint.isRecent()) {
|
||||
latestTrackPoint = null;
|
||||
}
|
||||
|
||||
updateUI();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,10 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
|
||||
private static final String TAG = TrackRecordingService.class.getSimpleName();
|
||||
|
||||
private static final Duration RECORDING_DATA_UPDATE_INTERVAL = Duration.ofSeconds(1);
|
||||
|
||||
public static final RecordingStatus STATUS_DEFAULT = new RecordingStatus(null, false);
|
||||
public static final RecordingData NOT_RECORDING = new RecordingData(null, null, null);
|
||||
public static final GpsStatusValue STATUS_GPS_DEFAULT = GpsStatusValue.GPS_NONE;
|
||||
|
||||
// The following variables are set in onCreate:
|
||||
@@ -84,6 +87,19 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
private PeriodicTaskExecutor voiceExecutor;
|
||||
private TrackRecordingServiceNotificationManager notificationManager;
|
||||
|
||||
private Handler handler;
|
||||
private final Runnable updateRecordingData = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Track track = contentProviderUtils.getTrack(recordingStatus.getTrackId());
|
||||
track.getTrackStatistics().setTotalTime(TrackRecordingService.this.getTotalTime());
|
||||
|
||||
SensorDataSet sensorDataSet = fillWithSensorDataSet(lastTrackPoint);
|
||||
recordingDataObservable.postValue(new RecordingData(track, lastTrackPoint, sensorDataSet));
|
||||
handler.postDelayed(this, RECORDING_DATA_UPDATE_INTERVAL.toMillis());
|
||||
}
|
||||
};
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private Distance recordingDistanceInterval;
|
||||
@@ -127,14 +143,18 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
private RecordingStatus recordingStatus;
|
||||
private MutableLiveData<RecordingStatus> recordingStatusObservable;
|
||||
private MutableLiveData<GpsStatusValue> gpsStatusObservable;
|
||||
private MutableLiveData<RecordingData> recordingDataObservable;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
handler = new Handler();
|
||||
|
||||
recordingStatusObservable = new MutableLiveData<>();
|
||||
updateRecordingStatus(STATUS_DEFAULT);
|
||||
gpsStatusObservable = new MutableLiveData<>(STATUS_GPS_DEFAULT);
|
||||
recordingDataObservable = new MutableLiveData<>(NOT_RECORDING);
|
||||
|
||||
handlerServer = new HandlerServer(this);
|
||||
|
||||
@@ -160,6 +180,8 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
handler = null;
|
||||
|
||||
handlerServer.stop(this);
|
||||
handlerServer = null;
|
||||
|
||||
@@ -192,6 +214,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
|
||||
recordingStatusObservable = null;
|
||||
gpsStatusObservable = null;
|
||||
recordingDataObservable = null;
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
@@ -208,6 +231,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
return recordingStatus.getTrackId();
|
||||
}
|
||||
|
||||
//TODO
|
||||
public TrackStatistics getTrackStatistics() {
|
||||
if (trackStatisticsUpdater == null) {
|
||||
return null;
|
||||
@@ -352,6 +376,8 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
remoteSensorManager = new BluetoothRemoteSensorManager(this);
|
||||
remoteSensorManager.start();
|
||||
|
||||
handler.postDelayed(updateRecordingData, RECORDING_DATA_UPDATE_INTERVAL.toMillis());
|
||||
|
||||
altitudeSumManager = new AltitudeSumManager();
|
||||
altitudeSumManager.start(this);
|
||||
|
||||
@@ -438,6 +464,13 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
* @param trackStopped true if track is stopped, false if track is paused
|
||||
*/
|
||||
private void endRecording(boolean trackStopped) {
|
||||
if (trackStopped) {
|
||||
recordingDataObservable.postValue(NOT_RECORDING);
|
||||
} else {
|
||||
updateRecordingData.run();
|
||||
}
|
||||
handler.removeCallbacks(updateRecordingData);
|
||||
|
||||
// Shutdown periodic tasks
|
||||
voiceExecutor.shutdown();
|
||||
|
||||
@@ -613,14 +646,18 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
trackPoint.setAltitudeLoss(getAltitudeLoss_m());
|
||||
altitudeSumManager.reset();
|
||||
}
|
||||
|
||||
//TODO We need a copy of the SensorDataSet as data will be reset!
|
||||
SensorDataSet sensorDataSet = fillWithSensorDataSet(trackPoint);
|
||||
if (remoteSensorManager != null) {
|
||||
fillWithSensorDataSet(trackPoint);
|
||||
remoteSensorManager.reset();
|
||||
}
|
||||
contentProviderUtils.insertTrackPoint(trackPoint, track.getId());
|
||||
trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
|
||||
|
||||
track.setTrackStatistics(trackStatisticsUpdater.getTrackStatistics());
|
||||
|
||||
recordingDataObservable.postValue(new RecordingData(track, trackPoint, sensorDataSet));
|
||||
|
||||
contentProviderUtils.updateTrack(track);
|
||||
} catch (SQLiteException e) {
|
||||
/*
|
||||
@@ -632,25 +669,26 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
voiceExecutor.update();
|
||||
}
|
||||
|
||||
public SensorDataSet getSensorDataSet() {
|
||||
private SensorDataSet fillWithSensorDataSet(TrackPoint trackPoint) {
|
||||
if (remoteSensorManager == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return remoteSensorManager.getSensorData();
|
||||
}
|
||||
SensorDataSet sensorData = remoteSensorManager.getSensorData();
|
||||
if (sensorData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void fillWithSensorDataSet(TrackPoint trackPoint) {
|
||||
SensorDataSet sensorData = getSensorDataSet();
|
||||
if (sensorData != null) {
|
||||
if (trackPoint != null) {
|
||||
sensorData.fillTrackPoint(trackPoint);
|
||||
}
|
||||
return sensorData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the relative altitude gain (since last trackpoint).
|
||||
*/
|
||||
public Float getAltitudeGain_m() {
|
||||
private Float getAltitudeGain_m() {
|
||||
if (altitudeSumManager == null || !altitudeSumManager.isConnected()) {
|
||||
return null;
|
||||
}
|
||||
@@ -661,7 +699,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
/**
|
||||
* Returns the relative altitude loss (since last trackpoint).
|
||||
*/
|
||||
public Float getAltitudeLoss_m() {
|
||||
private Float getAltitudeLoss_m() {
|
||||
if (altitudeSumManager == null || !altitudeSumManager.isConnected()) {
|
||||
return null;
|
||||
}
|
||||
@@ -712,6 +750,10 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
return gpsStatusObservable;
|
||||
}
|
||||
|
||||
public MutableLiveData<RecordingData> getRecordingDataObservable() {
|
||||
return recordingDataObservable;
|
||||
}
|
||||
|
||||
public LiveData<RecordingStatus> getRecordingStatusObservable() {
|
||||
return recordingStatusObservable;
|
||||
}
|
||||
@@ -792,4 +834,50 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class RecordingData {
|
||||
|
||||
private final Track track;
|
||||
|
||||
private final TrackPoint latestTrackPoint;
|
||||
|
||||
private final SensorDataSet sensorDataSet;
|
||||
|
||||
/**
|
||||
* {@link Track} and {@link TrackPoint} must be immutable (i.e., their content does not change).
|
||||
*/
|
||||
public RecordingData(Track track, TrackPoint lastTrackPoint, SensorDataSet sensorDataSet) {
|
||||
this.track = track;
|
||||
this.latestTrackPoint = lastTrackPoint;
|
||||
this.sensorDataSet = sensorDataSet;
|
||||
}
|
||||
|
||||
public Track getTrack() {
|
||||
return track;
|
||||
}
|
||||
|
||||
public String getTrackCategory() {
|
||||
if (track == null) {
|
||||
return "";
|
||||
}
|
||||
return track.getCategory();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public TrackStatistics getTrackStatistics() {
|
||||
if (track == null) {
|
||||
return new TrackStatistics();
|
||||
}
|
||||
|
||||
return track.getTrackStatistics();
|
||||
}
|
||||
|
||||
public TrackPoint getLatestTrackPoint() {
|
||||
return latestTrackPoint;
|
||||
}
|
||||
|
||||
public SensorDataSet getSensorDataSet() {
|
||||
return sensorDataSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,7 @@ public class TrackRecordingServiceConnection implements ServiceConnection, Death
|
||||
|
||||
private static final String TAG = TrackRecordingServiceConnection.class.getSimpleName();
|
||||
|
||||
@Deprecated //TODO Implement proper interface that passes the current TrackRecordingService
|
||||
private final Runnable callback;
|
||||
|
||||
private TrackRecordingService trackRecordingService;
|
||||
|
||||
@@ -178,7 +178,7 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
}
|
||||
String category = track.getCategory();
|
||||
|
||||
//TODO Querying all TrackPoints all the time is inefficient; use TrackDataHub or something else.
|
||||
//TODO Querying all TrackPoints all the time is inefficient; get infos from TrackRecordingService
|
||||
TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null);
|
||||
ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
||||
while (trackPointIterator.hasNext()) {
|
||||
|
||||
Reference in New Issue
Block a user