diff --git a/src/androidTest/java/de/dennisguse/opentracks/content/data/TestDataUtil.java b/src/androidTest/java/de/dennisguse/opentracks/content/data/TestDataUtil.java index 9ae50ac84..ee6344bcf 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/content/data/TestDataUtil.java +++ b/src/androidTest/java/de/dennisguse/opentracks/content/data/TestDataUtil.java @@ -16,6 +16,7 @@ public class TestDataUtil { public static final double INITIAL_LATITUDE = 37.0; public static final double INITIAL_LONGITUDE = -57.0; public static final double ALTITUDE_INTERVAL = 2.5; + public static final float ELEVATION_GAIN = 3; /** * Create a track without any trackPoints. @@ -70,7 +71,7 @@ public class TestDataUtil { trackPoint.setHeartRate_bpm(100f + i); trackPoint.setCyclingCadence_rpm(300f + i); trackPoint.setPower(400f + i); - trackPoint.setElevationGain(500f + i); + trackPoint.setElevationGain(ELEVATION_GAIN); return trackPoint; } diff --git a/src/androidTest/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsTest.java b/src/androidTest/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsTest.java index 0f053ca66..0ade930e1 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsTest.java @@ -80,7 +80,7 @@ public class IntervalStatisticsTest { // given List trackPoints = buildTrackPoints(200); TrackStatistics trackStatistics = buildTrackStatistics(trackPoints); - float distanceInterval = 3000; + float distanceInterval = 3000f; IntervalStatistics intervalStatistics = new IntervalStatistics(); // when and then @@ -97,7 +97,7 @@ public class IntervalStatisticsTest { // given List trackPoints = buildTrackPoints(1000); TrackStatistics trackStatistics = buildTrackStatistics(trackPoints); - float distanceInterval = 3000; + float distanceInterval = 3000f; IntervalStatistics intervalStatistics = new IntervalStatistics(); // when and then @@ -114,7 +114,7 @@ public class IntervalStatisticsTest { // given List trackPoints = buildTrackPoints(10000); TrackStatistics trackStatistics = buildTrackStatistics(trackPoints); - float distanceInterval = 1000; + float distanceInterval = 1000f; IntervalStatistics intervalStatistics = new IntervalStatistics(); // when and then @@ -126,15 +126,18 @@ public class IntervalStatisticsTest { List intervalList = intervalStatistics.getIntervalList(); double totalDistance = 0d; long totalTime = 0L; + float totalGain = 0f; for (IntervalStatistics.Interval i : intervalList) { totalDistance += i.getDistance_m(); totalTime += ((i.getDistance_m() / i.getSpeed_ms()) * UnitConversions.S_TO_MS); + totalGain += i.getGain_m(); } // then assertEquals(trackStatistics.getTotalDistance(), totalDistance, 0.01); assertEquals(trackStatistics.getTotalTime() * UnitConversions.MS_TO_S, totalTime * UnitConversions.MS_TO_S, 0.1); assertEquals(intervalList.size(), (int) Math.ceil(trackStatistics.getTotalDistance() / distanceInterval)); + assertEquals(totalGain, trackPoints.size() * TestDataUtil.ELEVATION_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(); diff --git a/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java index 1eae187cc..9421d7aa1 100644 --- a/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java +++ b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java @@ -66,8 +66,8 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc // Preferences private boolean recordingTrackPaused; - // Intervals recording fragment needs Track.Id when the activity creates it. - private OnTrackIdListener intervalsListener; + // Intervals recording fragment needs Track.Id when the activity creates it and knowing when category change. + private OnTrackRecordingListener intervalsListener; private final Runnable bindChangedCallback = new Runnable() { @Override @@ -370,6 +370,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc Track track = contentProviderUtils.getTrack(trackId); String category = getString(TrackIconUtils.getIconActivityType(iconValue)); TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils); + intervalsListener.onCategoryChanged(category); } private class CustomFragmentPagerAdapter extends FragmentPagerAdapter { @@ -417,11 +418,12 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc } } - public interface OnTrackIdListener { + public interface OnTrackRecordingListener { void onTrackId(Track.Id trackId); + void onCategoryChanged(String category); } - public void setTrackIdListener(OnTrackIdListener listener) { + public void setTrackIdListener(OnTrackRecordingListener listener) { this.intervalsListener = listener; if (trackId != null) { listener.onTrackId(trackId); diff --git a/src/main/java/de/dennisguse/opentracks/adapters/IntervalStatisticsAdapter.java b/src/main/java/de/dennisguse/opentracks/adapters/IntervalStatisticsAdapter.java index f923d9006..2e718f32b 100644 --- a/src/main/java/de/dennisguse/opentracks/adapters/IntervalStatisticsAdapter.java +++ b/src/main/java/de/dennisguse/opentracks/adapters/IntervalStatisticsAdapter.java @@ -23,10 +23,12 @@ public class IntervalStatisticsAdapter extends ArrayAdapter intervalList, StackMode stackMode) { + public IntervalStatisticsAdapter(Context context, List intervalList, String category, StackMode stackMode) { super(context, R.layout.interval_stats_list_item, intervalList); metricUnits = PreferencesUtils.isMetricUnits(context); + this.category = category; this.stackMode = stackMode; } @@ -43,8 +45,8 @@ public class IntervalStatisticsAdapter extends ArrayAdapter speedParts = StringUtils.getSpeedParts(getContext(), interval.getSpeed_ms(), metricUnits, true); - viewHolder.speed.setText(speedParts.first + " " + speedParts.second); + if (PreferencesUtils.isReportSpeed(getContext(), category)) { + Pair speedParts = StringUtils.getSpeedParts(getContext(), interval.getSpeed_ms(), metricUnits, true); + viewHolder.rate.setText(speedParts.first + " " + speedParts.second); + } else { + Pair paceParts = StringUtils.getSpeedParts(getContext(), interval.getSpeed_ms(), metricUnits, false); + viewHolder.rate.setText(paceParts.first + " " + paceParts.second); + } - Pair paceParts = StringUtils.getSpeedParts(getContext(), interval.getSpeed_ms(), metricUnits, false); - viewHolder.pace.setText(paceParts.first + " " + paceParts.second); + Pair gainParts = StringUtils.formatElevation(getContext(), interval.getGain_m(), metricUnits); + viewHolder.gain.setText(gainParts.first + " " + gainParts.second); return intervalView; } @@ -77,7 +84,7 @@ public class IntervalStatisticsAdapter extends ArrayAdapter spinnerAdapter; + private TextView rateLabel; + private Track.Id trackId; + private String category; protected final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (preferences, key) -> { if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key) || PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) { @@ -54,6 +58,7 @@ public class IntervalsFragment extends Fragment { if (adapter != null) { adapter.notifyDataSetChanged(); spinnerAdapter.notifyDataSetChanged(); + setRateLabel(); intervalChanged(); } } @@ -81,11 +86,13 @@ public class IntervalsFragment extends Fragment { intervalUnit = PreferencesUtils.isMetricUnits(getContext()) ? getContext().getString(R.string.unit_kilometer) : getContext().getString(R.string.unit_mile); if (savedInstanceState != null) { - trackId = savedInstanceState.getParcelable(TRACK_ID_KEY); + setTrackId(savedInstanceState.getParcelable(TRACK_ID_KEY)); } else { - trackId = getArguments().getParcelable(TRACK_ID_KEY); + setTrackId(getArguments().getParcelable(TRACK_ID_KEY)); } + rateLabel = view.findViewById(R.id.interval_rate); + intervalListView = view.findViewById(R.id.interval_list); intervalListView.setEmptyView(view.findViewById(R.id.interval_list_empty_view)); @@ -126,6 +133,7 @@ public class IntervalsFragment extends Fragment { } }); + setRateLabel(); intervalChanged(); } @@ -147,6 +155,12 @@ public class IntervalsFragment extends Fragment { outState.putParcelable(TRACK_ID_KEY, trackId); } + @Override + public void onResume() { + super.onResume(); + setTrackId(trackId); + } + /** * Update intervals through {@link IntervalStatisticsModel} view model. */ @@ -158,7 +172,7 @@ public class IntervalsFragment extends Fragment { LiveData liveData = viewModel.getIntervalStats(trackId, selectedInterval); liveData.observe(getActivity(), intervalStatistics -> { if (intervalStatistics != null) { - adapter = new IntervalStatisticsAdapter(getContext(), intervalStatistics.getIntervalList(), stackModeListView); + adapter = new IntervalStatisticsAdapter(getContext(), intervalStatistics.getIntervalList(), category, stackModeListView); intervalListView.setAdapter(adapter); } }); @@ -166,9 +180,29 @@ public class IntervalsFragment extends Fragment { public void setTrackId(Track.Id trackId) { this.trackId = trackId; + if (this.trackId != null) { + ContentProviderUtils contentProviderUtils = new ContentProviderUtils(getContext()); + Track track = contentProviderUtils.getTrack(this.trackId); + setCategory(track.getCategory()); + } } - public static class IntervalsRecordingFragment extends IntervalsFragment implements TrackRecordingActivity.OnTrackIdListener { + public void setCategory(String category) { + if (this.category == null || !this.category.equals(category)) { + this.category = category; + setRateLabel(); + intervalChanged(); + } + } + + private void setRateLabel() { + if (rateLabel != null) { + boolean reportSpeed = PreferencesUtils.isReportSpeed(getContext(), category); + rateLabel.setText(reportSpeed ? R.string.stats_speed : R.string.stats_pace); + } + } + + public static class IntervalsRecordingFragment extends IntervalsFragment implements TrackRecordingActivity.OnTrackRecordingListener { // Refreshing intervals stats it's not so demanding so 5 seconds is enough to balance performance and user experience. private static final long UI_UPDATE_INTERVAL = 5 * UnitConversions.ONE_SECOND_MS; @@ -221,5 +255,10 @@ public class IntervalsFragment extends Fragment { public void onTrackId(Track.Id trackId) { setTrackId(trackId); } + + @Override + public void onCategoryChanged(String category) { + setCategory(category); + } } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatistics.java b/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatistics.java index cb703478f..124ac0c7a 100644 --- a/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatistics.java +++ b/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatistics.java @@ -24,6 +24,7 @@ public class IntervalStatistics { } Interval interval = new Interval(); + interval.gain_m += trackPointList.get(0).hasElevationGain() ? trackPointList.get(0).getElevationGain() : 0; for (int i = 1; i < trackPointList.size(); i++) { TrackPoint prevTrackPoint = trackPointList.get(i - 1); TrackPoint trackPoint = trackPointList.get(i); @@ -31,6 +32,7 @@ public class IntervalStatistics { if (LocationUtils.isValidLocation(trackPoint.getLocation()) && LocationUtils.isValidLocation(prevTrackPoint.getLocation())) { interval.distance_m += prevTrackPoint.distanceTo(trackPoint); interval.time_ms += trackPoint.getTime() - prevTrackPoint.getTime(); + interval.gain_m += trackPoint.hasElevationGain() ? trackPoint.getElevationGain() : 0; if (interval.distance_m >= distanceInterval_m) { float adjustFactor = distanceInterval_m / interval.distance_m; @@ -80,17 +82,20 @@ public class IntervalStatistics { public static class Interval { private float distance_m = 0f; private float time_ms = 0f; + private float gain_m = 0f; public Interval() {} public Interval(float distance_m, float time_ms) { this.distance_m = distance_m; this.time_ms = time_ms; + this.gain_m = 0f; } public Interval(Interval i) { distance_m = i.distance_m; time_ms = i.time_ms; + gain_m = i.gain_m; } public float getDistance_m() { @@ -111,5 +116,9 @@ public class IntervalStatistics { } return distance_m / (float) (time_ms * UnitConversions.MS_TO_S); } + + public float getGain_m() { + return gain_m; + } } } diff --git a/src/main/res/drawable/ic_arrow_drop_up_24.xml b/src/main/res/drawable/ic_arrow_drop_up_24.xml new file mode 100644 index 000000000..e88c221d4 --- /dev/null +++ b/src/main/res/drawable/ic_arrow_drop_up_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/src/main/res/layout/interval_list_view.xml b/src/main/res/layout/interval_list_view.xml index 4a801b632..3a8fe1828 100644 --- a/src/main/res/layout/interval_list_view.xml +++ b/src/main/res/layout/interval_list_view.xml @@ -2,6 +2,7 @@ + tools:text="Speed"/> + android:text="@string/stats_elevation" /> diff --git a/src/main/res/layout/interval_stats_list_item.xml b/src/main/res/layout/interval_stats_list_item.xml index 9923da212..1de6ac727 100644 --- a/src/main/res/layout/interval_stats_list_item.xml +++ b/src/main/res/layout/interval_stats_list_item.xml @@ -5,28 +5,43 @@ android:layout_marginBottom="8dp" android:orientation="horizontal"> - + - + - + + + + + + + \ No newline at end of file