diff --git a/src/androidTest/java/de/dennisguse/opentracks/util/AnnouncementUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/util/AnnouncementUtilsTest.java index fb1469796..09a7835be 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/util/AnnouncementUtilsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/util/AnnouncementUtilsTest.java @@ -52,7 +52,7 @@ public class AnnouncementUtilsTest { stats.setTotalAltitudeGain(6000f); List trackPoints = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 10).second; - IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, 1000); + IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, Distance.of(1000)); IntervalStatistics.Interval lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1); // when diff --git a/src/androidTest/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsTest.java b/src/androidTest/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsTest.java index 38e208cc5..aed2bffb7 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsTest.java @@ -114,26 +114,27 @@ public class IntervalStatisticsTest { } private void whenAndThen(List trackPoints, TrackStatistics trackStatistics, float distanceInterval) { - IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, distanceInterval); + IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, Distance.of(distanceInterval)); List intervalList = intervalStatistics.getIntervalList(); - double totalDistance = 0d; + Distance totalDistance = Distance.of(0); float totalTime = 0L; float totalGain = 0f; for (IntervalStatistics.Interval i : intervalList) { - totalDistance += i.getDistance_m(); - totalTime += i.getDistance_m() / i.getSpeed_ms(); + totalDistance = totalDistance.plus(i.getDistance()); + totalTime += i.getDistance().toM() / i.getSpeed().toMPS(); totalGain += i.getGain_m(); } // then - assertEquals(trackStatistics.getTotalDistance().toM(), totalDistance, 0.01); + assertEquals(trackStatistics.getTotalDistance().toM(), totalDistance.toM(), 0.01); assertEquals(trackStatistics.getTotalTime().toMillis(), totalTime * UnitConversions.S_TO_MS, 1); assertEquals(intervalList.size(), (int) Math.ceil(trackStatistics.getTotalDistance().toM() / distanceInterval)); assertEquals(totalGain, trackPoints.size() * TestDataUtil.ALTITUDE_GAIN, 0.1); + for (int i = 0; i < intervalList.size() - 1; i++) { - assertEquals(intervalList.get(i).getDistance_m(), distanceInterval, 0.001); - totalDistance -= intervalList.get(i).getDistance_m(); + assertEquals(intervalList.get(i).getDistance().toM(), distanceInterval, 0.001); + totalDistance = totalDistance.minus(intervalList.get(i).getDistance()); } - assertEquals(intervalList.get(intervalList.size() - 1).getDistance_m(), totalDistance, 0.01); + assertEquals(intervalList.get(intervalList.size() - 1).getDistance().toM(), totalDistance.toM(), 0.01); } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/adapters/IntervalStatisticsAdapter.java b/src/main/java/de/dennisguse/opentracks/adapters/IntervalStatisticsAdapter.java index df598417b..9b528ee2d 100644 --- a/src/main/java/de/dennisguse/opentracks/adapters/IntervalStatisticsAdapter.java +++ b/src/main/java/de/dennisguse/opentracks/adapters/IntervalStatisticsAdapter.java @@ -13,7 +13,6 @@ import java.util.List; import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.content.data.Distance; -import de.dennisguse.opentracks.content.data.Speed; import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.viewmodels.IntervalStatistics; @@ -48,15 +47,17 @@ public class IntervalStatisticsAdapter extends RecyclerView.Adapter 0) { - sumDistance_m = actualPosition * intervalList.get(actualPosition - 1).getDistance_m() + interval.getDistance_m(); + sumDistance = intervalList.get(actualPosition - 1).getDistance() + .multipliedBy(actualPosition) + .plus(interval.getDistance()); } else { - sumDistance_m = nextPosition * interval.getDistance_m(); + sumDistance = interval.getDistance().multipliedBy(nextPosition); } - viewHolder.distance.setText(StringUtils.formatDistance(context, Distance.of(sumDistance_m), metricUnits)); + viewHolder.distance.setText(StringUtils.formatDistance(context, sumDistance, metricUnits)); - viewHolder.rate.setText(StringUtils.formatSpeed(context, Speed.of(interval.getSpeed_ms()), metricUnits, isReportSpeed)); + viewHolder.rate.setText(StringUtils.formatSpeed(context, interval.getSpeed(), metricUnits, isReportSpeed)); viewHolder.gain.setText(StringUtils.formatDistance(context, Distance.of(interval.getGain_m()), metricUnits)); viewHolder.loss.setText(StringUtils.formatDistance(context, Distance.of(interval.getLoss_m()), metricUnits)); diff --git a/src/main/java/de/dennisguse/opentracks/content/data/Distance.java b/src/main/java/de/dennisguse/opentracks/content/data/Distance.java index c1453bc10..bec374dc9 100644 --- a/src/main/java/de/dennisguse/opentracks/content/data/Distance.java +++ b/src/main/java/de/dennisguse/opentracks/content/data/Distance.java @@ -20,6 +20,14 @@ public class Distance { return of(distance_mile * UnitConversions.MI_TO_M); } + public static Distance one(boolean metricUnit) { + if (metricUnit) { + return Distance.of(1); + } else { + return Distance.ofMile(1); + } + } + public static Distance invalid() { return of(Double.NaN); } @@ -34,10 +42,22 @@ public class Distance { return new Distance(distance_m + distance.distance_m); } + public Distance minus(@NonNull Distance distance) { + return new Distance(distance_m - distance.distance_m); + } + public Distance multipliedBy(double factor) { return new Distance(factor * distance_m); } + public Distance dividedBy(double divisor) { + return multipliedBy(1 / divisor); + } + + public double dividedBy(@NonNull Distance divisor) { + return distance_m / divisor.distance_m; + } + public boolean isZero() { return distance_m == 0; } diff --git a/src/main/java/de/dennisguse/opentracks/services/tasks/AnnouncementPeriodicTask.java b/src/main/java/de/dennisguse/opentracks/services/tasks/AnnouncementPeriodicTask.java index b913d4535..4a8c0b7f3 100644 --- a/src/main/java/de/dennisguse/opentracks/services/tasks/AnnouncementPeriodicTask.java +++ b/src/main/java/de/dennisguse/opentracks/services/tasks/AnnouncementPeriodicTask.java @@ -26,6 +26,7 @@ import android.util.Log; import java.util.ArrayList; import java.util.Locale; +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.provider.ContentProviderUtils; @@ -35,7 +36,6 @@ import de.dennisguse.opentracks.services.TrackRecordingServiceStatus; import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.util.AnnouncementUtils; import de.dennisguse.opentracks.util.PreferencesUtils; -import de.dennisguse.opentracks.util.UnitConversions; import de.dennisguse.opentracks.viewmodels.IntervalStatistics; /** @@ -193,7 +193,7 @@ public class AnnouncementPeriodicTask implements PeriodicTask { boolean isMetricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, context); boolean isReportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, context, category); - IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, (float) (isMetricUnits ? 1d * UnitConversions.KM_TO_M : 1d * UnitConversions.MI_TO_M)); + IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, Distance.one(isMetricUnits)); IntervalStatistics.Interval lastInterval = intervalStatistics.getLastInterval(); String announcement = AnnouncementUtils.getAnnouncement(context, trackStatistics, isMetricUnits, isReportSpeed, lastInterval); diff --git a/src/main/java/de/dennisguse/opentracks/util/AnnouncementUtils.java b/src/main/java/de/dennisguse/opentracks/util/AnnouncementUtils.java index c3a891d3a..11659d474 100644 --- a/src/main/java/de/dennisguse/opentracks/util/AnnouncementUtils.java +++ b/src/main/java/de/dennisguse/opentracks/util/AnnouncementUtils.java @@ -20,16 +20,12 @@ public class AnnouncementUtils { public static String getAnnouncement(Context context, TrackStatistics trackStatistics, boolean isMetricUnits, boolean isReportSpeed, @Nullable IntervalStatistics.Interval currentInterval) { Distance distance = trackStatistics.getTotalDistance(); Speed distancePerTime = trackStatistics.getAverageMovingSpeed(); - double currentDistancePerTime = currentInterval != null ? currentInterval.getSpeed_ms() * UnitConversions.MPS_TO_KMH : 0; //TODO Use Speed? + Speed currentDistancePerTime = currentInterval != null ? currentInterval.getSpeed() : null; if (distance.isZero()) { return context.getString(R.string.voice_total_distance_zero); } - if (!isMetricUnits) { - currentDistancePerTime *= UnitConversions.KM_TO_MI; - } - String rate; String currentRate; String currentRateMsg; @@ -38,17 +34,16 @@ public class AnnouncementUtils { double distanceInUnit = distancePerTime.to(isMetricUnits); rate = context.getResources().getQuantityString(speedId, getQuantityCount(distanceInUnit), distanceInUnit); - currentRate = context.getResources().getQuantityString(speedId, getQuantityCount(currentDistancePerTime), currentDistancePerTime); + double currentDistancePerTimeInUnit = currentDistancePerTime != null ? currentDistancePerTime.to(isMetricUnits) : 0; + currentRate = context.getResources().getQuantityString(speedId, getQuantityCount(currentDistancePerTimeInUnit), currentDistancePerTimeInUnit); currentRateMsg = context.getString(R.string.voice_speed_lap, currentRate); } else { - double timePerDistance = distancePerTime.isZero() ? 0.0 : 1 / distancePerTime.to(isMetricUnits); //TODO PACE + Duration time = distancePerTime.toPace(isMetricUnits); int paceId = isMetricUnits ? R.string.voice_pace_per_kilometer : R.string.voice_pace_per_mile; - Duration time = Duration.ofMillis((long) (timePerDistance * UnitConversions.HR_TO_MIN * UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS)); rate = context.getString(paceId, getAnnounceTime(context, time)); - double currentTimePerDistance = currentDistancePerTime == 0 ? 0.0 : 1 / currentDistancePerTime; //TODO PACE - Duration currentTime = Duration.ofMillis((long) (currentTimePerDistance * UnitConversions.HR_TO_MIN * UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS)); + Duration currentTime = currentDistancePerTime.toPace(isMetricUnits); currentRate = context.getString(paceId, getAnnounceTime(context, currentTime)); currentRateMsg = context.getString(R.string.voice_pace_lap, currentRate); } diff --git a/src/main/java/de/dennisguse/opentracks/util/UnitConversions.java b/src/main/java/de/dennisguse/opentracks/util/UnitConversions.java index 91c0f6cde..0b479a242 100644 --- a/src/main/java/de/dennisguse/opentracks/util/UnitConversions.java +++ b/src/main/java/de/dennisguse/opentracks/util/UnitConversions.java @@ -22,32 +22,29 @@ package de.dennisguse.opentracks.util; */ public class UnitConversions { + // Time //TODO Use Duration // multiplication factor to convert seconds to milliseconds public static final long S_TO_MS = 1000; - // Time - // TODO Use int instead of long. - public static final long ONE_SECOND_US = UnitConversions.S_TO_MS * 1000; - public static final long ONE_SECOND_MS = UnitConversions.S_TO_MS; - // multiplication factor to convert milliseconds to seconds + @Deprecated public static final double MS_TO_S = 1d / S_TO_MS; // multiplication factor to convert minutes to seconds + @Deprecated public static final double MIN_TO_S = 60.0; // multiplication factor to convert seconds to minutes + @Deprecated public static final double S_TO_MIN = 1 / MIN_TO_S; - // multiplication factor to convert hours to minutes - public static final double HR_TO_MIN = 60.0; // multiplication factor to convert minutes to hours - public static final double MIN_TO_HR = 1 / HR_TO_MIN; - public static final double S_TO_HR = 1d / (S_TO_MIN * MIN_TO_HR); + @Deprecated + public static final double MIN_TO_HR = 1 / 60.0; // multiplication factor to convert kilometers to miles public static final double KM_TO_MI = 0.621371192; public static final double MM_TO_M = 0.001; - // Distance + // Distance //TODO Make private to Distance class! // multiplication factor to convert miles to feet private static final double MI_TO_FT = 5280.0; // multiplication factor to convert feet to miles diff --git a/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatistics.java b/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatistics.java index f009dddcd..307899305 100644 --- a/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatistics.java +++ b/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatistics.java @@ -6,20 +6,17 @@ 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.util.UnitConversions; public class IntervalStatistics { private final List intervalList = new ArrayList<>(); - private final float distanceInterval_m; + private final Distance distanceInterval; - /** - * @param trackPoints the list of TrackPoint. - * @param distanceInterval_m the meters of every interval. - */ - public IntervalStatistics(@NonNull List trackPoints, float distanceInterval_m) { + public IntervalStatistics(@NonNull List trackPoints, Distance distanceInterval) { intervalList.clear(); - this.distanceInterval_m = distanceInterval_m; + this.distanceInterval = distanceInterval; if (trackPoints.size() == 0) { return; @@ -33,24 +30,22 @@ public class IntervalStatistics { TrackPoint trackPoint = trackPoints.get(i); if (trackPoint.hasLocation() && prevTrackPoint.hasLocation()) { - interval.distance_m += prevTrackPoint.distanceToPrevious(trackPoint).toM(); + interval.distance = interval.distance.plus(prevTrackPoint.distanceToPrevious(trackPoint)); interval.time = interval.time.plus(Duration.between(prevTrackPoint.getTime(), trackPoint.getTime())); interval.gain_m += trackPoint.hasAltitudeGain() ? trackPoint.getAltitudeGain() : 0; interval.loss_m += trackPoint.hasAltitudeLoss() ? trackPoint.getAltitudeLoss() : 0; - if (interval.distance_m >= distanceInterval_m) { - float adjustFactor = distanceInterval_m / interval.distance_m; - Interval adjustedInterval = new Interval(interval); - adjustedInterval.adjust(adjustFactor); + if (interval.distance.greaterThan(distanceInterval)) { + Interval adjustedInterval = new Interval(interval, distanceInterval.dividedBy(interval.distance)); intervalList.add(adjustedInterval); - interval = new Interval(interval.distance_m - adjustedInterval.distance_m, interval.time.minus(adjustedInterval.time)); + interval = new Interval(interval.distance.minus(adjustedInterval.distance), interval.time.minus(adjustedInterval.time)); } } } - if (interval.distance_m > 1f) { + if (interval.distance.greaterThan(Distance.of(1))) { intervalList.add(interval); } } @@ -66,12 +61,12 @@ public class IntervalStatistics { * @return the interval object or null if any interval is completed. */ public Interval getLastInterval() { - if (intervalList.size() == 1 && intervalList.get(0).getDistance_m() < distanceInterval_m) { + 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_m() >= distanceInterval_m) { + if (intervalList.get(i).getDistance().greaterOrEqualThan(distanceInterval)) { return this.intervalList.get(i); } } @@ -80,7 +75,7 @@ public class IntervalStatistics { } public static class Interval { - private float distance_m = 0f; + private Distance distance = Distance.of(0); private Duration time = Duration.ofSeconds(0); private float gain_m = 0f; private float loss_m = 0f; @@ -88,35 +83,25 @@ public class IntervalStatistics { public Interval() { } - public Interval(float distance_m, Duration time) { - this.distance_m = distance_m; + public Interval(Distance distance, Duration time) { + this.distance = distance; this.time = time; } - public Interval(Interval i) { - distance_m = i.distance_m; + 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 float getDistance_m() { - return distance_m; + public Distance getDistance() { + return distance; } - public void adjust(float adjustFactor) { - distance_m *= adjustFactor; - time = Duration.ofMillis((long) (time.toMillis() * adjustFactor)); - } - - /** - * @return speed of the interval in m/s. - */ - public float getSpeed_ms() { - if (distance_m == 0f) { - return 0f; - } - return (distance_m / (time.toMillis() * (float) UnitConversions.MS_TO_S)); + public Speed getSpeed() { + return Speed.of(distance, time); } public float getGain_m() { diff --git a/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsModel.java b/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsModel.java index e79fbc752..c0b0efab8 100644 --- a/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsModel.java +++ b/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsModel.java @@ -10,8 +10,8 @@ import androidx.lifecycle.MutableLiveData; import java.util.ArrayList; import java.util.List; +import de.dennisguse.opentracks.content.data.Distance; import de.dennisguse.opentracks.content.data.TrackPoint; -import de.dennisguse.opentracks.util.UnitConversions; /** * This model is used to load intervals for a track. @@ -21,7 +21,7 @@ public class IntervalStatisticsModel extends AndroidViewModel { private final List trackPoints = new ArrayList<>(); private MutableLiveData> intervalsLiveData; - private float distanceInterval; + private Distance distanceInterval; public IntervalStatisticsModel(@NonNull Application application) { super(application); @@ -35,7 +35,7 @@ public class IntervalStatisticsModel extends AndroidViewModel { } intervalsLiveData = new MutableLiveData<>(); - distanceInterval = metricUnits ? (float) (interval.getValue() * UnitConversions.KM_TO_M) : (float) (interval.getValue() * UnitConversions.MI_TO_M); + distanceInterval = interval.getValue(); loadIntervalStatistics(); } return intervalsLiveData; @@ -73,7 +73,7 @@ public class IntervalStatisticsModel extends AndroidViewModel { interval = IntervalOption.OPTION_1; } - distanceInterval = metricUnits ? (float) (interval.getValue() * UnitConversions.KM_TO_M) : (float) (interval.getValue() * UnitConversions.MI_TO_M); + distanceInterval = interval.getValue(); loadIntervalStatistics(); } } @@ -91,19 +91,19 @@ public class IntervalStatisticsModel extends AndroidViewModel { OPTION_20(20), OPTION_50(50); - private final int value; + private final Distance value; IntervalOption(int value) { - this.value = value; + this.value = Distance.of(value); } - public int getValue() { + public Distance getValue() { return value; } @Override public String toString() { - return "" + value; + return "" + (int) value.toM(); //TODO Somehow IntervalsFragment relies on a parsable Integer. } } }