mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-09 12:53:05 +02:00
@@ -1,184 +0,0 @@
|
||||
package de.dennisguse.opentracks.viewmodels;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.Distance;
|
||||
import de.dennisguse.opentracks.content.data.Speed;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||
|
||||
public class IntervalStatistics {
|
||||
private TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
|
||||
private final List<Interval> intervalList;
|
||||
private final Distance distanceInterval;
|
||||
private final Distance minGPSDistance;
|
||||
private Interval interval, lastInterval;
|
||||
|
||||
/**
|
||||
* @param distanceInterval distance of every interval.
|
||||
* @param minGPSDistance the setting value for GPS distance.
|
||||
*/
|
||||
public IntervalStatistics(Distance distanceInterval, Distance minGPSDistance) {
|
||||
this.distanceInterval = distanceInterval;
|
||||
this.minGPSDistance = minGPSDistance;
|
||||
|
||||
interval = new Interval();
|
||||
lastInterval = new Interval();
|
||||
intervalList = new ArrayList<>();
|
||||
intervalList.add(lastInterval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete intervals with the tracks points from the iterator.
|
||||
*
|
||||
* @return the last track point's id used to compute the intervals.
|
||||
*/
|
||||
public TrackPoint.Id addTrackPoints(TrackPointIterator trackPointIterator) {
|
||||
boolean newIntervalAdded = false;
|
||||
TrackPoint trackPoint = null;
|
||||
|
||||
while (trackPointIterator.hasNext()) {
|
||||
trackPoint = trackPointIterator.next();
|
||||
trackStatisticsUpdater.addTrackPoint(trackPoint, minGPSDistance);
|
||||
|
||||
if (trackStatisticsUpdater.getTrackStatistics().getTotalDistance().plus(interval.distance).greaterOrEqualThan(distanceInterval)) {
|
||||
interval.add(trackStatisticsUpdater.getTrackStatistics(), trackPoint);
|
||||
|
||||
double adjustFactor = distanceInterval.dividedBy(interval.distance);
|
||||
Interval adjustedInterval = new Interval(interval);
|
||||
adjustedInterval.adjust(adjustFactor);
|
||||
|
||||
intervalList.set(intervalList.size() - 1, adjustedInterval);
|
||||
|
||||
interval = new Interval(interval.distance.minus(adjustedInterval.distance), interval.time.minus(adjustedInterval.time));
|
||||
trackStatisticsUpdater = new TrackStatisticsUpdater();
|
||||
trackStatisticsUpdater.addTrackPoint(trackPoint, minGPSDistance);
|
||||
|
||||
lastInterval = new Interval(interval);
|
||||
intervalList.add(lastInterval);
|
||||
|
||||
newIntervalAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (newIntervalAdded) {
|
||||
lastInterval.add(trackStatisticsUpdater.getTrackStatistics(), null);
|
||||
} else {
|
||||
lastInterval.set(trackStatisticsUpdater.getTrackStatistics());
|
||||
}
|
||||
|
||||
return trackPoint != null ? trackPoint.getId() : null;
|
||||
}
|
||||
|
||||
public List<Interval> getIntervalList() {
|
||||
return intervalList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last completed interval.
|
||||
* An interval is complete if its distance is equal to distanceInterval_m.
|
||||
*
|
||||
* @return the interval object or null if any interval is completed.
|
||||
*/
|
||||
public Interval getLastInterval() {
|
||||
if (intervalList.size() == 1 && intervalList.get(0).getDistance().lessThan(distanceInterval)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = intervalList.size() - 1; i >= 0; i--) {
|
||||
if (intervalList.get(i).getDistance().greaterOrEqualThan(distanceInterval)) {
|
||||
return this.intervalList.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class Interval {
|
||||
private Distance distance = Distance.of(0);
|
||||
private Duration time = Duration.ofSeconds(0);
|
||||
private Float gain_m;
|
||||
private Float loss_m;
|
||||
|
||||
public Interval() {
|
||||
}
|
||||
|
||||
public Interval(Distance distance, Duration time) {
|
||||
this.distance = distance;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public Interval(Interval i, double adjustFactor) {
|
||||
distance = i.distance.multipliedBy(adjustFactor);
|
||||
time = Duration.ofMillis((long) (i.time.toMillis() * adjustFactor));
|
||||
time = i.time;
|
||||
gain_m = i.gain_m;
|
||||
loss_m = i.loss_m;
|
||||
}
|
||||
|
||||
public Interval(Interval i) {
|
||||
distance = i.distance;
|
||||
time = i.time;
|
||||
gain_m = i.gain_m;
|
||||
loss_m = i.loss_m;
|
||||
}
|
||||
|
||||
private void adjust(double adjustFactor) {
|
||||
distance = distance.multipliedBy(adjustFactor);
|
||||
time = Duration.ofMillis((long) (time.toMillis() * adjustFactor));
|
||||
}
|
||||
|
||||
public Distance getDistance() {
|
||||
return distance;
|
||||
}
|
||||
|
||||
public Speed getSpeed() {
|
||||
return Speed.of(distance, time);
|
||||
}
|
||||
|
||||
public boolean hasGain() {
|
||||
return gain_m != null;
|
||||
}
|
||||
|
||||
public Float getGain_m() {
|
||||
return gain_m;
|
||||
}
|
||||
|
||||
public boolean hasLoss() {
|
||||
return loss_m != null;
|
||||
}
|
||||
|
||||
public Float getLoss_m() {
|
||||
return loss_m;
|
||||
}
|
||||
|
||||
private void add(TrackStatistics trackStatistics, @Nullable TrackPoint lastTrackPoint) {
|
||||
distance = distance.plus(trackStatistics.getTotalDistance());
|
||||
time = time.plus(trackStatistics.getTotalTime());
|
||||
gain_m = trackStatistics.hasTotalAltitudeGain() ? trackStatistics.getTotalAltitudeGain() : gain_m;
|
||||
loss_m = trackStatistics.hasTotalAltitudeLoss() ? trackStatistics.getTotalAltitudeLoss() : loss_m;
|
||||
if (lastTrackPoint == null) {
|
||||
return;
|
||||
}
|
||||
if (hasGain() && lastTrackPoint.hasAltitudeGain()) {
|
||||
gain_m = gain_m - lastTrackPoint.getAltitudeGain();
|
||||
}
|
||||
if (hasLoss() && lastTrackPoint.hasAltitudeLoss()) {
|
||||
loss_m = loss_m - lastTrackPoint.getAltitudeLoss();
|
||||
}
|
||||
}
|
||||
|
||||
private void set(TrackStatistics trackStatistics) {
|
||||
distance = trackStatistics.getTotalDistance();
|
||||
time = trackStatistics.getTotalTime();
|
||||
gain_m = trackStatistics.hasTotalAltitudeGain() ? trackStatistics.getTotalAltitudeGain() : gain_m;
|
||||
loss_m = trackStatistics.hasTotalAltitudeLoss() ? trackStatistics.getTotalAltitudeLoss() : loss_m;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
package de.dennisguse.opentracks.viewmodels;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.ContentResolver;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.Distance;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* This model is used to load intervals for a track.
|
||||
* It uses a default interval but it can be set from outside to manage the interval length.
|
||||
*/
|
||||
public class IntervalStatisticsModel extends AndroidViewModel {
|
||||
|
||||
private static final String TAG = IntervalStatisticsModel.class.getSimpleName();
|
||||
|
||||
private MutableLiveData<List<IntervalStatistics.Interval>> intervalsLiveData;
|
||||
private IntervalStatistics intervalStatistics;
|
||||
private Distance distanceInterval;
|
||||
private final Distance minGPSDistance;
|
||||
private final ContentResolver contentResolver;
|
||||
private ContentObserver trackPointsTableObserver;
|
||||
private TrackPoint.Id lastTrackPointId;
|
||||
|
||||
private final Executor executor = Executors.newSingleThreadExecutor();
|
||||
private HandlerThread handlerThread;
|
||||
private Handler handler;
|
||||
|
||||
public IntervalStatisticsModel(@NonNull Application application) {
|
||||
super(application);
|
||||
minGPSDistance = PreferencesUtils.getRecordingDistanceInterval();
|
||||
contentResolver = getApplication().getContentResolver();
|
||||
handlerThread = new HandlerThread(TAG);
|
||||
handlerThread.start();
|
||||
handler = new Handler(handlerThread.getLooper());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
super.onCleared();
|
||||
if (trackPointsTableObserver != null) {
|
||||
contentResolver.unregisterContentObserver(trackPointsTableObserver);
|
||||
trackPointsTableObserver = null;
|
||||
}
|
||||
if (handlerThread != null) {
|
||||
handlerThread.getLooper().quit();
|
||||
handlerThread = null;
|
||||
}
|
||||
handler = null;
|
||||
}
|
||||
|
||||
public MutableLiveData<List<IntervalStatistics.Interval>> getIntervalStats(Track.Id trackId, boolean metricUnits, @Nullable IntervalOption interval) {
|
||||
if (intervalsLiveData == null) {
|
||||
if (interval == null) {
|
||||
interval = IntervalOption.OPTION_1;
|
||||
}
|
||||
|
||||
intervalsLiveData = new MutableLiveData<>();
|
||||
distanceInterval = interval.getDistance(metricUnits);
|
||||
intervalStatistics = new IntervalStatistics(distanceInterval, minGPSDistance);
|
||||
|
||||
loadIntervalStatistics(trackId);
|
||||
}
|
||||
|
||||
trackPointsTableObserver = new ContentObserver(handler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
loadIntervalStatistics(trackId);
|
||||
}
|
||||
};
|
||||
contentResolver.registerContentObserver(TrackPointsColumns.CONTENT_URI_BY_TRACKID, false, trackPointsTableObserver);
|
||||
|
||||
return intervalsLiveData;
|
||||
}
|
||||
|
||||
private void loadIntervalStatistics(Track.Id trackId) {
|
||||
executor.execute(() -> {
|
||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(getApplication());
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, lastTrackPointId)) {
|
||||
lastTrackPointId = intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
intervalsLiveData.postValue(intervalStatistics.getIntervalList());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
if (trackPointsTableObserver != null) {
|
||||
contentResolver.unregisterContentObserver(trackPointsTableObserver);
|
||||
}
|
||||
}
|
||||
|
||||
public void update(Track.Id trackId, boolean metricUnits, @Nullable IntervalOption interval) {
|
||||
if (interval == null) {
|
||||
interval = IntervalOption.OPTION_1;
|
||||
}
|
||||
|
||||
lastTrackPointId = null;
|
||||
distanceInterval = interval.getDistance(metricUnits);
|
||||
intervalStatistics = new IntervalStatistics(distanceInterval, minGPSDistance);
|
||||
loadIntervalStatistics(trackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intervals length this view model support.
|
||||
*/
|
||||
public enum IntervalOption {
|
||||
OPTION_1(1),
|
||||
OPTION_2(2),
|
||||
OPTION_3(3),
|
||||
OPTION_4(4),
|
||||
OPTION_5(5),
|
||||
OPTION_10(10),
|
||||
OPTION_20(20),
|
||||
OPTION_50(50);
|
||||
|
||||
private final int multiplier;
|
||||
|
||||
IntervalOption(int multiplier) {
|
||||
this.multiplier = multiplier;
|
||||
}
|
||||
|
||||
public Distance getDistance(boolean metricUnits) {
|
||||
return Distance
|
||||
.one(metricUnits)
|
||||
.multipliedBy(multiplier);
|
||||
}
|
||||
|
||||
public boolean sameMultiplier(IntervalOption intervalOption) {
|
||||
return intervalOption != null && this.multiplier == intervalOption.multiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "" + multiplier; //TODO Somehow IntervalsFragment relies on a parsable Integer.
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user