diff --git a/src/androidTest/java/de/dennisguse/opentracks/ui/aggregatedStatistics/AggregatedStatisticsTest.java b/src/androidTest/java/de/dennisguse/opentracks/ui/aggregatedStatistics/AggregatedStatisticsTest.java index bc037d010..e6a76dcef 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/ui/aggregatedStatistics/AggregatedStatisticsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/ui/aggregatedStatistics/AggregatedStatisticsTest.java @@ -23,7 +23,6 @@ import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Statistics; import de.dennisguse.opentracks.data.models.Track; -import de.dennisguse.opentracks.stats.SegmentStatisticUpdater; @RunWith(JUnit4.class) public class AggregatedStatisticsTest { @@ -65,9 +64,8 @@ public class AggregatedStatisticsTest { assertNotNull(aggregatedStatistics.get(biking)); assertEquals(1, aggregatedStatistics.get(biking).getCountTracks()); - SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(biking).getTrackStatistics(); - assertEquals(totalDistance, statistics2.getTotalDistance()); - assertEquals(totalTime, statistics2.getMovingTime()); + assertEquals(totalDistance, aggregatedStatistics.get(biking).totalDistance); + assertEquals(totalTime, aggregatedStatistics.get(biking).totalMovingTime); } @Test @@ -119,9 +117,8 @@ public class AggregatedStatisticsTest { assertNotNull(aggregatedStatistics.get(biking)); assertEquals(2, aggregatedStatistics.get(biking).getCountTracks()); - SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(biking).getTrackStatistics(); - assertEquals(totalDistance.multipliedBy(2), statistics2.getTotalDistance()); - assertEquals(totalTime.multipliedBy(2), statistics2.getMovingTime()); + assertEquals(totalDistance.multipliedBy(2), aggregatedStatistics.get(biking).totalDistance); + assertEquals(totalTime.multipliedBy(2), aggregatedStatistics.get(biking).totalMovingTime); } @Test @@ -153,21 +150,18 @@ public class AggregatedStatisticsTest { assertEquals(1, aggregatedStatistics.get(walking).getCountTracks()); { - SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(biking).getTrackStatistics(); - assertEquals(totalDistance, statistics2.getTotalDistance()); - assertEquals(totalTime, statistics2.getMovingTime()); + assertEquals(totalDistance, aggregatedStatistics.get(biking).totalDistance); + assertEquals(totalTime, aggregatedStatistics.get(biking).totalMovingTime); } { - SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(running).getTrackStatistics(); - assertEquals(totalDistance, statistics2.getTotalDistance()); - assertEquals(totalTime, statistics2.getMovingTime()); + assertEquals(totalDistance, aggregatedStatistics.get(running).totalDistance); + assertEquals(totalTime, aggregatedStatistics.get(running).totalMovingTime); } { - SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(walking).getTrackStatistics(); - assertEquals(totalDistance, statistics2.getTotalDistance()); - assertEquals(totalTime, statistics2.getMovingTime()); + assertEquals(totalDistance, aggregatedStatistics.get(walking).totalDistance); + assertEquals(totalTime, aggregatedStatistics.get(walking).totalMovingTime); } } @@ -216,30 +210,26 @@ public class AggregatedStatisticsTest { // Biking. { - SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(biking).getTrackStatistics(); - assertEquals(totalDistance.multipliedBy(5), statistics2.getTotalDistance()); - assertEquals(totalTime.multipliedBy(5), statistics2.getMovingTime()); + assertEquals(totalDistance.multipliedBy(5), aggregatedStatistics.get(biking).totalDistance); + assertEquals(totalTime.multipliedBy(5), aggregatedStatistics.get(biking).totalMovingTime); } // Running. { - SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(running).getTrackStatistics(); - assertEquals(totalDistance.multipliedBy(2), statistics2.getTotalDistance()); - assertEquals(totalTime.multipliedBy(2), statistics2.getMovingTime()); + assertEquals(totalDistance.multipliedBy(2), aggregatedStatistics.get(running).totalDistance); + assertEquals(totalTime.multipliedBy(2), aggregatedStatistics.get(running).totalMovingTime); } // Walking. { - SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(walking).getTrackStatistics(); - assertEquals(totalDistance.multipliedBy(2), statistics2.getTotalDistance()); - assertEquals(totalTime.multipliedBy(2), statistics2.getMovingTime()); + assertEquals(totalDistance.multipliedBy(2), aggregatedStatistics.get(walking).totalDistance); + assertEquals(totalTime.multipliedBy(2), aggregatedStatistics.get(walking).totalMovingTime); } // Driving. { - SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(driving).getTrackStatistics(); - assertEquals(totalDistance, statistics2.getTotalDistance()); - assertEquals(totalTime, statistics2.getMovingTime()); + assertEquals(totalDistance, aggregatedStatistics.get(driving).totalDistance); + assertEquals(totalTime, aggregatedStatistics.get(driving).totalMovingTime); } // Check order diff --git a/src/main/java/de/dennisguse/opentracks/stats/SegmentStatisticUpdater.java b/src/main/java/de/dennisguse/opentracks/stats/SegmentStatisticUpdater.java index 1044810b2..ce1298534 100644 --- a/src/main/java/de/dennisguse/opentracks/stats/SegmentStatisticUpdater.java +++ b/src/main/java/de/dennisguse/opentracks/stats/SegmentStatisticUpdater.java @@ -108,7 +108,7 @@ public class SegmentStatisticUpdater { * * @param other another statistics data object */ - //TODO Should be refactored to append only [mainly due to isIdle] (NOTE: This requires to use a custom value object for AggregatedStatistics; this is anyhow recommended). + //TODO Should be refactored to append only [mainly due to isIdle] public void merge(SegmentStatisticUpdater other) { if (startTime == null) { startTime = other.startTime; diff --git a/src/main/java/de/dennisguse/opentracks/ui/aggregatedStatistics/AggregatedStatistics.java b/src/main/java/de/dennisguse/opentracks/ui/aggregatedStatistics/AggregatedStatistics.java index 5a6faa17d..ccb40c6a1 100644 --- a/src/main/java/de/dennisguse/opentracks/ui/aggregatedStatistics/AggregatedStatistics.java +++ b/src/main/java/de/dennisguse/opentracks/ui/aggregatedStatistics/AggregatedStatistics.java @@ -1,53 +1,47 @@ package de.dennisguse.opentracks.ui.aggregatedStatistics; +import android.util.Pair; + import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import java.time.Duration; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; -import de.dennisguse.opentracks.data.models.Statistics; +import de.dennisguse.opentracks.data.models.Distance; +import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Track; -import de.dennisguse.opentracks.stats.SegmentStatisticUpdater; public class AggregatedStatistics { - private final Map dataMap = new HashMap<>(); - - private final List dataList = new ArrayList<>(); + private final List dataList; public AggregatedStatistics(@NonNull List tracks) { - for (Track track : tracks) { - aggregate(track); - } + Map> groupedByActivityType = tracks.stream().collect(Collectors.groupingBy(Track::getActivityTypeLocalized)); - dataList.addAll(dataMap.values()); - dataList.sort((o1, o2) -> { - if (o1.getCountTracks() == o2.getCountTracks()) { - return o1.getActivityTypeLocalized().compareTo(o2.getActivityTypeLocalized()); - } - return (o1.getCountTracks() < o2.getCountTracks() ? 1 : -1); - }); - } + List>> groupedByActivityTypeList = new ArrayList<>(); + groupedByActivityType.forEach((activityTypeLocalized, trackList) -> groupedByActivityTypeList.add(new Pair<>(activityTypeLocalized, trackList))); - @VisibleForTesting - public void aggregate(@NonNull Track track) { - String activityTypeLocalized = track.getActivityTypeLocalized(); - if (dataMap.containsKey(activityTypeLocalized)) { - dataMap.get(activityTypeLocalized).add(track.getStatistics()); - } else { - dataMap.put(activityTypeLocalized, new AggregatedStatistic(activityTypeLocalized, track.getStatistics())); - } + dataList = groupedByActivityTypeList.parallelStream() + .map(i -> new AggregatedStatistic(i.first, i.second)) + .sorted((o1, o2) -> { + if (o1.getCountTracks() == o2.getCountTracks()) { + return o1.getActivityTypeLocalized().compareTo(o2.getActivityTypeLocalized()); + } + return (o1.getCountTracks() < o2.getCountTracks() ? 1 : -1); + }).toList(); } public int getCount() { - return dataMap.size(); + return dataList.size(); } - public AggregatedStatistic get(String activityType) { - return dataMap.get(activityType); + @VisibleForTesting + public AggregatedStatistic get(String activityTypeLocalized) { + return dataList.stream().filter(i -> i.getActivityTypeLocalized().equals(activityTypeLocalized)).findFirst().get(); } public AggregatedStatistic getItem(int position) { @@ -55,30 +49,33 @@ public class AggregatedStatistics { } public static class AggregatedStatistic { - private final String activityTypeLocalized; - private final SegmentStatisticUpdater trackStatistics; - private int countTracks = 1; + final String activityTypeLocalized; + final int countTracks; - public AggregatedStatistic(String activityTypeLocalized, Statistics trackStatistics) { + final Duration totalMovingTime; + + final Distance totalDistance; + + final Speed avgMovingSpeed; + + final Speed maxSpeed; + + public AggregatedStatistic(String activityTypeLocalized, List tracks) { this.activityTypeLocalized = activityTypeLocalized; - this.trackStatistics = new SegmentStatisticUpdater(trackStatistics); + this.countTracks = tracks.size(); + + totalMovingTime = tracks.stream().map(t -> t.getStatistics().movingTime()).reduce(Duration.ZERO, Duration::plus); + totalDistance = tracks.stream().map(t -> t.getStatistics().totalDistance()).reduce(Distance.of(0), Distance::plus); + maxSpeed = tracks.stream().map(t -> t.getStatistics().maxSpeed()).max((o1, o2) -> (int) (o1.speed_mps() - o2.speed_mps())).orElse(Speed.zero()); + avgMovingSpeed = Speed.of(totalDistance, totalMovingTime); } public String getActivityTypeLocalized() { return activityTypeLocalized; } - public SegmentStatisticUpdater getTrackStatistics() { - return trackStatistics; - } - public int getCountTracks() { return countTracks; } - - void add(Statistics statistics) { - trackStatistics.merge(new SegmentStatisticUpdater(statistics)); - countTracks++; - } } } diff --git a/src/main/java/de/dennisguse/opentracks/ui/aggregatedStatistics/AggregatedStatisticsAdapter.java b/src/main/java/de/dennisguse/opentracks/ui/aggregatedStatistics/AggregatedStatisticsAdapter.java index d19861c15..491b097fe 100644 --- a/src/main/java/de/dennisguse/opentracks/ui/aggregatedStatistics/AggregatedStatisticsAdapter.java +++ b/src/main/java/de/dennisguse/opentracks/ui/aggregatedStatistics/AggregatedStatisticsAdapter.java @@ -86,14 +86,14 @@ public class AggregatedStatisticsAdapter extends RecyclerView.Adapter parts = formatter.getSpeedParts(aggregatedStatistic.getTrackStatistics().getAverageMovingSpeed()); + Pair parts = formatter.getSpeedParts(aggregatedStatistic.avgMovingSpeed); viewBinding.aggregatedStatsAvgRate.setText(parts.first); viewBinding.aggregatedStatsAvgRateUnit.setText(parts.second); viewBinding.aggregatedStatsAvgRateLabel.setText(context.getString(R.string.stats_average_moving_speed)); } { - Pair parts = formatter.getSpeedParts(aggregatedStatistic.getTrackStatistics().getMaxSpeed()); + Pair parts = formatter.getSpeedParts(aggregatedStatistic.maxSpeed); viewBinding.aggregatedStatsMaxRate.setText(parts.first); viewBinding.aggregatedStatsMaxRateUnit.setText(parts.second); viewBinding.aggregatedStatsMaxRateLabel.setText(context.getString(R.string.stats_max_speed)); @@ -105,14 +105,14 @@ public class AggregatedStatisticsAdapter extends RecyclerView.Adapter parts = formatter.getSpeedParts(aggregatedStatistic.getTrackStatistics().getAverageMovingSpeed()); + Pair parts = formatter.getSpeedParts(aggregatedStatistic.avgMovingSpeed); viewBinding.aggregatedStatsAvgRate.setText(parts.first); viewBinding.aggregatedStatsAvgRateUnit.setText(parts.second); viewBinding.aggregatedStatsAvgRateLabel.setText(context.getString(R.string.stats_average_moving_pace)); } { - Pair parts = formatter.getSpeedParts(aggregatedStatistic.getTrackStatistics().getMaxSpeed()); + Pair parts = formatter.getSpeedParts(aggregatedStatistic.maxSpeed); viewBinding.aggregatedStatsMaxRate.setText(parts.first); viewBinding.aggregatedStatsMaxRateUnit.setText(parts.second); viewBinding.aggregatedStatsMaxRateLabel.setText(R.string.stats_fastest_pace); @@ -132,11 +132,11 @@ public class AggregatedStatisticsAdapter extends RecyclerView.Adapter parts = DistanceFormatter.Builder() .setUnit(unitSystem) - .build(context).getDistanceParts(aggregatedStatistic.getTrackStatistics().getTotalDistance()); + .build(context).getDistanceParts(aggregatedStatistic.totalDistance); viewBinding.aggregatedStatsDistance.setText(parts.first); viewBinding.aggregatedStatsDistanceUnit.setText(parts.second); - viewBinding.aggregatedStatsTime.setText(StringUtils.formatElapsedTime(aggregatedStatistic.getTrackStatistics().getMovingTime())); + viewBinding.aggregatedStatsTime.setText(StringUtils.formatElapsedTime(aggregatedStatistic.totalMovingTime)); } private int getIcon(AggregatedStatistics.AggregatedStatistic aggregatedStatistic) {