Convert metricUnits boolean into an enum.

Fixes #1199.
This commit is contained in:
Dennis Guse
2022-04-26 22:00:53 +02:00
parent db5c9479df
commit a5b44175a3
34 changed files with 464 additions and 318 deletions
@@ -5,12 +5,13 @@ import org.junit.runner.Description;
import org.junit.runners.model.Statement; import org.junit.runners.model.Statement;
import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.settings.UnitSystem;
public class PreferenceMetricUnitRule implements TestRule { public class PreferenceUnitSystemRule implements TestRule {
private final boolean metricUnits; private final UnitSystem unit;
public PreferenceMetricUnitRule(boolean metricUnits) { public PreferenceUnitSystemRule(UnitSystem unit) {
this.metricUnits = metricUnits; this.unit = unit;
} }
@Override @Override
@@ -18,13 +19,13 @@ public class PreferenceMetricUnitRule implements TestRule {
return new Statement() { return new Statement() {
@Override @Override
public void evaluate() throws Throwable { public void evaluate() throws Throwable {
final boolean previousMetricUnits = PreferencesUtils.isMetricUnits(); final UnitSystem previousUnitSystem = PreferencesUtils.getUnitSystem();
try { try {
PreferencesUtils.setMetricUnits(metricUnits); PreferencesUtils.setUnit(unit);
base.evaluate(); base.evaluate();
} finally { } finally {
PreferencesUtils.setMetricUnits(previousMetricUnits); PreferencesUtils.setUnit(previousUnitSystem);
} }
} }
}; };
@@ -14,6 +14,7 @@ import de.dennisguse.opentracks.data.models.Altitude;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@@ -26,7 +27,7 @@ public class ChartPointTest {
statistics.setTotalTime(Duration.ofSeconds(1000)); statistics.setTotalTime(Duration.ofSeconds(1000));
// when // when
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), Altitude.EGM2008.of(0), false, false); ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), Altitude.EGM2008.of(0), false, UnitSystem.IMPERIAL);
// then // then
assertEquals(1000000, (long) point.getTimeOrDistance()); assertEquals(1000000, (long) point.getTimeOrDistance());
@@ -39,7 +40,7 @@ public class ChartPointTest {
statistics.setTotalDistance(Distance.of(1000)); statistics.setTotalDistance(Distance.of(1000));
// when // when
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), Altitude.EGM2008.of(0), true, true); ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), Altitude.EGM2008.of(0), true, UnitSystem.METRIC);
// then // then
assertEquals(1, (long) point.getTimeOrDistance()); assertEquals(1, (long) point.getTimeOrDistance());
@@ -51,7 +52,7 @@ public class ChartPointTest {
TrackStatistics statistics = new TrackStatistics(); TrackStatistics statistics = new TrackStatistics();
// when // when
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), Altitude.EGM2008.of(50), false, true); ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), Altitude.EGM2008.of(50), false, UnitSystem.METRIC);
// then // then
assertEquals(50, point.getAltitude(), 0.01); assertEquals(50, point.getAltitude(), 0.01);
@@ -65,7 +66,7 @@ public class ChartPointTest {
TrackStatistics statistics = new TrackStatistics(); TrackStatistics statistics = new TrackStatistics();
// when // when
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), Altitude.EGM2008.of(50), false, true); ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), Altitude.EGM2008.of(50), false, UnitSystem.METRIC);
// then // then
assertNull(point.getHeartRate()); assertNull(point.getHeartRate());
@@ -84,7 +85,7 @@ public class ChartPointTest {
TrackStatistics statistics = new TrackStatistics(); TrackStatistics statistics = new TrackStatistics();
// when // when
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), Altitude.EGM2008.of(50), false, true); ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), Altitude.EGM2008.of(50), false, UnitSystem.METRIC);
// then // then
assertEquals(100.0, point.getHeartRate(), 0.01); assertEquals(100.0, point.getHeartRate(), 0.01);
@@ -10,6 +10,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import de.dennisguse.opentracks.settings.UnitSystem;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class DistanceFormatterTest { public class DistanceFormatterTest {
@@ -19,7 +21,7 @@ public class DistanceFormatterTest {
public void testFormatDistance_metric() { public void testFormatDistance_metric() {
DistanceFormatter formatter = DistanceFormatter.Builder() DistanceFormatter formatter = DistanceFormatter.Builder()
.setDecimalCount(2) .setDecimalCount(2)
.setMetricUnits(true) .setUnit(UnitSystem.METRIC)
.build(context); .build(context);
// A large number in metric // A large number in metric
@@ -32,7 +34,7 @@ public class DistanceFormatterTest {
public void testFormatDistance_imperial() { public void testFormatDistance_imperial() {
DistanceFormatter formatter = DistanceFormatter.Builder() DistanceFormatter formatter = DistanceFormatter.Builder()
.setDecimalCount(2) .setDecimalCount(2)
.setMetricUnits(false) .setUnit(UnitSystem.IMPERIAL)
.build(context); .build(context);
// A large number in imperial // A large number in imperial
@@ -10,6 +10,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import de.dennisguse.opentracks.settings.UnitSystem;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class SpeedFormatterTest { public class SpeedFormatterTest {
@@ -19,7 +21,7 @@ public class SpeedFormatterTest {
public void testGetSpeedParts_pace_metric() { public void testGetSpeedParts_pace_metric() {
SpeedFormatter formatter = SpeedFormatter.Builder() SpeedFormatter formatter = SpeedFormatter.Builder()
.setDecimalCount(2) .setDecimalCount(2)
.setMetricUnits(true) .setUnit(UnitSystem.METRIC)
.setReportSpeedOrPace(false) .setReportSpeedOrPace(false)
.build(context); .build(context);
@@ -33,7 +35,7 @@ public class SpeedFormatterTest {
public void testGetSpeedParts_pace_imperial() { public void testGetSpeedParts_pace_imperial() {
SpeedFormatter formatter = SpeedFormatter.Builder() SpeedFormatter formatter = SpeedFormatter.Builder()
.setDecimalCount(2) .setDecimalCount(2)
.setMetricUnits(false) .setUnit(UnitSystem.IMPERIAL)
.setReportSpeedOrPace(false) .setReportSpeedOrPace(false)
.build(context); .build(context);
@@ -47,7 +49,7 @@ public class SpeedFormatterTest {
public void testGetSpeedParts_speed_metric() { public void testGetSpeedParts_speed_metric() {
SpeedFormatter formatter = SpeedFormatter.Builder() SpeedFormatter formatter = SpeedFormatter.Builder()
.setDecimalCount(2) .setDecimalCount(2)
.setMetricUnits(true) .setUnit(UnitSystem.METRIC)
.setReportSpeedOrPace(true) .setReportSpeedOrPace(true)
.build(context); .build(context);
@@ -59,7 +61,7 @@ public class SpeedFormatterTest {
public void testGetSpeedParts_speed_imperial() { public void testGetSpeedParts_speed_imperial() {
SpeedFormatter formatter = SpeedFormatter.Builder() SpeedFormatter formatter = SpeedFormatter.Builder()
.setDecimalCount(2) .setDecimalCount(2)
.setMetricUnits(false) .setUnit(UnitSystem.IMPERIAL)
.setReportSpeedOrPace(true) .setReportSpeedOrPace(true)
.build(context); .build(context);
@@ -71,7 +73,7 @@ public class SpeedFormatterTest {
public void testFormatSpeed() { public void testFormatSpeed() {
SpeedFormatter formatter = SpeedFormatter.Builder() SpeedFormatter formatter = SpeedFormatter.Builder()
.setDecimalCount(2) .setDecimalCount(2)
.setMetricUnits(true) .setUnit(UnitSystem.METRIC)
.setReportSpeedOrPace(false) .setReportSpeedOrPace(false)
.build(context); .build(context);
@@ -19,6 +19,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
@@ -48,7 +49,7 @@ public class TrackRecordingServiceNotificationManagerTest {
.thenReturn(notificationCompatBuilder); .thenReturn(notificationCompatBuilder);
TrackRecordingServiceNotificationManager subject = new TrackRecordingServiceNotificationManager(notificationManager, notificationCompatBuilder); TrackRecordingServiceNotificationManager subject = new TrackRecordingServiceNotificationManager(notificationManager, notificationCompatBuilder);
subject.setMetricUnits(true); subject.setUnitSystem(UnitSystem.METRIC);
// when // when
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, Distance.of(100)); subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, Distance.of(100));
@@ -25,6 +25,7 @@ import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.SensorStatistics; import de.dennisguse.opentracks.stats.SensorStatistics;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.ui.intervals.IntervalStatistics; import de.dennisguse.opentracks.ui.intervals.IntervalStatistics;
@@ -54,7 +55,7 @@ public class VoiceAnnouncementUtilsTest {
stats.setTotalAltitudeGain(6000f); stats.setTotalAltitudeGain(6000f);
// when // when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, true, null, null).toString(); String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
// then // then
assertEquals("Total distance 20.00 kilometers. 1 hour 5 minutes 10 seconds. Speed 18.4 kilometers per hour.", announcement); assertEquals("Total distance 20.00 kilometers. 1 hour 5 minutes 10 seconds. Speed 18.4 kilometers per hour.", announcement);
@@ -76,7 +77,7 @@ public class VoiceAnnouncementUtilsTest {
} }
// when // when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, true, lastInterval, null).toString(); String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, null).toString();
// then // then
assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour.", announcement); assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour.", announcement);
@@ -92,7 +93,7 @@ public class VoiceAnnouncementUtilsTest {
stats.setTotalAltitudeGain(6000f); stats.setTotalAltitudeGain(6000f);
// when // when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, false, null, null).toString(); String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, false, null, null).toString();
// then // then
assertEquals("Total distance 20.00 kilometers. 1 hour 5 minutes 10 seconds. Pace 3 minutes 15 seconds per kilometer.", announcement); assertEquals("Total distance 20.00 kilometers. 1 hour 5 minutes 10 seconds. Pace 3 minutes 15 seconds per kilometer.", announcement);
@@ -114,7 +115,7 @@ public class VoiceAnnouncementUtilsTest {
} }
// when // when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, false, lastInterval, null).toString(); String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, false, lastInterval, null).toString();
// then // then
assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Pace 1 minute 10 seconds per kilometer. Lap time 1 minute 10 seconds per kilometer.", announcement); assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Pace 1 minute 10 seconds per kilometer. Lap time 1 minute 10 seconds per kilometer.", announcement);
@@ -130,7 +131,7 @@ public class VoiceAnnouncementUtilsTest {
stats.setTotalAltitudeGain(6000f); stats.setTotalAltitudeGain(6000f);
// when // when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, true, null, null).toString(); String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, true, null, null).toString();
// then // then
assertEquals("Total distance 12.43 miles. 1 hour 5 minutes 10 seconds. Speed 11.4 miles per hour.", announcement); assertEquals("Total distance 12.43 miles. 1 hour 5 minutes 10 seconds. Speed 11.4 miles per hour.", announcement);
@@ -152,7 +153,7 @@ public class VoiceAnnouncementUtilsTest {
} }
// when // when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, true, lastInterval, null).toString(); String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, true, lastInterval, null).toString();
// then // then
assertEquals("Total distance 8.83 miles. 16 minutes 39 seconds. Speed 31.8 miles per hour. Lap speed 31.8 miles per hour.", announcement); assertEquals("Total distance 8.83 miles. 16 minutes 39 seconds. Speed 31.8 miles per hour. Lap speed 31.8 miles per hour.", announcement);
@@ -168,7 +169,7 @@ public class VoiceAnnouncementUtilsTest {
stats.setTotalAltitudeGain(6000f); stats.setTotalAltitudeGain(6000f);
// when // when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, false, null, null).toString(); String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, false, null, null).toString();
// then // then
assertEquals("Total distance 12.43 miles. 1 hour 5 minutes 10 seconds. Pace 5 minutes 15 seconds per mile.", announcement); assertEquals("Total distance 12.43 miles. 1 hour 5 minutes 10 seconds. Pace 5 minutes 15 seconds per mile.", announcement);
@@ -190,7 +191,7 @@ public class VoiceAnnouncementUtilsTest {
} }
// when // when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, false, lastInterval, null).toString(); String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, false, lastInterval, null).toString();
// then // then
assertEquals("Total distance 8.83 miles. 16 minutes 39 seconds. Pace 1 minute 53 seconds per mile. Lap time 1 minute 53 seconds per mile.", announcement); assertEquals("Total distance 8.83 miles. 16 minutes 39 seconds. Pace 1 minute 53 seconds per mile. Lap time 1 minute 53 seconds per mile.", announcement);
@@ -215,7 +216,7 @@ public class VoiceAnnouncementUtilsTest {
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null); SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null);
// when // when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, true, lastInterval, sensorStatistics).toString(); String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
// then // then
assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour. Average heart rate 180 bpm. Current heart rate 133 bpm.", announcement); assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour. Average heart rate 180 bpm. Current heart rate 133 bpm.", announcement);
@@ -23,6 +23,7 @@ import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Power; import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.services.RecordingData; import de.dennisguse.opentracks.services.RecordingData;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.SensorStatistics; import de.dennisguse.opentracks.stats.SensorStatistics;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout; import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
@@ -61,7 +62,7 @@ public class StatisticDataBuilderTest extends TestCase {
layout.addField(context.getString(R.string.stats_custom_layout_max_speed_key), context.getString(R.string.stats_max_speed), false, true, false); layout.addField(context.getString(R.string.stats_custom_layout_max_speed_key), context.getString(R.string.stats_max_speed), false, true, false);
// when // when
List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(context, recordingDataMock, layout, true); List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(context, recordingDataMock, layout, UnitSystem.METRIC);
// then // then
assertEquals(statisticDataList.size(), 3); assertEquals(statisticDataList.size(), 3);
@@ -100,7 +101,7 @@ public class StatisticDataBuilderTest extends TestCase {
layout.addField(context.getString(R.string.stats_custom_layout_power_key), context.getString(R.string.stats_sensors_power), true, true, false); layout.addField(context.getString(R.string.stats_custom_layout_power_key), context.getString(R.string.stats_sensors_power), true, true, false);
// when // when
List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(context, recordingDataMock, layout, true); List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(context, recordingDataMock, layout, UnitSystem.METRIC);
// then // then
assertEquals(statisticDataList.size(), 18); assertEquals(statisticDataList.size(), 18);
@@ -4,8 +4,6 @@ import android.app.Application;
import android.os.StrictMode; import android.os.StrictMode;
import android.util.Log; import android.util.Log;
import java.util.Locale;
import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.settings.PreferencesUtils;
/** /**
@@ -28,9 +26,7 @@ public class Startup extends Application {
PreferencesUtils.initPreferences(this, getResources()); PreferencesUtils.initPreferences(this, getResources());
// Set default values of preferences on first start. // Set default values of preferences on first start.
PreferencesUtils.resetPreferences(this, false); PreferencesUtils.resetPreferences(this, false);
if (PreferencesUtils.getString( R.string.stats_units_key, "").equals("")) { PreferencesUtils.applyDefaultUnit();
PreferencesUtils.setMetricUnits(!Locale.US.equals(Locale.getDefault()));
}
PreferencesUtils.applyNightMode(); PreferencesUtils.applyNightMode();
@@ -59,6 +59,7 @@ import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.services.handlers.GpsStatusValue; import de.dennisguse.opentracks.services.handlers.GpsStatusValue;
import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.settings.SettingsActivity; import de.dennisguse.opentracks.settings.SettingsActivity;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.share.ShareUtils; import de.dennisguse.opentracks.share.ShareUtils;
import de.dennisguse.opentracks.ui.aggregatedStatistics.AggregatedStatisticsActivity; import de.dennisguse.opentracks.ui.aggregatedStatistics.AggregatedStatisticsActivity;
import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragment; import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragment;
@@ -88,7 +89,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
private final TrackLoaderCallBack loaderCallbacks = new TrackLoaderCallBack(); private final TrackLoaderCallBack loaderCallbacks = new TrackLoaderCallBack();
// Preferences // Preferences
private boolean metricUnits = true; private UnitSystem unitSystem = UnitSystem.defaultUnitSystem();
private GpsStatusValue gpsStatusValue = TrackRecordingService.STATUS_GPS_DEFAULT; private GpsStatusValue gpsStatusValue = TrackRecordingService.STATUS_GPS_DEFAULT;
private RecordingStatus recordingStatus = TrackRecordingService.STATUS_DEFAULT; private RecordingStatus recordingStatus = TrackRecordingService.STATUS_DEFAULT;
@@ -112,7 +113,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> { private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) { if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
metricUnits = PreferencesUtils.isMetricUnits(); unitSystem = PreferencesUtils.getUnitSystem();
} }
if (key != null) { if (key != null) {
runOnUiThread(() -> { runOnUiThread(() -> {
@@ -192,7 +193,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
String name = cursor.getString(nameIndex); String name = cursor.getString(nameIndex);
String totalTime = StringUtils.formatElapsedTime(Duration.ofMillis(cursor.getLong(totalTimeIndex))); String totalTime = StringUtils.formatElapsedTime(Duration.ofMillis(cursor.getLong(totalTimeIndex)));
String totalDistance = DistanceFormatter.Builder() String totalDistance = DistanceFormatter.Builder()
.setMetricUnits(metricUnits) .setUnit(unitSystem)
.build(TrackListActivity.this).formatDistance(Distance.of(cursor.getDouble(totalDistanceIndex))); .build(TrackListActivity.this).formatDistance(Distance.of(cursor.getDouble(totalDistanceIndex)));
int markerCount = cursor.getInt(markerCountIndex); int markerCount = cursor.getInt(markerCountIndex);
long startTime = cursor.getLong(startTimeIndex); long startTime = cursor.getLong(startTimeIndex);
@@ -39,6 +39,7 @@ import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.databinding.ChartBinding; import de.dennisguse.opentracks.databinding.ChartBinding;
import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater; import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
@@ -77,9 +78,9 @@ public class ChartFragment extends Fragment implements TrackDataHub.Listener {
@Override @Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) { if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
boolean metricUnits = PreferencesUtils.isMetricUnits(); UnitSystem unitSystem = PreferencesUtils.getUnitSystem();
if (metricUnits != viewBinding.chartView.getMetricUnits()) { if (unitSystem != viewBinding.chartView.getUnitSystem()) {
viewBinding.chartView.setMetricUnits(metricUnits); viewBinding.chartView.setUnitSystem(unitSystem);
runOnUiThread(() -> { runOnUiThread(() -> {
if (isResumed()) { if (isResumed()) {
viewBinding.chartView.requestLayout(); viewBinding.chartView.requestLayout();
@@ -189,7 +190,7 @@ public class ChartFragment extends Fragment implements TrackDataHub.Listener {
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics trackStatistics, Speed smoothedSpeed, Altitude smoothedAltitude) { public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics trackStatistics, Speed smoothedSpeed, Altitude smoothedAltitude) {
if (isResumed()) { if (isResumed()) {
ChartPoint point = new ChartPoint(trackStatistics, trackPoint, smoothedSpeed, smoothedAltitude, chartByDistance, viewBinding.chartView.getMetricUnits()); ChartPoint point = new ChartPoint(trackStatistics, trackPoint, smoothedSpeed, smoothedAltitude, chartByDistance, viewBinding.chartView.getUnitSystem());
pendingPoints.add(point); pendingPoints.add(point);
} }
} }
@@ -8,6 +8,7 @@ import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.data.models.UnitConversions; import de.dennisguse.opentracks.data.models.UnitConversions;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
public class ChartPoint { public class ChartPoint {
@@ -28,20 +29,20 @@ public class ChartPoint {
this.altitude = altitude; this.altitude = altitude;
} }
public ChartPoint(@NonNull TrackStatistics trackStatistics, @NonNull TrackPoint trackPoint, Speed smoothedSpeed, Altitude smoothedAltitude, boolean chartByDistance, boolean metricUnits) { public ChartPoint(@NonNull TrackStatistics trackStatistics, @NonNull TrackPoint trackPoint, Speed smoothedSpeed, Altitude smoothedAltitude, boolean chartByDistance, UnitSystem unitSystem) {
if (chartByDistance) { if (chartByDistance) {
timeOrDistance = trackStatistics.getTotalDistance().toKM_Miles(metricUnits); timeOrDistance = trackStatistics.getTotalDistance().toKM_Miles(unitSystem);
} else { } else {
timeOrDistance = trackStatistics.getTotalTime().toMillis(); timeOrDistance = trackStatistics.getTotalTime().toMillis();
} }
if (smoothedAltitude != null) { if (smoothedAltitude != null) {
altitude = Distance.of(smoothedAltitude.toM()).toM_FT(metricUnits); altitude = Distance.of(smoothedAltitude.toM()).toM_FT(unitSystem);
} }
if (smoothedSpeed != null) { if (smoothedSpeed != null) {
speed = smoothedSpeed.to(metricUnits); speed = smoothedSpeed.to(unitSystem);
pace = smoothedSpeed.toPace(metricUnits).toMillis() * UnitConversions.MS_TO_S * UnitConversions.S_TO_MIN; pace = smoothedSpeed.toPace(unitSystem).toMillis() * UnitConversions.MS_TO_S * UnitConversions.S_TO_MIN;
} }
if (trackPoint.hasHeartRate()) { if (trackPoint.hasHeartRate()) {
heartRate = (double) trackPoint.getHeartRate().getBPM(); heartRate = (double) trackPoint.getHeartRate().getBPM();
@@ -27,6 +27,7 @@ import androidx.annotation.NonNull;
import java.text.NumberFormat; import java.text.NumberFormat;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.ExtremityMonitor; import de.dennisguse.opentracks.stats.ExtremityMonitor;
/** /**
@@ -217,11 +218,15 @@ abstract class ChartValueSeries {
return maxMarkerValue; return maxMarkerValue;
} }
/** int getTitleId(UnitSystem unitSystem) {
* Gets the title id. switch (unitSystem) {
*/ case METRIC:
int getTitleId(boolean metricUnits) { return metricTitleId;
return metricUnits ? metricTitleId : imperialTitleId; case IMPERIAL:
return imperialTitleId;
default:
throw new RuntimeException("Not implemented");
}
} }
/** /**
@@ -46,6 +46,7 @@ import java.util.List;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.ExtremityMonitor; import de.dennisguse.opentracks.stats.ExtremityMonitor;
import de.dennisguse.opentracks.ui.markers.MarkerDetailActivity; import de.dennisguse.opentracks.ui.markers.MarkerDetailActivity;
import de.dennisguse.opentracks.ui.markers.MarkerUtils; import de.dennisguse.opentracks.ui.markers.MarkerUtils;
@@ -116,7 +117,7 @@ public class ChartView extends View {
private int effectiveHeight = 0; private int effectiveHeight = 0;
private boolean chartByDistance = false; private boolean chartByDistance = false;
private boolean metricUnits = true; private UnitSystem unitSystem = UnitSystem.defaultUnitSystem();
private boolean reportSpeed = true; private boolean reportSpeed = true;
private boolean showPointer = false; private boolean showPointer = false;
@@ -371,17 +372,12 @@ public class ChartView extends View {
this.chartByDistance = chartByDistance; this.chartByDistance = chartByDistance;
} }
public boolean getMetricUnits() { public UnitSystem getUnitSystem() {
return metricUnits; return unitSystem;
} }
/** public void setUnitSystem(UnitSystem value) {
* Sets metric units. unitSystem = value;
*
* @param value true to use metric units
*/
public void setMetricUnits(boolean value) {
metricUnits = value;
} }
public boolean getReportSpeed() { public boolean getReportSpeed() {
@@ -677,7 +673,7 @@ public class ChartView extends View {
for (ChartValueSeries chartValueSeries : seriesList) { for (ChartValueSeries chartValueSeries : seriesList) {
if (chartValueSeries.isEnabled() && chartValueSeries.hasData() || allowIfEmpty(chartValueSeries)) { if (chartValueSeries.isEnabled() && chartValueSeries.hasData() || allowIfEmpty(chartValueSeries)) {
count++; count++;
String title = getContext().getString(chartValueSeries.getTitleId(metricUnits)); String title = getContext().getString(chartValueSeries.getTitleId(unitSystem));
Paint paint = chartValueSeries.getTitlePaint(); Paint paint = chartValueSeries.getTitlePaint();
int x = (int) (0.5 * width) + getScrollX(); int x = (int) (0.5 * width) + getScrollX();
int y = topBorder - spacer - (lines - count) * (lineHeight + spacer); int y = topBorder - spacer - (lines - count) * (lineHeight + spacer);
@@ -696,7 +692,7 @@ public class ChartView extends View {
for (ChartValueSeries chartValueSeries : seriesList) { for (ChartValueSeries chartValueSeries : seriesList) {
if (chartValueSeries.isEnabled() && chartValueSeries.hasData() || allowIfEmpty(chartValueSeries)) { if (chartValueSeries.isEnabled() && chartValueSeries.hasData() || allowIfEmpty(chartValueSeries)) {
lines++; lines++;
String title = getContext().getString(chartValueSeries.getTitleId(metricUnits)); String title = getContext().getString(chartValueSeries.getTitleId(unitSystem));
Rect rect = getRect(chartValueSeries.getTitlePaint(), title); Rect rect = getRect(chartValueSeries.getTitlePaint(), title);
if (rect.height() > lineHeight) { if (rect.height() > lineHeight) {
lineHeight = rect.height(); lineHeight = rect.height();
@@ -731,7 +727,14 @@ public class ChartView extends View {
private String getXAxisLabel() { private String getXAxisLabel() {
Context context = getContext(); Context context = getContext();
if (chartByDistance) { if (chartByDistance) {
return metricUnits ? context.getString(R.string.unit_kilometer) : context.getString(R.string.unit_mile); switch (unitSystem) {
case METRIC:
return context.getString(R.string.unit_kilometer);
case IMPERIAL:
return context.getString(R.string.unit_mile);
default:
throw new RuntimeException("Not implemented");
}
} else { } else {
return context.getString(R.string.description_time); return context.getString(R.string.description_time);
} }
@@ -986,7 +989,7 @@ public class ChartView extends View {
private double getMarkerXValue(Marker marker) { private double getMarkerXValue(Marker marker) {
if (chartByDistance) { if (chartByDistance) {
return marker.getLength().toKM_Miles(metricUnits); return marker.getLength().toKM_Miles(unitSystem);
} else { } else {
return marker.getDuration().toMillis(); return marker.getDuration().toMillis();
} }
@@ -5,6 +5,8 @@ import androidx.annotation.Nullable;
import java.util.Objects; import java.util.Objects;
import de.dennisguse.opentracks.settings.UnitSystem;
public class Distance { public class Distance {
public static Distance of(double distance_m) { public static Distance of(double distance_m) {
@@ -43,11 +45,14 @@ public class Distance {
return of(0.1 * distance_dm); return of(0.1 * distance_dm);
} }
public static Distance one(boolean metricUnit) { public static Distance one(UnitSystem unitSystem) {
if (metricUnit) { switch (unitSystem) {
return Distance.ofKilometer(1); case METRIC:
} else { return Distance.ofKilometer(1);
return Distance.ofMile(1); case IMPERIAL:
return Distance.ofMile(1);
default:
throw new RuntimeException("Not implemented");
} }
} }
@@ -109,12 +114,26 @@ public class Distance {
return toKM() * UnitConversions.KM_TO_MI; return toKM() * UnitConversions.KM_TO_MI;
} }
public double toKM_Miles(boolean metricUnit) { public double toKM_Miles(UnitSystem unitSystem) {
return metricUnit ? toKM() : toMI(); switch (unitSystem) {
case METRIC:
return toKM();
case IMPERIAL:
return toMI();
default:
throw new RuntimeException("Not implemented");
}
} }
public double toM_FT(boolean metricUnit) { public double toM_FT(UnitSystem unitSystem) {
return metricUnit ? toM() : toFT(); switch (unitSystem) {
case METRIC:
return toM();
case IMPERIAL:
return toFT();
default:
throw new RuntimeException("Not implemented");
}
} }
@Override @Override
@@ -4,7 +4,10 @@ import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.util.Pair; import android.util.Pair;
import androidx.annotation.Nullable;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
public class DistanceFormatter { public class DistanceFormatter {
@@ -13,12 +16,13 @@ public class DistanceFormatter {
private final int decimalCount; private final int decimalCount;
private final boolean metricUnits; private final UnitSystem unitSystem;
private DistanceFormatter(Resources resources, int decimalCount, boolean metricUnits) { private DistanceFormatter(Resources resources, int decimalCount, UnitSystem unitSystem) {
this.resources = resources; this.resources = resources;
this.decimalCount = decimalCount; this.decimalCount = decimalCount;
this.metricUnits = metricUnits; this.unitSystem = unitSystem;
assert unitSystem != null;
} }
public String formatDistance(Distance distance) { public String formatDistance(Distance distance) {
@@ -34,26 +38,35 @@ public class DistanceFormatter {
/** /**
* Get the formatted distance with unit. * Get the formatted distance with unit.
* *
* @param metricUnits true to use metric unit
* @return the formatted distance (or null) and it's unit as {@link Pair} * @return the formatted distance (or null) and it's unit as {@link Pair}
*/ */
public Pair<String, String> getDistanceParts(Distance distance) { public Pair<String, String> getDistanceParts(Distance distance) {
if (distance.isInvalid()) { if (distance.isInvalid()) {
return new Pair<>(null, resources.getString(metricUnits ? R.string.unit_meter : R.string.unit_feet)); switch (unitSystem) {
case METRIC:
return new Pair<>(null, resources.getString(R.string.unit_meter));
case IMPERIAL:
return new Pair<>(null, resources.getString(R.string.unit_feet));
default:
throw new RuntimeException("Not implemented");
}
} }
if (metricUnits) { switch (unitSystem) {
if (distance.greaterThan(Distance.of(500))) { case METRIC:
return new Pair<>(StringUtils.formatDecimal(distance.toKM(), decimalCount), resources.getString(R.string.unit_kilometer)); if (distance.greaterThan(Distance.of(500))) {
} else { return new Pair<>(StringUtils.formatDecimal(distance.toKM(), decimalCount), resources.getString(R.string.unit_kilometer));
return new Pair<>(StringUtils.formatDecimal(distance.toM(), decimalCount), resources.getString(R.string.unit_meter)); } else {
} return new Pair<>(StringUtils.formatDecimal(distance.toM(), decimalCount), resources.getString(R.string.unit_meter));
} else { }
if (distance.greaterThan(Distance.ofMile(0.5))) { case IMPERIAL:
return new Pair<>(StringUtils.formatDecimal(distance.toMI(), decimalCount), resources.getString(R.string.unit_mile)); if (distance.greaterThan(Distance.ofMile(0.5))) {
} else { return new Pair<>(StringUtils.formatDecimal(distance.toMI(), decimalCount), resources.getString(R.string.unit_mile));
return new Pair<>(StringUtils.formatDecimal(distance.toFT(), decimalCount), resources.getString(R.string.unit_feet)); } else {
} return new Pair<>(StringUtils.formatDecimal(distance.toFT(), decimalCount), resources.getString(R.string.unit_feet));
}
default:
throw new RuntimeException("Not implemented");
} }
} }
@@ -65,11 +78,10 @@ public class DistanceFormatter {
private int decimalCount; private int decimalCount;
private boolean metricUnits; private UnitSystem unitSystem;
public Builder() { public Builder() {
decimalCount = 2; decimalCount = 2;
metricUnits = true;
} }
public Builder setDecimalCount(int decimalCount) { public Builder setDecimalCount(int decimalCount) {
@@ -77,13 +89,13 @@ public class DistanceFormatter {
return this; return this;
} }
public Builder setMetricUnits(boolean metricUnits) { public Builder setUnit(@Nullable UnitSystem unitSystem) {
this.metricUnits = metricUnits; this.unitSystem = unitSystem;
return this; return this;
} }
public DistanceFormatter build(Resources resource) { public DistanceFormatter build(Resources resource) {
return new DistanceFormatter(resource, decimalCount, metricUnits); return new DistanceFormatter(resource, decimalCount, unitSystem);
} }
public DistanceFormatter build(Context context) { public DistanceFormatter build(Context context) {
@@ -6,6 +6,7 @@ import java.time.Duration;
import java.util.Objects; import java.util.Objects;
import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.settings.UnitSystem;
public class Speed { public class Speed {
@@ -92,35 +93,37 @@ public class Speed {
return toKMH() * UnitConversions.KM_TO_MI; return toKMH() * UnitConversions.KM_TO_MI;
} }
public Duration toPace(boolean metricUnit) { public Duration toPace(UnitSystem unitSystem) {
if (isZero()) { if (isZero()) {
return Duration.ofSeconds(0); return Duration.ofSeconds(0);
} }
double distance = speed_mps * (metricUnit ? UnitConversions.M_TO_KM : UnitConversions.M_TO_MI); double distance = speed_mps;
switch (unitSystem) { //TODO Can we use to(UnitSystem)?
case METRIC:
distance *= UnitConversions.M_TO_KM;
break;
case IMPERIAL:
distance *= UnitConversions.M_TO_MI;
break;
default:
throw new RuntimeException("Not implemented");
}
return Duration.ofSeconds(Math.round(1 / distance)); return Duration.ofSeconds(Math.round(1 / distance));
} }
public double to(boolean metricUnit) { public double to(UnitSystem unitSystem) {
return to(metricUnit ? Unit.KMH : Unit.MPH); switch (unitSystem) {
} case METRIC:
public double to(Unit unit) {
switch (unit) {
case KMH:
return toKMH(); return toKMH();
case MPH: case IMPERIAL:
return toMPH(); return toMPH();
default: default:
throw new RuntimeException("Not implemented"); throw new RuntimeException("Not implemented");
} }
} }
public enum Unit {
KMH,
MPH,
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
@@ -4,7 +4,10 @@ import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.util.Pair; import android.util.Pair;
import androidx.annotation.NonNull;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
public class SpeedFormatter { public class SpeedFormatter {
@@ -13,14 +16,14 @@ public class SpeedFormatter {
private final int decimalCount; private final int decimalCount;
private final boolean metricUnits; private final UnitSystem unitSystem;
private final boolean reportSpeedOrPace; private final boolean reportSpeedOrPace;
private SpeedFormatter(Resources resources, int decimalCount, boolean metricUnits, boolean reportSpeedOrPace) { private SpeedFormatter(Resources resources, int decimalCount, UnitSystem unitSystem, boolean reportSpeedOrPace) {
this.resources = resources; this.resources = resources;
this.decimalCount = decimalCount; this.decimalCount = decimalCount;
this.metricUnits = metricUnits; this.unitSystem = unitSystem;
this.reportSpeedOrPace = reportSpeedOrPace; this.reportSpeedOrPace = reportSpeedOrPace;
} }
@@ -37,11 +40,17 @@ public class SpeedFormatter {
*/ */
public Pair<String, String> getSpeedParts(Speed speed) { public Pair<String, String> getSpeedParts(Speed speed) {
int unitId; int unitId;
if (metricUnits) { switch (unitSystem) {
unitId = reportSpeedOrPace ? R.string.unit_kilometer_per_hour : R.string.unit_minute_per_kilometer; case METRIC:
} else { unitId = reportSpeedOrPace ? R.string.unit_kilometer_per_hour : R.string.unit_minute_per_kilometer;
unitId = reportSpeedOrPace ? R.string.unit_mile_per_hour : R.string.unit_minute_per_mile; break;
case IMPERIAL:
unitId = reportSpeedOrPace ? R.string.unit_mile_per_hour : R.string.unit_minute_per_mile;
break;
default:
throw new RuntimeException("Not implemented");
} }
String unitString = resources.getString(unitId); String unitString = resources.getString(unitId);
if (speed == null) { if (speed == null) {
@@ -49,10 +58,10 @@ public class SpeedFormatter {
} }
if (reportSpeedOrPace) { if (reportSpeedOrPace) {
return new Pair<>(StringUtils.formatDecimal(speed.to(metricUnits), 1), unitString); return new Pair<>(StringUtils.formatDecimal(speed.to(unitSystem), 1), unitString);
} }
int pace = (int) speed.toPace(metricUnits).getSeconds(); int pace = (int) speed.toPace(unitSystem).getSeconds();
int minutes = pace / 60; int minutes = pace / 60;
int seconds = pace % 60; int seconds = pace % 60;
@@ -67,13 +76,12 @@ public class SpeedFormatter {
private int decimalCount; private int decimalCount;
private boolean metricUnits; private UnitSystem unitSystem;
private boolean reportSpeedOrPace; private boolean reportSpeedOrPace;
public Builder() { public Builder() {
decimalCount = 2; decimalCount = 2;
metricUnits = true;
reportSpeedOrPace = true; reportSpeedOrPace = true;
} }
@@ -82,8 +90,8 @@ public class SpeedFormatter {
return this; return this;
} }
public Builder setMetricUnits(boolean metricUnits) { public Builder setUnit(@NonNull UnitSystem unitSystem) {
this.metricUnits = metricUnits; this.unitSystem = unitSystem;
return this; return this;
} }
@@ -93,7 +101,7 @@ public class SpeedFormatter {
} }
public SpeedFormatter build(Resources resource) { public SpeedFormatter build(Resources resource) {
return new SpeedFormatter(resource, decimalCount, metricUnits, reportSpeedOrPace); return new SpeedFormatter(resource, decimalCount, unitSystem, reportSpeedOrPace);
} }
public SpeedFormatter build(Context context) { public SpeedFormatter build(Context context) {
@@ -42,6 +42,7 @@ import de.dennisguse.opentracks.data.models.SpeedFormatter;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.databinding.StatisticsRecordedBinding; import de.dennisguse.opentracks.databinding.StatisticsRecordedBinding;
import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.SensorStatistics; import de.dennisguse.opentracks.stats.SensorStatistics;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
@@ -81,7 +82,7 @@ public class StatisticsRecordedFragment extends Fragment {
private StatisticsRecordedBinding viewBinding; private StatisticsRecordedBinding viewBinding;
private boolean preferenceMetricUnits; private UnitSystem unitSystem = UnitSystem.defaultUnitSystem();
private boolean preferenceReportSpeed; private boolean preferenceReportSpeed;
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> { private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
@@ -89,7 +90,7 @@ public class StatisticsRecordedFragment extends Fragment {
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) { if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
updateUInecessary = true; updateUInecessary = true;
preferenceMetricUnits = PreferencesUtils.isMetricUnits(); unitSystem = PreferencesUtils.getUnitSystem();
} }
if (PreferencesUtils.isKey(R.string.stats_rate_key, key) && track != null) { if (PreferencesUtils.isKey(R.string.stats_rate_key, key) && track != null) {
@@ -189,7 +190,7 @@ public class StatisticsRecordedFragment extends Fragment {
// Set total distance // Set total distance
{ {
Pair<String, String> parts = DistanceFormatter.Builder() Pair<String, String> parts = DistanceFormatter.Builder()
.setMetricUnits(preferenceMetricUnits) .setUnit(unitSystem)
.build(getContext()).getDistanceParts(trackStatistics.getTotalDistance()); .build(getContext()).getDistanceParts(trackStatistics.getTotalDistance());
viewBinding.statsDistanceValue.setText(parts.first); viewBinding.statsDistanceValue.setText(parts.first);
@@ -208,7 +209,7 @@ public class StatisticsRecordedFragment extends Fragment {
viewBinding.statsTotalTimeValue.setText(StringUtils.formatElapsedTime(trackStatistics.getTotalTime())); viewBinding.statsTotalTimeValue.setText(StringUtils.formatElapsedTime(trackStatistics.getTotalTime()));
} }
SpeedFormatter formatter = SpeedFormatter.Builder().setMetricUnits(preferenceMetricUnits).setReportSpeedOrPace(preferenceReportSpeed).build(getContext()); SpeedFormatter formatter = SpeedFormatter.Builder().setUnit(unitSystem).setReportSpeedOrPace(preferenceReportSpeed).build(getContext());
// Set average speed/pace // Set average speed/pace
{ {
viewBinding.statsAverageSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_average_speed : R.string.stats_average_pace); viewBinding.statsAverageSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_average_speed : R.string.stats_average_pace);
@@ -243,11 +244,11 @@ public class StatisticsRecordedFragment extends Fragment {
Pair<String, String> parts; Pair<String, String> parts;
parts = StringUtils.getAltitudeChangeParts(getContext(), altitudeGain_m, preferenceMetricUnits); parts = StringUtils.getAltitudeParts(getContext(), altitudeGain_m, unitSystem);
viewBinding.statsAltitudeGainValue.setText(parts.first); viewBinding.statsAltitudeGainValue.setText(parts.first);
viewBinding.statsAltitudeGainUnit.setText(parts.second); viewBinding.statsAltitudeGainUnit.setText(parts.second);
parts = StringUtils.getAltitudeChangeParts(getContext(), altitudeLoss_m, preferenceMetricUnits); parts = StringUtils.getAltitudeParts(getContext(), altitudeLoss_m, unitSystem);
viewBinding.statsAltitudeLossValue.setText(parts.first); viewBinding.statsAltitudeLossValue.setText(parts.first);
viewBinding.statsAltitudeLossUnit.setText(parts.second); viewBinding.statsAltitudeLossUnit.setText(parts.second);
@@ -25,6 +25,7 @@ import de.dennisguse.opentracks.services.RecordingData;
import de.dennisguse.opentracks.services.TrackRecordingService; import de.dennisguse.opentracks.services.TrackRecordingService;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection; import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout; import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
import de.dennisguse.opentracks.viewmodels.StatisticData; import de.dennisguse.opentracks.viewmodels.StatisticData;
import de.dennisguse.opentracks.viewmodels.StatisticsDataModel; import de.dennisguse.opentracks.viewmodels.StatisticsDataModel;
@@ -55,14 +56,14 @@ public class StatisticsRecordingFragment extends Fragment {
private StatisticsDataModel viewModel; private StatisticsDataModel viewModel;
private LiveData<List<StatisticData>> statisticsLiveData; private LiveData<List<StatisticData>> statisticsLiveData;
private boolean preferenceMetricUnits; private UnitSystem unitSystem = UnitSystem.defaultUnitSystem();
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> { private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
boolean updateUInecessary = false; boolean updateUInecessary = false;
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) { if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
updateUInecessary = true; updateUInecessary = true;
preferenceMetricUnits = PreferencesUtils.isMetricUnits(); unitSystem = PreferencesUtils.getUnitSystem();
} }
if (PreferencesUtils.isKey(R.string.stats_custom_layouts_key, key) || PreferencesUtils.isKey(R.string.stats_custom_layout_selected_layout_key, key)) { if (PreferencesUtils.isKey(R.string.stats_custom_layouts_key, key) || PreferencesUtils.isKey(R.string.stats_custom_layout_selected_layout_key, key)) {
@@ -157,7 +158,7 @@ public class StatisticsRecordingFragment extends Fragment {
private void updateUI() { private void updateUI() {
if (isResumed()) { if (isResumed()) {
viewModel.update(recordingData, layout, preferenceMetricUnits); viewModel.update(recordingData, layout, unitSystem);
} }
} }
@@ -24,6 +24,7 @@ import de.dennisguse.opentracks.data.models.SpeedFormatter;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.IntentUtils; import de.dennisguse.opentracks.util.IntentUtils;
@@ -42,7 +43,7 @@ class TrackRecordingServiceNotificationManager implements SharedPreferences.OnSh
private boolean previousLocationWasAccurate = true; private boolean previousLocationWasAccurate = true;
private Boolean metricUnits = null; private UnitSystem unitSystem = null;
TrackRecordingServiceNotificationManager(Context context) { TrackRecordingServiceNotificationManager(Context context) {
PreferencesUtils.registerOnSharedPreferenceChangeListener(this); PreferencesUtils.registerOnSharedPreferenceChangeListener(this);
@@ -85,7 +86,7 @@ class TrackRecordingServiceNotificationManager implements SharedPreferences.OnSh
String formattedAccuracy = context.getString(R.string.value_none); String formattedAccuracy = context.getString(R.string.value_none);
DistanceFormatter formatter = DistanceFormatter.Builder() DistanceFormatter formatter = DistanceFormatter.Builder()
.setMetricUnits(metricUnits) .setUnit(unitSystem)
.build(context); .build(context);
if (trackPoint.hasHorizontalAccuracy()) { if (trackPoint.hasHorizontalAccuracy()) {
formattedAccuracy = formatter.formatDistance(trackPoint.getHorizontalAccuracy()); formattedAccuracy = formatter.formatDistance(trackPoint.getHorizontalAccuracy());
@@ -97,7 +98,7 @@ class TrackRecordingServiceNotificationManager implements SharedPreferences.OnSh
} }
notificationBuilder.setContentTitle(context.getString(R.string.track_distance_notification, formatter.formatDistance(trackStatistics.getTotalDistance()))); notificationBuilder.setContentTitle(context.getString(R.string.track_distance_notification, formatter.formatDistance(trackStatistics.getTotalDistance())));
String formattedSpeed = SpeedFormatter.Builder().setMetricUnits(metricUnits).setReportSpeedOrPace(true).build(context).formatSpeed(trackPoint.getSpeed()); String formattedSpeed = SpeedFormatter.Builder().setUnit(unitSystem).setReportSpeedOrPace(true).build(context).formatSpeed(trackPoint.getSpeed());
notificationBuilder.setContentText(context.getString(R.string.track_speed_notification, formattedSpeed)); notificationBuilder.setContentText(context.getString(R.string.track_speed_notification, formattedSpeed));
notificationBuilder.setSubText(context.getString(R.string.track_recording_notification_accuracy, formattedAccuracy)); notificationBuilder.setSubText(context.getString(R.string.track_recording_notification_accuracy, formattedAccuracy));
updateNotification(); updateNotification();
@@ -149,8 +150,8 @@ class TrackRecordingServiceNotificationManager implements SharedPreferences.OnSh
notificationManager.cancel(NOTIFICATION_ID); notificationManager.cancel(NOTIFICATION_ID);
} }
void setMetricUnits(boolean metricUnits) { void setUnitSystem(UnitSystem unitSystem) {
this.metricUnits = metricUnits; this.unitSystem = unitSystem;
} }
private void updateNotification() { private void updateNotification() {
@@ -164,7 +165,7 @@ class TrackRecordingServiceNotificationManager implements SharedPreferences.OnSh
@Override @Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) { if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
setMetricUnits(PreferencesUtils.isMetricUnits()); setUnitSystem(PreferencesUtils.getUnitSystem());
} }
} }
} }
@@ -35,6 +35,7 @@ import de.dennisguse.opentracks.data.TrackPointIterator;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.SensorStatistics; import de.dennisguse.opentracks.stats.SensorStatistics;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.ui.intervals.IntervalStatistics; import de.dennisguse.opentracks.ui.intervals.IntervalStatistics;
@@ -165,12 +166,12 @@ public class VoiceAnnouncement {
} }
boolean isMetricUnits = PreferencesUtils.isMetricUnits(); UnitSystem unitSystem = PreferencesUtils.getUnitSystem();
boolean isReportSpeed = PreferencesUtils.isReportSpeed(track.getCategory()); boolean isReportSpeed = PreferencesUtils.isReportSpeed(track.getCategory());
//TODO Do not load all trackpoints for every announcement //TODO Do not load all trackpoints for every announcement
TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null); TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null);
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.one(isMetricUnits)); IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.one(unitSystem));
intervalStatistics.addTrackPoints(trackPointIterator); intervalStatistics.addTrackPoints(trackPointIterator);
IntervalStatistics.Interval lastInterval = intervalStatistics.getLastInterval(); IntervalStatistics.Interval lastInterval = intervalStatistics.getLastInterval();
SensorStatistics sensorStatistics = null; SensorStatistics sensorStatistics = null;
@@ -178,7 +179,7 @@ public class VoiceAnnouncement {
sensorStatistics = contentProviderUtils.getSensorStats(track.getId()); sensorStatistics = contentProviderUtils.getSensorStats(track.getId());
} }
Spannable announcement = VoiceAnnouncementUtils.getAnnouncement(context, track.getTrackStatistics(), isMetricUnits, isReportSpeed, lastInterval, sensorStatistics); Spannable announcement = VoiceAnnouncementUtils.getAnnouncement(context, track.getTrackStatistics(), unitSystem, isReportSpeed, lastInterval, sensorStatistics);
// We don't care about the utterance id. It is supplied here to force onUtteranceCompleted to be called. // We don't care about the utterance id. It is supplied here to force onUtteranceCompleted to be called.
tts.speak(announcement, TextToSpeech.QUEUE_FLUSH, null, "not used"); tts.speak(announcement, TextToSpeech.QUEUE_FLUSH, null, "not used");
@@ -16,6 +16,7 @@ import java.time.Duration;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.SensorStatistics; import de.dennisguse.opentracks.stats.SensorStatistics;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.ui.intervals.IntervalStatistics; import de.dennisguse.opentracks.ui.intervals.IntervalStatistics;
@@ -25,15 +26,37 @@ class VoiceAnnouncementUtils {
private VoiceAnnouncementUtils() { private VoiceAnnouncementUtils() {
} }
static Spannable getAnnouncement(Context context, TrackStatistics trackStatistics, boolean isMetricUnits, boolean isReportSpeed, @Nullable IntervalStatistics.Interval currentInterval, @Nullable SensorStatistics sensorStatistics) { static Spannable getAnnouncement(Context context, TrackStatistics trackStatistics, UnitSystem unitSystem, boolean isReportSpeed, @Nullable IntervalStatistics.Interval currentInterval, @Nullable SensorStatistics sensorStatistics) {
SpannableStringBuilder builder = new SpannableStringBuilder(); SpannableStringBuilder builder = new SpannableStringBuilder();
Distance distance = trackStatistics.getTotalDistance(); Distance distance = trackStatistics.getTotalDistance();
Speed distancePerTime = trackStatistics.getAverageMovingSpeed(); Speed distancePerTime = trackStatistics.getAverageMovingSpeed();
Speed currentDistancePerTime = currentInterval != null ? currentInterval.getSpeed() : null; Speed currentDistancePerTime = currentInterval != null ? currentInterval.getSpeed() : null;
int perUnitStringId = isMetricUnits ? R.string.voice_per_kilometer : R.string.voice_per_mile;
double distanceInUnit = distance.toKM_Miles(isMetricUnits); int perUnitStringId;
int distanceId = isMetricUnits ? R.plurals.voiceDistanceKilometers : R.plurals.voiceDistanceMiles; int distanceId;
int speedId;
String unitDistanceTTS;
String unitSpeedTTS;
switch (unitSystem) {
case METRIC:
perUnitStringId = R.string.voice_per_kilometer;
distanceId = R.plurals.voiceDistanceKilometers;
speedId = R.plurals.voiceSpeedKilometersPerHour;
unitDistanceTTS = "kilometer";
unitSpeedTTS = "kilometer per hour";
break;
case IMPERIAL:
perUnitStringId = R.string.voice_per_mile;
distanceId = R.plurals.voiceDistanceMiles;
speedId = R.plurals.voiceSpeedMilesPerHour;
unitDistanceTTS = "mile";
unitSpeedTTS = "mile per hour";
break;
default:
throw new RuntimeException("Not implemented");
}
double distanceInUnit = distance.toKM_Miles(unitSystem);
builder.append(context.getString(R.string.total_distance)); builder.append(context.getString(R.string.total_distance));
long distanceIntegerPart = (long) distanceInUnit; long distanceIntegerPart = (long) distanceInUnit;
@@ -41,7 +64,7 @@ class VoiceAnnouncementUtils {
String distanceFractionalPart = String.format("%.2f", (distanceInUnit - distanceIntegerPart)).substring(2); String distanceFractionalPart = String.format("%.2f", (distanceInUnit - distanceIntegerPart)).substring(2);
// Units should always be english singular for TTS. // Units should always be english singular for TTS.
// See https://developer.android.com/reference/android/text/style/TtsSpan?hl=en#TYPE_MEASURE // See https://developer.android.com/reference/android/text/style/TtsSpan?hl=en#TYPE_MEASURE
appendDecimalUnit(builder, context.getResources().getQuantityString(distanceId, getQuantityCount(distanceInUnit), distanceInUnit), distanceIntegerPart, distanceFractionalPart, isMetricUnits ? "kilometer" : "mile"); appendDecimalUnit(builder, context.getResources().getQuantityString(distanceId, getQuantityCount(distanceInUnit), distanceInUnit), distanceIntegerPart, distanceFractionalPart, unitDistanceTTS);
// Punctuation helps introduce natural pauses in TTS // Punctuation helps introduce natural pauses in TTS
builder.append("."); builder.append(".");
if (distance.isZero()) { if (distance.isZero()) {
@@ -56,19 +79,18 @@ class VoiceAnnouncementUtils {
} }
if (isReportSpeed) { if (isReportSpeed) {
int speedId = isMetricUnits ? R.plurals.voiceSpeedKilometersPerHour : R.plurals.voiceSpeedMilesPerHour; double speedInUnit = distancePerTime.to(unitSystem);
double speedInUnit = distancePerTime.to(isMetricUnits);
builder.append(" ") builder.append(" ")
.append(context.getString(R.string.speed)); .append(context.getString(R.string.speed));
long speedIntegerPart = (long) speedInUnit; long speedIntegerPart = (long) speedInUnit;
// Extract the decimal part // Extract the decimal part
String speedFractionalPart = String.format("%.1f", (speedInUnit - speedIntegerPart)).substring(2); String speedFractionalPart = String.format("%.1f", (speedInUnit - speedIntegerPart)).substring(2);
appendDecimalUnit(builder, context.getResources().getQuantityString(speedId, getQuantityCount(speedInUnit), speedInUnit), speedIntegerPart, speedFractionalPart, isMetricUnits ? "kilometer per hour" : "mile per hour"); appendDecimalUnit(builder, context.getResources().getQuantityString(speedId, getQuantityCount(speedInUnit), speedInUnit), speedIntegerPart, speedFractionalPart, unitSpeedTTS);
builder.append("."); builder.append(".");
if (currentDistancePerTime != null) { if (currentDistancePerTime != null) {
double currentDistancePerTimeInUnit = currentDistancePerTime.to(isMetricUnits); double currentDistancePerTimeInUnit = currentDistancePerTime.to(unitSystem);
if (currentDistancePerTimeInUnit > 0) { if (currentDistancePerTimeInUnit > 0) {
@@ -77,12 +99,12 @@ class VoiceAnnouncementUtils {
long currentDistanceIntegerPart = (long) currentDistancePerTimeInUnit; long currentDistanceIntegerPart = (long) currentDistancePerTimeInUnit;
// Extract the decimal part // Extract the decimal part
String currentDistanceFractionalPart = String.format("%.1f", (currentDistancePerTimeInUnit - currentDistanceIntegerPart)).substring(2); String currentDistanceFractionalPart = String.format("%.1f", (currentDistancePerTimeInUnit - currentDistanceIntegerPart)).substring(2);
appendDecimalUnit(builder, context.getResources().getQuantityString(speedId, getQuantityCount(currentDistancePerTimeInUnit), currentDistancePerTimeInUnit), currentDistanceIntegerPart, currentDistanceFractionalPart, isMetricUnits ? "kilometer per hour" : "mile per hour"); appendDecimalUnit(builder, context.getResources().getQuantityString(speedId, getQuantityCount(currentDistancePerTimeInUnit), currentDistancePerTimeInUnit), currentDistanceIntegerPart, currentDistanceFractionalPart, unitSpeedTTS);
builder.append("."); builder.append(".");
} }
} }
} else { } else {
Duration time = distancePerTime.toPace(isMetricUnits); Duration time = distancePerTime.toPace(unitSystem);
builder.append(" ") builder.append(" ")
.append(context.getString(R.string.pace)); .append(context.getString(R.string.pace));
appendDuration(context, builder, time); appendDuration(context, builder, time);
@@ -90,7 +112,7 @@ class VoiceAnnouncementUtils {
.append(context.getString(perUnitStringId)) .append(context.getString(perUnitStringId))
.append("."); .append(".");
Duration currentTime = currentDistancePerTime != null ? currentDistancePerTime.toPace(isMetricUnits) : Duration.ofMillis(0); Duration currentTime = currentDistancePerTime != null ? currentDistancePerTime.toPace(unitSystem) : Duration.ofMillis(0);
if (!currentTime.isZero()) { if (!currentTime.isZero()) {
builder.append(" ") builder.append(" ")
.append(context.getString(R.string.lap_time)); .append(context.getString(R.string.lap_time));
@@ -64,10 +64,23 @@ public class DefaultsSettingsFragment extends PreferenceFragmentCompat implement
} }
private void updateUnits() { private void updateUnits() {
boolean metricUnits = PreferencesUtils.isMetricUnits(); UnitSystem unitSystem = PreferencesUtils.getUnitSystem();
ListPreference statsRatePreferences = findPreference(getString(R.string.stats_rate_key)); ListPreference statsRatePreferences = findPreference(getString(R.string.stats_rate_key));
String[] entries = getResources().getStringArray(metricUnits ? R.array.stats_rate_metric_options : R.array.stats_rate_imperial_options);
int entriesId;
switch (unitSystem) {
case METRIC:
entriesId = R.array.stats_rate_metric_options;
break;
case IMPERIAL:
entriesId = R.array.stats_rate_imperial_options;
break;
default:
throw new RuntimeException("Not implemented");
}
String[] entries = getResources().getStringArray(entriesId);
statsRatePreferences.setEntries(entries); statsRatePreferences.setEntries(entries);
HackUtils.invalidatePreference(statsRatePreferences); HackUtils.invalidatePreference(statsRatePreferences);
@@ -17,11 +17,11 @@ public class GpsSettingsFragment extends PreferenceFragmentCompat {
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.settings_gps); addPreferencesFromResource(R.xml.settings_gps);
boolean metricUnits = PreferencesUtils.isMetricUnits(); UnitSystem unitSystem = PreferencesUtils.getUnitSystem();
final DistanceFormatter formatter = DistanceFormatter.Builder() final DistanceFormatter formatter = DistanceFormatter.Builder()
.setDecimalCount(0) .setDecimalCount(0)
.setMetricUnits(metricUnits) .setUnit(unitSystem)
.build(getContext()); .build(getContext());
findPreference(getString(R.string.recording_distance_interval_key)) findPreference(getString(R.string.recording_distance_interval_key))
@@ -32,7 +32,9 @@ import androidx.preference.PreferenceManager;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -193,19 +195,30 @@ public class PreferencesUtils {
return getBoolean(R.string.publicapi_dashboard_enabled_key, resources.getBoolean(R.bool.publicapi_dashboard_enabled_default)); return getBoolean(R.string.publicapi_dashboard_enabled_key, resources.getBoolean(R.bool.publicapi_dashboard_enabled_default));
} }
public static boolean isMetricUnits() { public static UnitSystem getUnitSystem() {
final String STATS_UNIT = resources.getString(R.string.stats_units_default); final String STATS_UNIT_DEFAULT = resources.getString(R.string.stats_units_default);
return STATS_UNIT.equals(getString(R.string.stats_units_key, STATS_UNIT));
final String VALUE = getString(R.string.stats_units_key, STATS_UNIT_DEFAULT);
return Arrays.stream(UnitSystem.values())
.filter(d -> VALUE.equals(getString(d.getPreferenceId(), STATS_UNIT_DEFAULT)))
.findFirst()
.orElse(UnitSystem.defaultUnitSystem()); //TODO This AGAIN defines the default
} }
public static void setMetricUnits(boolean metricUnits) { public static void setUnit(UnitSystem unitSystem) {
String unit; setString(R.string.stats_units_key, unitSystem.getPreferenceId());
if (metricUnits) { }
unit = resources.getString(R.string.stats_units_metric);
} else { //TODO Check if actually needed or can be superseeded by a flexible default in getUnit()
unit = resources.getString(R.string.stats_units_imperial); public static void applyDefaultUnit() {
if (getString(R.string.stats_units_key, "").equals("")) {
if (!Locale.US.equals(Locale.getDefault())) {
setUnit(UnitSystem.METRIC);
} else {
setUnit(UnitSystem.IMPERIAL);
}
} }
setString(R.string.stats_units_key, unit);
} }
public static boolean isReportSpeed(String category) { public static boolean isReportSpeed(String category) {
@@ -292,31 +305,31 @@ public class PreferencesUtils {
} }
/** /**
* @return Result depends on isMetricUnits * @return Result depends on getUnitSystem
*/ */
public static Distance getVoiceAnnouncementDistance() { public static Distance getVoiceAnnouncementDistance() {
final float DEFAULT = Integer.parseInt(resources.getString(R.string.voice_announcement_distance_default)); final float DEFAULT = Integer.parseInt(resources.getString(R.string.voice_announcement_distance_default));
float value = getFloat(R.string.voice_announcement_distance_key, DEFAULT); float value = getFloat(R.string.voice_announcement_distance_key, DEFAULT);
return Distance.one(isMetricUnits()).multipliedBy(value); return Distance.one(getUnitSystem()).multipliedBy(value);
} }
/** /**
* @return Result depends on isMetricUnits * @return Result depends on getUnitSystem
*/ */
static String[] getVoiceAnnouncementDistanceEntries() { static String[] getVoiceAnnouncementDistanceEntries() {
String[] values = resources.getStringArray(R.array.voice_announcement_distance_values); String[] values = resources.getStringArray(R.array.voice_announcement_distance_values);
String[] options = new String[values.length]; String[] options = new String[values.length];
boolean metricUnits = isMetricUnits(); UnitSystem unitSystem = getUnitSystem();
DistanceFormatter formatter = DistanceFormatter.Builder() DistanceFormatter formatter = DistanceFormatter.Builder()
.setDecimalCount(0) .setDecimalCount(0)
.setMetricUnits(metricUnits) .setUnit(unitSystem)
.build(resources); .build(resources);
for (int i = 0; i < values.length; i++) { for (int i = 0; i < values.length; i++) {
if (resources.getString(R.string.announcement_off).equals(values[i])) { if (resources.getString(R.string.announcement_off).equals(values[i])) {
options[i] = resources.getString(R.string.value_off); options[i] = resources.getString(R.string.value_off);
} else { } else {
Distance distance = Distance.one(metricUnits).multipliedBy(Double.parseDouble(values[i])); Distance distance = Distance.one(unitSystem).multipliedBy(Double.parseDouble(values[i]));
options[i] = formatter.formatDistance(distance); options[i] = formatter.formatDistance(distance);
} }
} }
@@ -354,10 +367,10 @@ public class PreferencesUtils {
String[] entries = new String[entryValues.length]; String[] entries = new String[entryValues.length];
final int recordingDistanceIntervalDefault = (int) getRecordingDistanceIntervalDefault().toM(); final int recordingDistanceIntervalDefault = (int) getRecordingDistanceIntervalDefault().toM();
boolean metricUnits = isMetricUnits(); UnitSystem unitSystem = getUnitSystem();
DistanceFormatter formatter = DistanceFormatter.Builder() DistanceFormatter formatter = DistanceFormatter.Builder()
.setMetricUnits(metricUnits) .setUnit(unitSystem)
.setDecimalCount(0) .setDecimalCount(0)
.build(resources); .build(resources);
for (int i = 0; i < entryValues.length; i++) { for (int i = 0; i < entryValues.length; i++) {
@@ -365,18 +378,23 @@ public class PreferencesUtils {
Distance distance = Distance.of(1).multipliedBy(value); Distance distance = Distance.of(1).multipliedBy(value);
String displayValue = formatter.formatDistance(distance); String displayValue = formatter.formatDistance(distance);
if (metricUnits) { switch (unitSystem) {
if (value == recordingDistanceIntervalDefault) { case METRIC:
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value); if (value == recordingDistanceIntervalDefault) {
} else { entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
entries[i] = displayValue; } else {
} entries[i] = displayValue;
} else { }
if (value == recordingDistanceIntervalDefault) { break;
entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT()); case IMPERIAL:
} else { if (value == recordingDistanceIntervalDefault) {
entries[i] = displayValue; entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT());
} } else {
entries[i] = displayValue;
}
break;
default:
throw new RuntimeException("Not implemented");
} }
} }
@@ -393,29 +411,34 @@ public class PreferencesUtils {
String[] entries = new String[entryValues.length]; String[] entries = new String[entryValues.length];
final int maxRecordingDistanceDefault = Integer.parseInt(resources.getString(R.string.max_recording_distance_default)); final int maxRecordingDistanceDefault = Integer.parseInt(resources.getString(R.string.max_recording_distance_default));
boolean metricUnits = isMetricUnits(); UnitSystem unitSystem = getUnitSystem();
DistanceFormatter formatter = DistanceFormatter.Builder() DistanceFormatter formatter = DistanceFormatter.Builder()
.setDecimalCount(0) .setDecimalCount(0)
.setMetricUnits(metricUnits) .setUnit(unitSystem)
.build(resources); .build(resources);
for (int i = 0; i < entryValues.length; i++) { for (int i = 0; i < entryValues.length; i++) {
int value = Integer.parseInt(entryValues[i]); int value = Integer.parseInt(entryValues[i]);
Distance distance = Distance.of(1).multipliedBy(value); Distance distance = Distance.of(1).multipliedBy(value);
String displayValue = formatter.formatDistance(distance); String displayValue = formatter.formatDistance(distance);
if (metricUnits) { switch (unitSystem) {
if (value == maxRecordingDistanceDefault) { case METRIC:
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value); if (value == maxRecordingDistanceDefault) {
} else { entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
entries[i] = displayValue; } else {
} entries[i] = displayValue;
} else { }
if (value == maxRecordingDistanceDefault) { break;
entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT()); case IMPERIAL:
} else { if (value == maxRecordingDistanceDefault) {
entries[i] = displayValue; entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT());
} } else {
entries[i] = displayValue;
}
break;
default:
throw new RuntimeException("Not implemented");
} }
} }
@@ -460,11 +483,11 @@ public class PreferencesUtils {
final int recordingGPSAccuracyExcellent = Integer.parseInt(resources.getString(R.string.recording_gps_accuracy_excellent)); final int recordingGPSAccuracyExcellent = Integer.parseInt(resources.getString(R.string.recording_gps_accuracy_excellent));
final int recordingGPSAccuracyPoor = Integer.parseInt(resources.getString(R.string.recording_gps_accuracy_poor)); final int recordingGPSAccuracyPoor = Integer.parseInt(resources.getString(R.string.recording_gps_accuracy_poor));
boolean metricUnits = isMetricUnits(); UnitSystem unitSystem = getUnitSystem();
DistanceFormatter formatter = DistanceFormatter.Builder() DistanceFormatter formatter = DistanceFormatter.Builder()
.setDecimalCount(0) .setDecimalCount(0)
.setMetricUnits(metricUnits) .setUnit(unitSystem)
.build(resources); .build(resources);
for (int i = 0; i < entryValues.length; i++) { for (int i = 0; i < entryValues.length; i++) {
@@ -472,24 +495,29 @@ public class PreferencesUtils {
Distance distance = Distance.of(1).multipliedBy(value); Distance distance = Distance.of(1).multipliedBy(value);
String displayValue = formatter.formatDistance(distance); String displayValue = formatter.formatDistance(distance);
if (metricUnits) { switch (unitSystem) {
if (value == recordingGPSAccuracyDefault) { case METRIC:
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value); if (value == recordingGPSAccuracyDefault) {
} else if (value == recordingGPSAccuracyExcellent) { entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
entries[i] = resources.getString(R.string.value_integer_meter_excellent_gps, value); } else if (value == recordingGPSAccuracyExcellent) {
} else if (value == recordingGPSAccuracyPoor) { entries[i] = resources.getString(R.string.value_integer_meter_excellent_gps, value);
entries[i] = resources.getString(R.string.value_integer_meter_poor_gps, value); } else if (value == recordingGPSAccuracyPoor) {
} else { entries[i] = resources.getString(R.string.value_integer_meter_poor_gps, value);
entries[i] = displayValue; } else {
} entries[i] = displayValue;
} else { }
if (value == recordingGPSAccuracyDefault) { break;
entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT()); case IMPERIAL:
} else if (value == recordingGPSAccuracyExcellent) { if (value == recordingGPSAccuracyDefault) {
entries[i] = resources.getString(R.string.value_integer_feet_excellent_gps, (int) distance.toFT()); entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT());
} else { } else if (value == recordingGPSAccuracyExcellent) {
entries[i] = displayValue; entries[i] = resources.getString(R.string.value_integer_feet_excellent_gps, (int) distance.toFT());
} } else {
entries[i] = displayValue;
}
break;
default:
throw new RuntimeException("Not implemented");
} }
} }
@@ -509,24 +537,30 @@ public class PreferencesUtils {
final float idleSpeedDefault = Float.parseFloat(resources.getString(R.string.idle_speed_default)); final float idleSpeedDefault = Float.parseFloat(resources.getString(R.string.idle_speed_default));
boolean metricUnits = isMetricUnits(); UnitSystem unitSystem = getUnitSystem();
for (int i = 0; i < entryValues.length; i++) { for (int i = 0; i < entryValues.length; i++) {
float value = Float.parseFloat(entryValues[i]); float value = Float.parseFloat(entryValues[i]);
if (metricUnits) {
if (value == idleSpeedDefault) {
entries[i] = resources.getString(R.string.value_float_kilometer_hour_recommended, value);
} else {
entries[i] = resources.getString(R.string.value_float_kilometer_hour, value);
}
} else {
double valueMPH = Speed.ofKMH(value).toMPH();
if (value == idleSpeedDefault) { switch (unitSystem) {
entries[i] = resources.getString(R.string.value_float_mile_hour_recommended, valueMPH); case METRIC:
} else { if (value == idleSpeedDefault) {
entries[i] = resources.getString(R.string.value_float_mile_hour, valueMPH); entries[i] = resources.getString(R.string.value_float_kilometer_hour_recommended, value);
} } else {
entries[i] = resources.getString(R.string.value_float_kilometer_hour, value);
}
break;
case IMPERIAL:
double valueMPH = Speed.ofKMH(value).toMPH();
if (value == idleSpeedDefault) {
entries[i] = resources.getString(R.string.value_float_mile_hour_recommended, valueMPH);
} else {
entries[i] = resources.getString(R.string.value_float_mile_hour, valueMPH);
}
break;
default:
throw new RuntimeException("Not implemented");
} }
} }
@@ -0,0 +1,23 @@
package de.dennisguse.opentracks.settings;
import de.dennisguse.opentracks.R;
public enum UnitSystem {
METRIC(R.string.stats_units_metric),
IMPERIAL(R.string.stats_units_imperial);
private final int preferenceIdentifier;
UnitSystem(int preferenceIdentifier) {
this.preferenceIdentifier = preferenceIdentifier;
}
public int getPreferenceId() {
return preferenceIdentifier;
}
@Deprecated //TODO used to initialize before loading from preferences; should be loaded first
public static UnitSystem defaultUnitSystem() {
return METRIC;
}
}
@@ -31,6 +31,7 @@ import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.SpeedFormatter; import de.dennisguse.opentracks.data.models.SpeedFormatter;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.UnitConversions; import de.dennisguse.opentracks.data.models.UnitConversions;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
@@ -210,8 +211,8 @@ public class DescriptionGenerator {
*/ */
@VisibleForTesting @VisibleForTesting
void writePace(Speed speed, StringBuilder builder, int resId, String lineBreak) { void writePace(Speed speed, StringBuilder builder, int resId, String lineBreak) {
Pair<String, String> paceInMetrics = SpeedFormatter.Builder().setMetricUnits(true).setReportSpeedOrPace(false).build(context).getSpeedParts(speed); Pair<String, String> paceInMetrics = SpeedFormatter.Builder().setUnit(UnitSystem.METRIC).setReportSpeedOrPace(false).build(context).getSpeedParts(speed);
Pair<String, String> paceInImperial = SpeedFormatter.Builder().setMetricUnits(false).setReportSpeedOrPace(false).build(context).getSpeedParts(speed); Pair<String, String> paceInImperial = SpeedFormatter.Builder().setUnit(UnitSystem.IMPERIAL).setReportSpeedOrPace(false).build(context).getSpeedParts(speed);
String formattedPaceMetrics = paceInMetrics.first != null ? paceInMetrics.first : context.getString(R.string.value_unknown); String formattedPaceMetrics = paceInMetrics.first != null ? paceInMetrics.first : context.getString(R.string.value_unknown);
String formattedPaceImperial = paceInImperial.first != null ? paceInImperial.first : context.getString(R.string.value_unknown); String formattedPaceImperial = paceInImperial.first != null ? paceInImperial.first : context.getString(R.string.value_unknown);
@@ -16,6 +16,7 @@ import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.models.DistanceFormatter; import de.dennisguse.opentracks.data.models.DistanceFormatter;
import de.dennisguse.opentracks.data.models.SpeedFormatter; import de.dennisguse.opentracks.data.models.SpeedFormatter;
import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
import de.dennisguse.opentracks.util.TrackIconUtils; import de.dennisguse.opentracks.util.TrackIconUtils;
@@ -99,7 +100,7 @@ public class AggregatedStatisticsAdapter extends BaseAdapter {
private final TextView maxSpeedUnit; private final TextView maxSpeedUnit;
private final TextView maxSpeedLabel; private final TextView maxSpeedLabel;
private boolean metricsUnits; private UnitSystem unitSystem = UnitSystem.defaultUnitSystem();
private boolean reportSpeed; private boolean reportSpeed;
public ViewHolder(View view) { public ViewHolder(View view) {
@@ -121,7 +122,7 @@ public class AggregatedStatisticsAdapter extends BaseAdapter {
public void setSpeed(AggregatedStatistics.AggregatedStatistic aggregatedStatistic) { public void setSpeed(AggregatedStatistics.AggregatedStatistic aggregatedStatistic) {
setCommonValues(aggregatedStatistic); setCommonValues(aggregatedStatistic);
SpeedFormatter formatter = SpeedFormatter.Builder().setMetricUnits(metricsUnits).setReportSpeedOrPace(reportSpeed).build(context); SpeedFormatter formatter = SpeedFormatter.Builder().setUnit(unitSystem).setReportSpeedOrPace(reportSpeed).build(context);
{ {
Pair<String, String> parts = formatter.getSpeedParts(aggregatedStatistic.getTrackStatistics().getAverageMovingSpeed()); Pair<String, String> parts = formatter.getSpeedParts(aggregatedStatistic.getTrackStatistics().getAverageMovingSpeed());
avgSpeed.setText(parts.first); avgSpeed.setText(parts.first);
@@ -138,7 +139,7 @@ public class AggregatedStatisticsAdapter extends BaseAdapter {
} }
public void setPace(AggregatedStatistics.AggregatedStatistic aggregatedStatistic) { public void setPace(AggregatedStatistics.AggregatedStatistic aggregatedStatistic) {
SpeedFormatter formatter = SpeedFormatter.Builder().setMetricUnits(metricsUnits).setReportSpeedOrPace(reportSpeed).build(context); SpeedFormatter formatter = SpeedFormatter.Builder().setUnit(unitSystem).setReportSpeedOrPace(reportSpeed).build(context);
setCommonValues(aggregatedStatistic); setCommonValues(aggregatedStatistic);
{ {
@@ -161,14 +162,14 @@ public class AggregatedStatisticsAdapter extends BaseAdapter {
String category = aggregatedStatistic.getCategory(); String category = aggregatedStatistic.getCategory();
reportSpeed = PreferencesUtils.isReportSpeed(category); reportSpeed = PreferencesUtils.isReportSpeed(category);
metricsUnits = PreferencesUtils.isMetricUnits(); unitSystem = PreferencesUtils.getUnitSystem();
sportIcon.setImageResource(getIcon(aggregatedStatistic)); sportIcon.setImageResource(getIcon(aggregatedStatistic));
typeLabel.setText(category); typeLabel.setText(category);
numTracks.setText(StringUtils.valueInParentheses(String.valueOf(aggregatedStatistic.getCountTracks()))); numTracks.setText(StringUtils.valueInParentheses(String.valueOf(aggregatedStatistic.getCountTracks())));
Pair<String, String> parts = DistanceFormatter.Builder() Pair<String, String> parts = DistanceFormatter.Builder()
.setMetricUnits(metricsUnits) .setUnit(unitSystem)
.build(context).getDistanceParts(aggregatedStatistic.getTrackStatistics().getTotalDistance()); .build(context).getDistanceParts(aggregatedStatistic.getTrackStatistics().getTotalDistance());
distance.setText(parts.first); distance.setText(parts.first);
distanceUnit.setText(parts.second); distanceUnit.setText(parts.second);
@@ -15,6 +15,7 @@ import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.DistanceFormatter; import de.dennisguse.opentracks.data.models.DistanceFormatter;
import de.dennisguse.opentracks.data.models.SpeedFormatter; import de.dennisguse.opentracks.data.models.SpeedFormatter;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
public class IntervalStatisticsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { public class IntervalStatisticsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
@@ -22,11 +23,11 @@ public class IntervalStatisticsAdapter extends RecyclerView.Adapter<RecyclerView
private List<IntervalStatistics.Interval> intervalList; private List<IntervalStatistics.Interval> intervalList;
private final Context context; private final Context context;
private final StackMode stackMode; private final StackMode stackMode;
private boolean metricUnits; private UnitSystem unitSystem = UnitSystem.defaultUnitSystem();
private boolean isReportSpeed; private boolean isReportSpeed;
public IntervalStatisticsAdapter(Context context, StackMode stackMode, boolean metricUnits, boolean isReportSpeed) { public IntervalStatisticsAdapter(Context context, StackMode stackMode, UnitSystem unitSystem, boolean isReportSpeed) {
this.metricUnits = metricUnits; this.unitSystem = unitSystem;
this.context = context; this.context = context;
this.stackMode = stackMode; this.stackMode = stackMode;
this.isReportSpeed = isReportSpeed; this.isReportSpeed = isReportSpeed;
@@ -57,14 +58,14 @@ public class IntervalStatisticsAdapter extends RecyclerView.Adapter<RecyclerView
sumDistance = interval.getDistance().multipliedBy(nextPosition); sumDistance = interval.getDistance().multipliedBy(nextPosition);
} }
viewHolder.distance.setText(DistanceFormatter.Builder() viewHolder.distance.setText(DistanceFormatter.Builder()
.setMetricUnits(metricUnits) .setUnit(unitSystem)
.build(context).formatDistance(sumDistance)); .build(context).formatDistance(sumDistance));
SpeedFormatter formatter = SpeedFormatter.Builder().setMetricUnits(metricUnits).setReportSpeedOrPace(isReportSpeed).build(context); SpeedFormatter formatter = SpeedFormatter.Builder().setUnit(unitSystem).setReportSpeedOrPace(isReportSpeed).build(context);
viewHolder.rate.setText(formatter.formatSpeed(interval.getSpeed())); viewHolder.rate.setText(formatter.formatSpeed(interval.getSpeed()));
viewHolder.gain.setText(StringUtils.formatAltitudeChange(context, interval.getGain_m(), metricUnits)); viewHolder.gain.setText(StringUtils.formatAltitude(context, interval.getGain_m(), unitSystem));
viewHolder.loss.setText(StringUtils.formatAltitudeChange(context, interval.getLoss_m(), metricUnits)); viewHolder.loss.setText(StringUtils.formatAltitude(context, interval.getLoss_m(), unitSystem));
} }
@@ -76,8 +77,8 @@ public class IntervalStatisticsAdapter extends RecyclerView.Adapter<RecyclerView
return intervalList.size(); return intervalList.size();
} }
public List<IntervalStatistics.Interval> swapData(List<IntervalStatistics.Interval> data, boolean metricUnits, boolean isReportSpeed) { public List<IntervalStatistics.Interval> swapData(List<IntervalStatistics.Interval> data, UnitSystem unitSystem, boolean isReportSpeed) {
this.metricUnits = metricUnits; this.unitSystem = unitSystem;
this.isReportSpeed = isReportSpeed; this.isReportSpeed = isReportSpeed;
intervalList = data; intervalList = data;
@@ -22,6 +22,7 @@ import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.data.tables.TrackPointsColumns; import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
import de.dennisguse.opentracks.settings.UnitSystem;
/** /**
* This model is used to load intervals for a track. * This model is used to load intervals for a track.
@@ -64,14 +65,14 @@ public class IntervalStatisticsModel extends AndroidViewModel {
handler = null; handler = null;
} }
public MutableLiveData<List<IntervalStatistics.Interval>> getIntervalStats(Track.Id trackId, boolean metricUnits, @Nullable IntervalOption interval) { public MutableLiveData<List<IntervalStatistics.Interval>> getIntervalStats(Track.Id trackId, UnitSystem unitSystem, @Nullable IntervalOption interval) {
if (intervalsLiveData == null) { if (intervalsLiveData == null) {
if (interval == null) { if (interval == null) {
interval = IntervalOption.OPTION_1; interval = IntervalOption.OPTION_1;
} }
intervalsLiveData = new MutableLiveData<>(); intervalsLiveData = new MutableLiveData<>();
distanceInterval = interval.getDistance(metricUnits); distanceInterval = interval.getDistance(unitSystem);
intervalStatistics = new IntervalStatistics(distanceInterval); intervalStatistics = new IntervalStatistics(distanceInterval);
loadIntervalStatistics(trackId); loadIntervalStatistics(trackId);
@@ -104,13 +105,13 @@ public class IntervalStatisticsModel extends AndroidViewModel {
} }
} }
public void update(Track.Id trackId, boolean metricUnits, @Nullable IntervalOption interval) { public void update(Track.Id trackId, UnitSystem unitSystem, @Nullable IntervalOption interval) {
if (interval == null) { if (interval == null) {
interval = IntervalOption.DEFAULT; interval = IntervalOption.DEFAULT;
} }
lastTrackPointId = null; lastTrackPointId = null;
distanceInterval = interval.getDistance(metricUnits); distanceInterval = interval.getDistance(unitSystem);
intervalStatistics = new IntervalStatistics(distanceInterval); intervalStatistics = new IntervalStatistics(distanceInterval);
loadIntervalStatistics(trackId); loadIntervalStatistics(trackId);
} }
@@ -138,9 +139,9 @@ public class IntervalStatisticsModel extends AndroidViewModel {
this.multiplier = multiplier; this.multiplier = multiplier;
} }
public Distance getDistance(boolean metricUnits) { public Distance getDistance(UnitSystem unitSystem) {
return Distance return Distance
.one(metricUnits) .one(unitSystem)
.multipliedBy(multiplier); .multipliedBy(multiplier);
} }
@@ -24,6 +24,7 @@ import de.dennisguse.opentracks.data.models.DistanceFormatter;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.databinding.IntervalListViewBinding; import de.dennisguse.opentracks.databinding.IntervalListViewBinding;
import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.settings.UnitSystem;
/** /**
* A fragment to display the intervals from recorded track. * A fragment to display the intervals from recorded track.
@@ -41,7 +42,7 @@ public class IntervalsFragment extends Fragment {
private IntervalStatisticsModel.IntervalOption selectedInterval; private IntervalStatisticsModel.IntervalOption selectedInterval;
private Track.Id trackId; private Track.Id trackId;
private boolean metricUnits; private UnitSystem unitSystem = UnitSystem.defaultUnitSystem();
private IntervalStatisticsAdapter adapter; private IntervalStatisticsAdapter adapter;
private ArrayAdapter<IntervalStatisticsModel.IntervalOption> intervalsAdapter; private ArrayAdapter<IntervalStatisticsModel.IntervalOption> intervalsAdapter;
@@ -51,7 +52,7 @@ public class IntervalsFragment extends Fragment {
protected final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> { protected final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
if (PreferencesUtils.isKey(R.string.stats_units_key, key) || PreferencesUtils.isKey(R.string.stats_rate_key, key)) { if (PreferencesUtils.isKey(R.string.stats_units_key, key) || PreferencesUtils.isKey(R.string.stats_rate_key, key)) {
updateIntervals(PreferencesUtils.isMetricUnits(), selectedInterval); updateIntervals(PreferencesUtils.getUnitSystem(), selectedInterval);
if (intervalsAdapter != null) { if (intervalsAdapter != null) {
intervalsAdapter.notifyDataSetChanged(); intervalsAdapter.notifyDataSetChanged();
} }
@@ -101,14 +102,14 @@ public class IntervalsFragment extends Fragment {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
adapter = new IntervalStatisticsAdapter(getContext(), stackModeListView, metricUnits, isReportSpeed); adapter = new IntervalStatisticsAdapter(getContext(), stackModeListView, unitSystem, isReportSpeed);
viewBinding.intervalList.setLayoutManager(new LinearLayoutManager(getContext())); viewBinding.intervalList.setLayoutManager(new LinearLayoutManager(getContext()));
// TODO handle empty view: before we did viewBinding.intervalList.setEmptyView(viewBinding.intervalListEmptyView); // TODO handle empty view: before we did viewBinding.intervalList.setEmptyView(viewBinding.intervalListEmptyView);
viewBinding.intervalList.setAdapter(adapter); viewBinding.intervalList.setAdapter(adapter);
final DistanceFormatter formatter = DistanceFormatter.Builder() final DistanceFormatter formatter = DistanceFormatter.Builder()
.setDecimalCount(0) .setDecimalCount(0)
.setMetricUnits(metricUnits) .setUnit(unitSystem)
.build(getContext()); .build(getContext());
intervalsAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, IntervalStatisticsModel.IntervalOption.values()) { intervalsAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, IntervalStatisticsModel.IntervalOption.values()) {
@@ -118,7 +119,7 @@ public class IntervalsFragment extends Fragment {
TextView v = (TextView) super.getView(position, convertView, parent); TextView v = (TextView) super.getView(position, convertView, parent);
IntervalStatisticsModel.IntervalOption option = getItem(position); IntervalStatisticsModel.IntervalOption option = getItem(position);
String stringValue = formatter.formatDistance(option.getDistance(metricUnits)); String stringValue = formatter.formatDistance(option.getDistance(unitSystem));
v.setText(stringValue); v.setText(stringValue);
return v; return v;
} }
@@ -131,7 +132,7 @@ public class IntervalsFragment extends Fragment {
viewBinding.intervalsDropdown.setAdapter(intervalsAdapter); viewBinding.intervalsDropdown.setAdapter(intervalsAdapter);
viewBinding.intervalsDropdown.setOnItemClickListener((parent, view1, position, id) -> { viewBinding.intervalsDropdown.setOnItemClickListener((parent, view1, position, id) -> {
updateIntervals(metricUnits, IntervalStatisticsModel.IntervalOption.values()[position]); updateIntervals(unitSystem, IntervalStatisticsModel.IntervalOption.values()[position]);
//TODO This duplicates the intervalAdapter code //TODO This duplicates the intervalAdapter code
setIntervalsDropdownText(); setIntervalsDropdownText();
@@ -191,8 +192,8 @@ public class IntervalsFragment extends Fragment {
} }
viewBinding.intervalRate.setText(isReportSpeed ? getString(R.string.stats_speed) : getString(R.string.stats_pace)); viewBinding.intervalRate.setText(isReportSpeed ? getString(R.string.stats_speed) : getString(R.string.stats_pace));
LiveData<List<IntervalStatistics.Interval>> liveData = viewModel.getIntervalStats(trackId, metricUnits, selectedInterval); LiveData<List<IntervalStatistics.Interval>> liveData = viewModel.getIntervalStats(trackId, unitSystem, selectedInterval);
liveData.observe(getActivity(), intervalList -> adapter.swapData(intervalList, metricUnits, isReportSpeed)); liveData.observe(getActivity(), intervalList -> adapter.swapData(intervalList, unitSystem, isReportSpeed));
setIntervalsDropdownText(); setIntervalsDropdownText();
} }
@@ -200,23 +201,23 @@ public class IntervalsFragment extends Fragment {
private void setIntervalsDropdownText() { private void setIntervalsDropdownText() {
DistanceFormatter formatter = DistanceFormatter.Builder() DistanceFormatter formatter = DistanceFormatter.Builder()
.setDecimalCount(0) .setDecimalCount(0)
.setMetricUnits(metricUnits) .setUnit(unitSystem)
.build(getContext()); .build(getContext());
IntervalStatisticsModel.IntervalOption option = selectedInterval != null ? selectedInterval : IntervalStatisticsModel.IntervalOption.DEFAULT; IntervalStatisticsModel.IntervalOption option = selectedInterval != null ? selectedInterval : IntervalStatisticsModel.IntervalOption.DEFAULT;
String stringValue = formatter.formatDistance(option.getDistance(metricUnits)); String stringValue = formatter.formatDistance(option.getDistance(unitSystem));
viewBinding.intervalsDropdown.setText(stringValue, false); viewBinding.intervalsDropdown.setText(stringValue, false);
} }
private synchronized void updateIntervals(boolean metricUnits, IntervalStatisticsModel.IntervalOption selectedInterval) { private synchronized void updateIntervals(UnitSystem unitSystem, IntervalStatisticsModel.IntervalOption selectedInterval) {
boolean update = metricUnits != this.metricUnits boolean update = unitSystem != this.unitSystem
|| selectedInterval == null || selectedInterval == null
|| !selectedInterval.sameMultiplier(this.selectedInterval); || !selectedInterval.sameMultiplier(this.selectedInterval);
this.metricUnits = metricUnits; this.unitSystem = unitSystem;
this.selectedInterval = selectedInterval; this.selectedInterval = selectedInterval;
if (update && viewModel != null) { if (update && viewModel != null) {
viewModel.update(trackId, this.metricUnits, this.selectedInterval); viewModel.update(trackId, this.unitSystem, this.selectedInterval);
} }
} }
} }
@@ -43,8 +43,10 @@ import java.time.temporal.TemporalAccessor;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.DistanceFormatter;
import de.dennisguse.opentracks.data.models.HeartRate; import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Power; import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.settings.UnitSystem;
/** /**
* Various string manipulation methods. * Various string manipulation methods.
@@ -252,35 +254,18 @@ public class StringUtils {
/** /**
* @return the formatted altitude_m (or null) and it's unit as {@link Pair} * @return the formatted altitude_m (or null) and it's unit as {@link Pair}
*/ */
public static Pair<String, String> getAltitudeParts(Context context, Float altitude_m, boolean metricUnits) { public static Pair<String, String> getAltitudeParts(Context context, Float altitude_m, UnitSystem unitSystem) {
String formattedValue = context.getString(R.string.value_unknown); DistanceFormatter formatter = DistanceFormatter.Builder()
String unit = context.getString(metricUnits ? R.string.unit_meter : R.string.unit_feet); .setDecimalCount(1)
.setUnit(unitSystem)
.build(context);
if (altitude_m != null) { Distance distance = altitude_m != null ? Distance.of(altitude_m) : Distance.of(Double.NaN); //TODO Refactor Double.NaN
double value = Distance.of(altitude_m).toM_FT(metricUnits); return formatter.getDistanceParts(distance);
formattedValue = StringUtils.formatDecimal(value, 1);
}
return new Pair<>(formattedValue, unit);
} }
public static String formatAltitude(Context context, Float altitude_m, boolean metricUnits) { public static String formatAltitude(Context context, Float altitude_m, UnitSystem unitSystem) {
Pair<String, String> altitudeParts = getAltitudeParts(context, altitude_m, metricUnits); Pair<String, String> altitudeParts = getAltitudeParts(context, altitude_m, unitSystem);
return context.getString(R.string.altitude_with_unit, altitudeParts.first, altitudeParts.second);
}
/**
* @return the formatted altitudeDifference_m (or null) and it's unit as {@link Pair}
*/
public static Pair<String, String> getAltitudeChangeParts(Context context, Float altitudeDifference_m, boolean metricUnits) {
String formattedValue = altitudeDifference_m != null ? String.valueOf((int) Distance.of(altitudeDifference_m).toM_FT(metricUnits)) : context.getString(R.string.value_unknown);
String unit = context.getString(metricUnits ? R.string.unit_meter : R.string.unit_feet);
return new Pair<>(formattedValue, unit);
}
public static String formatAltitudeChange(Context context, Float altitude_m, boolean metricUnits) {
Pair<String, String> altitudeParts = getAltitudeChangeParts(context, altitude_m, metricUnits);
return context.getString(R.string.altitude_with_unit, altitudeParts.first, altitudeParts.second); return context.getString(R.string.altitude_with_unit, altitudeParts.first, altitudeParts.second);
} }
@@ -18,6 +18,7 @@ import de.dennisguse.opentracks.data.models.SpeedFormatter;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet; import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
import de.dennisguse.opentracks.services.RecordingData; import de.dennisguse.opentracks.services.RecordingData;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.SensorStatistics; import de.dennisguse.opentracks.stats.SensorStatistics;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField; import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
@@ -26,17 +27,17 @@ import de.dennisguse.opentracks.util.StringUtils;
public class StatisticDataBuilder { public class StatisticDataBuilder {
public static List<StatisticData> fromRecordingData(@NonNull Context context, @NonNull RecordingData recordingData, @NonNull Layout layout, boolean metricUnits) { public static List<StatisticData> fromRecordingData(@NonNull Context context, @NonNull RecordingData recordingData, @NonNull Layout layout, UnitSystem unitSystem) {
List<StatisticData> statisticDataList = layout.getFields().stream() List<StatisticData> statisticDataList = layout.getFields().stream()
.filter(DataField::isVisible) .filter(DataField::isVisible)
.map(field -> build(context, recordingData, field.getKey(), field.isPrimary(), metricUnits)) .map(field -> build(context, recordingData, field.getKey(), field.isPrimary(), unitSystem))
.filter(Objects::nonNull) .filter(Objects::nonNull)
.collect(Collectors.toList()); .collect(Collectors.toList());
statisticDataList.addAll(getSensorStatsDataIfNeeded(context, recordingData, statisticDataList, metricUnits)); statisticDataList.addAll(getSensorStatsDataIfNeeded(context, recordingData, statisticDataList, unitSystem));
return statisticDataList; return statisticDataList;
} }
private static StatisticData build(@NonNull Context context, @NonNull RecordingData recordingData, @NonNull String fieldKey, boolean isPrimary, boolean metricUnits) { private static StatisticData build(@NonNull Context context, @NonNull RecordingData recordingData, @NonNull String fieldKey, boolean isPrimary, UnitSystem unitSystem) {
final TrackPoint latestTrackPoint = recordingData.getLatestTrackPoint(); final TrackPoint latestTrackPoint = recordingData.getLatestTrackPoint();
final SensorDataSet sensorDataSet = recordingData.getSensorDataSet(); final SensorDataSet sensorDataSet = recordingData.getSensorDataSet();
final TrackStatistics trackStatistics = recordingData.getTrackStatistics(); final TrackStatistics trackStatistics = recordingData.getTrackStatistics();
@@ -48,8 +49,8 @@ public class StatisticDataBuilder {
final String sensorUnknown = context.getString(R.string.value_unknown); final String sensorUnknown = context.getString(R.string.value_unknown);
SpeedFormatter speedFormatterPace = SpeedFormatter.Builder().setMetricUnits(metricUnits).setReportSpeedOrPace(false).build(context); SpeedFormatter speedFormatterPace = SpeedFormatter.Builder().setUnit(unitSystem).setReportSpeedOrPace(false).build(context);
SpeedFormatter speedFormatterSpeed = SpeedFormatter.Builder().setMetricUnits(metricUnits).setReportSpeedOrPace(true).build(context); SpeedFormatter speedFormatterSpeed = SpeedFormatter.Builder().setUnit(unitSystem).setReportSpeedOrPace(true).build(context);
if (fieldKey.equals(context.getString(R.string.stats_custom_layout_total_time_key))) { if (fieldKey.equals(context.getString(R.string.stats_custom_layout_total_time_key))) {
@@ -60,14 +61,14 @@ public class StatisticDataBuilder {
title = context.getString(R.string.stats_moving_time); title = context.getString(R.string.stats_moving_time);
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_distance_key))) { } else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_distance_key))) {
valueAndUnit = DistanceFormatter.Builder() valueAndUnit = DistanceFormatter.Builder()
.setMetricUnits(metricUnits) .setUnit(unitSystem)
.build(context).getDistanceParts(trackStatistics.getTotalDistance()); .build(context).getDistanceParts(trackStatistics.getTotalDistance());
title = context.getString(R.string.stats_distance); title = context.getString(R.string.stats_distance);
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_speed_key)) || fieldKey.equals(context.getString(R.string.stats_custom_layout_pace_key))) { } else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_speed_key)) || fieldKey.equals(context.getString(R.string.stats_custom_layout_pace_key))) {
boolean reportSpeed = fieldKey.equals("speed"); boolean reportSpeed = fieldKey.equals("speed");
title = reportSpeed ? context.getString(R.string.stats_speed) : context.getString(R.string.stats_pace); title = reportSpeed ? context.getString(R.string.stats_speed) : context.getString(R.string.stats_pace);
SpeedFormatter localSpeedFormatter = SpeedFormatter.Builder().setMetricUnits(metricUnits).setReportSpeedOrPace(reportSpeed).build(context); SpeedFormatter localSpeedFormatter = SpeedFormatter.Builder().setUnit(unitSystem).setReportSpeedOrPace(reportSpeed).build(context);
if (sensorDataSet != null && sensorDataSet.getSpeed() != null) { if (sensorDataSet != null && sensorDataSet.getSpeed() != null) {
valueAndUnit = localSpeedFormatter.getSpeedParts(sensorDataSet.getSpeed().first); valueAndUnit = localSpeedFormatter.getSpeedParts(sensorDataSet.getSpeed().first);
description = sensorDataSet.getSpeed().second; description = sensorDataSet.getSpeed().second;
@@ -98,12 +99,12 @@ public class StatisticDataBuilder {
Float altitude = latestTrackPoint != null && latestTrackPoint.hasAltitude() ? (float) latestTrackPoint.getAltitude().toM() : null; Float altitude = latestTrackPoint != null && latestTrackPoint.hasAltitude() ? (float) latestTrackPoint.getAltitude().toM() : null;
title = context.getString(R.string.stats_altitude); title = context.getString(R.string.stats_altitude);
description = latestTrackPoint != null && latestTrackPoint.hasAltitude() ? context.getString(latestTrackPoint.getAltitude().getLabelId()) : null; description = latestTrackPoint != null && latestTrackPoint.hasAltitude() ? context.getString(latestTrackPoint.getAltitude().getLabelId()) : null;
valueAndUnit = StringUtils.getAltitudeParts(context, altitude, metricUnits); valueAndUnit = StringUtils.getAltitudeParts(context, altitude, unitSystem);
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_gain_key))) { } else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_gain_key))) {
valueAndUnit = StringUtils.getAltitudeChangeParts(context, trackStatistics.getTotalAltitudeGain(), metricUnits); valueAndUnit = StringUtils.getAltitudeParts(context, trackStatistics.getTotalAltitudeGain(), unitSystem);
title = context.getString(R.string.stats_gain); title = context.getString(R.string.stats_gain);
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_loss_key))) { } else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_loss_key))) {
valueAndUnit = StringUtils.getAltitudeChangeParts(context, trackStatistics.getTotalAltitudeLoss(), metricUnits); valueAndUnit = StringUtils.getAltitudeParts(context, trackStatistics.getTotalAltitudeLoss(), unitSystem);
title = context.getString(R.string.stats_loss); title = context.getString(R.string.stats_loss);
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_coordinates_key))) { } else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_coordinates_key))) {
title = context.getString(R.string.stats_coordinates); title = context.getString(R.string.stats_coordinates);
@@ -162,20 +163,20 @@ public class StatisticDataBuilder {
/** /**
* Builds a list of StatisticData with sensors configured but not in statisticDataList. * Builds a list of StatisticData with sensors configured but not in statisticDataList.
*/ */
private static List<StatisticData> getSensorStatsDataIfNeeded(Context context, RecordingData recordingData, List<StatisticData> statisticDataList, boolean metricUnits) { private static List<StatisticData> getSensorStatsDataIfNeeded(Context context, RecordingData recordingData, List<StatisticData> statisticDataList, UnitSystem unitSystem) {
List<StatisticData> sensorDataList = new ArrayList<>(); List<StatisticData> sensorDataList = new ArrayList<>();
SensorDataSet sensorDataSet = recordingData.getSensorDataSet(); SensorDataSet sensorDataSet = recordingData.getSensorDataSet();
if (sensorDataSet == null) { if (sensorDataSet == null) {
return sensorDataList; return sensorDataList;
} }
if (statisticDataList.stream().noneMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_heart_rate))) && sensorDataSet.getHeartRate() != null) { if (statisticDataList.stream().noneMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_heart_rate))) && sensorDataSet.getHeartRate() != null) {
sensorDataList.add(build(context, recordingData, "heart_rate", true, metricUnits)); sensorDataList.add(build(context, recordingData, "heart_rate", true, unitSystem));
} }
if (statisticDataList.stream().noneMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_cadence))) && sensorDataSet.getCadence() != null) { if (statisticDataList.stream().noneMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_cadence))) && sensorDataSet.getCadence() != null) {
sensorDataList.add(build(context, recordingData, "cadence", true, metricUnits)); sensorDataList.add(build(context, recordingData, "cadence", true, unitSystem));
} }
if (statisticDataList.stream().noneMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_power))) && sensorDataSet.getCyclingPower() != null && sensorDataSet.getCyclingPower().hasValue()) { if (statisticDataList.stream().noneMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_power))) && sensorDataSet.getCyclingPower() != null && sensorDataSet.getCyclingPower().hasValue()) {
sensorDataList.add(build(context, recordingData, "power", true, metricUnits)); sensorDataList.add(build(context, recordingData, "power", true, unitSystem));
} }
return sensorDataList; return sensorDataList;
@@ -9,6 +9,7 @@ import androidx.lifecycle.MutableLiveData;
import java.util.List; import java.util.List;
import de.dennisguse.opentracks.services.RecordingData; import de.dennisguse.opentracks.services.RecordingData;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout; import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
public class StatisticsDataModel extends AndroidViewModel { public class StatisticsDataModel extends AndroidViewModel {
@@ -26,9 +27,9 @@ public class StatisticsDataModel extends AndroidViewModel {
return statsData; return statsData;
} }
public void update(RecordingData recordingData, Layout layout, boolean metricUnit) { public void update(RecordingData recordingData, Layout layout, UnitSystem unitSystem) {
new Thread(() -> { new Thread(() -> {
List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(getApplication(), recordingData, layout, metricUnit); List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(getApplication(), recordingData, layout, unitSystem);
statsData.postValue(statisticDataList); statsData.postValue(statisticDataList);
}).start(); }).start();
} }