Replace long time_ms with Instant and Duration, respectively.

Fixes #561.
This commit is contained in:
Dennis Guse
2021-01-09 13:27:14 +01:00
parent 3bbcbfe202
commit e990b413cd
47 changed files with 442 additions and 434 deletions
@@ -25,6 +25,9 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.Duration;
import java.time.Instant;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.stats.TrackStatistics;
@@ -40,7 +43,7 @@ import static org.junit.Assert.assertEquals;
@RunWith(AndroidJUnit4.class)
public class DescriptionGeneratorTest {
private static final long START_TIME = 1288721514000L;
private static final Instant START_TIME = Instant.ofEpochMilli(1288721514000L);
private DescriptionGenerator descriptionGenerator;
private final Context context = ApplicationProvider.getApplicationContext();
@@ -58,14 +61,14 @@ public class DescriptionGeneratorTest {
Track track = new Track();
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(20000);
stats.setTotalTime(600000);
stats.setMovingTime(300000);
stats.setTotalTime(Duration.ofMillis(600000));
stats.setMovingTime(Duration.ofMillis(300000));
stats.setMaxSpeed(100);
stats.setMaxElevation(550);
stats.setMinElevation(-500);
stats.setTotalElevationGain(6000f);
stats.setTotalElevationLoss(6000);
stats.setStartTime_ms(START_TIME);
stats.setStartTime(START_TIME);
track.setTrackStatistics(stats);
track.setCategory("hiking");
String expected = //"Created by"
@@ -96,14 +99,14 @@ public class DescriptionGeneratorTest {
Track track = new Track();
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(20000);
stats.setTotalTime(600000);
stats.setMovingTime(300000);
stats.setTotalTime(Duration.ofMillis(600000));
stats.setMovingTime(Duration.ofMillis(300000));
stats.setMaxSpeed(100);
stats.setMaxElevation(Double.POSITIVE_INFINITY);
stats.setMinElevation(Double.NEGATIVE_INFINITY);
stats.setTotalElevationGain(6000f);
stats.setTotalElevationLoss(6000);
stats.setStartTime_ms(START_TIME);
stats.setStartTime(START_TIME);
track.setTrackStatistics(stats);
track.setCategory("hiking");
String expected = //"Created by"
@@ -139,12 +142,12 @@ public class DescriptionGeneratorTest {
}
/**
* Tests {@link DescriptionGenerator#writeTime(long, StringBuilder, int, String)}.
* Tests {@link DescriptionGenerator#writeTime(Duration, StringBuilder, int, String)}.
*/
@Test
public void testWriteTime() {
StringBuilder builder = new StringBuilder();
descriptionGenerator.writeTime(1000, builder, R.string.description_total_time, "<br>");
descriptionGenerator.writeTime(Duration.ofMillis(1000), builder, R.string.description_total_time, "<br>");
assertEquals("Total time: 00:01<br>", builder.toString());
}
@@ -7,6 +7,7 @@ import android.util.Pair;
import java.io.File;
import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@@ -56,7 +57,7 @@ public class TestDataUtil {
int i = 0;
List<TrackPoint> trackPoints = List.of(
TrackPoint.createSegmentStartManualWithTime(i++ + 1),
TrackPoint.createSegmentStartManualWithTime(Instant.ofEpochMilli(i++ + 1)),
createTrackPoint(i++),
createTrackPoint(i++),
createTrackPoint(i++),
@@ -64,13 +65,13 @@ public class TestDataUtil {
createTrackPoint(i++),
createTrackPoint(i++),
createTrackPoint(i++),
TrackPoint.createSegmentEndWithTime(i++ + 1),
TrackPoint.createSegmentEndWithTime(Instant.ofEpochSecond(i++ + 1)),
TrackPoint.createSegmentStartManualWithTime(i++),
TrackPoint.createSegmentStartManualWithTime(Instant.ofEpochSecond(i++)),
createTrackPoint(i++),
createTrackPoint(i++),
createTrackPoint(i++),
TrackPoint.createSegmentEndWithTime(i++ + 1)
TrackPoint.createSegmentEndWithTime(Instant.ofEpochSecond(i++ + 1))
);
List<Marker> markers = List.of(
@@ -114,7 +115,7 @@ public class TestDataUtil {
trackPoint.setLongitude(INITIAL_LONGITUDE - (double) i / 10000.0);
trackPoint.setAccuracy((float) i / 100.0f);
trackPoint.setAltitude(i * ALTITUDE_INTERVAL);
trackPoint.setTime(i + 1);
trackPoint.setTime(Instant.ofEpochSecond(i + 1));
trackPoint.setSpeed(5f + (i / 10f));
trackPoint.setHeartRate_bpm(100f + i);
@@ -33,6 +33,8 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@@ -423,14 +425,12 @@ public class CustomContentProviderUtilsTest {
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Pair<Track, List<TrackPoint>> track = TestDataUtil.createTrack(trackId, 10);
// Bottom
long startTime = 1000L;
// AverageSpeed
TrackStatistics statistics = new TrackStatistics();
statistics.setStartTime_ms(startTime);
statistics.setStopTime_ms(2500L);
statistics.setTotalTime(1500L);
statistics.setMovingTime(700L);
statistics.setStartTime(Instant.ofEpochMilli(1000));
statistics.setStopTime(Instant.ofEpochMilli(2500));
statistics.setTotalTime(Duration.ofMillis(1500));
statistics.setMovingTime(Duration.ofMillis(700));
statistics.setTotalDistance(750.0);
statistics.setTotalElevationGain(50.0f);
statistics.setMaxSpeed(60.0);
@@ -809,7 +809,7 @@ public class CustomContentProviderUtilsTest {
// then
assertEquals(longitude, trackPoint.getLongitude(), 0.01);
assertEquals(latitude, trackPoint.getLatitude(), 0.01);
assertEquals(time, trackPoint.getTime(), 0.01);
assertEquals(time, trackPoint.getTime().toEpochMilli());
assertEquals(speed, trackPoint.getSpeed(), 0.01);
assertFalse(trackPoint.hasHeartRate());
}
@@ -28,6 +28,9 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.Duration;
import java.time.Instant;
import de.dennisguse.opentracks.chart.ChartPoint;
import de.dennisguse.opentracks.chart.ChartView;
import de.dennisguse.opentracks.content.data.TrackPoint;
@@ -61,7 +64,7 @@ public class ChartFragmentTest {
@Before
public void setUp() {
boolean chartByDistance = false;
chartFragment = (ChartFragment) ChartFragment.newInstance(chartByDistance);
chartFragment = ChartFragment.newInstance(chartByDistance);
chartFragment.setChartView(new ChartView(ApplicationProvider.getApplicationContext(), chartByDistance));
}
@@ -193,18 +196,18 @@ public class ChartFragmentTest {
// given
chartFragment.setChartByDistance(false);
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setTime(TrackStubUtils.INITIAL_TIME); //Keep old TrackPoint behavior of having time=0 for this test
trackPoint1.setTime(Instant.ofEpochMilli(TrackStubUtils.INITIAL_TIME)); //Keep old TrackPoint behavior of having time=0 for this test
// when
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
// then
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
long timeSpan = 222;
Duration timeSpan = Duration.ofMillis(222);
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
trackPoint2.setTime(trackPoint1.getTime() + timeSpan);
trackPoint2.setTime(Instant.ofEpochMilli(TrackStubUtils.INITIAL_TIME).plus(timeSpan));
point = chartFragment.createPendingPoint(trackPoint2);
assertEquals((double) timeSpan, point.getTimeOrDistance(), 0.01);
assertEquals(timeSpan, Duration.ofMillis((long) point.getTimeOrDistance()));
}
/**
@@ -255,7 +258,7 @@ public class ChartFragmentTest {
/*
* Add a time span here to make sure the second point is valid, the value 222 here is doesn't matter.
*/
trackPoint2.setTime(trackPoint1.getTime() + 222);
trackPoint2.setTime(trackPoint1.getTime().plusMillis(222));
trackPoint2.setSpeed(130f);
point = chartFragment.createPendingPoint(trackPoint2);
assertEquals(130.0 * UnitConversions.MPS_TO_KMH, point.getSpeed(), 0.01);
@@ -280,7 +283,7 @@ public class ChartFragmentTest {
* Add a time span here to make sure the second point and the speed is valid.
* Speed is valid if: speedDifference > Constants.MAX_ACCELERATION * timeDifference speedDifference = 102 -100 timeDifference = 222
*/
trackPoint2.setTime(trackPoint2.getTime() + 222);
trackPoint2.setTime(trackPoint2.getTime().plusMillis(222));
trackPoint2.setSpeed(102f);
point = chartFragment.createPendingPoint(trackPoint2);
assertEquals(102.0 * UnitConversions.MPS_TO_KMH * UnitConversions.KM_TO_MI, point.getSpeed(), 0.01);
@@ -305,7 +308,7 @@ public class ChartFragmentTest {
* Add a time span here to make sure the second point and the speed is valid.
* Speed is valid if: speedDifference > Constants.MAX_ACCELERATION * timeDifference speedDifference = 102 -100 timeDifference = 222
*/
trackPoint2.setTime(trackPoint2.getTime() + 222);
trackPoint2.setTime(trackPoint2.getTime().plusMillis(222));
trackPoint2.setSpeed(102f);
point = chartFragment.createPendingPoint(trackPoint2);
assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MPS_TO_KMH), point.getPace(), 0.01);
@@ -57,7 +57,7 @@ public class TrackStubUtils {
location.setAltitude(altitude);
location.setAccuracy(INITIAL_ACCURACY);
location.setSpeed(INITIAL_SPEED);
location.setTime(INITIAL_TIME);
location.setTime(INITIAL_TIME); //TODO This is nowadays ignored as the constructor will replace the time.
location.setBearing(INITIAL_BEARING);
return new TrackPoint(location);
@@ -357,7 +357,7 @@ public class ExportImportTest {
TrackPoint trackPoint = trackPoints.get(i);
TrackPoint importedTrackPoint = importedTrackPoints.get(i);
assertEquals(trackPoint.getTime(), importedTrackPoint.getTime(), 0.01);
assertEquals(trackPoint.getTime(), importedTrackPoint.getTime());
TrackPoint.Type type = trackPoint.getType();
assertEquals("" + i, trackPoint.getType(), importedTrackPoint.getType());
@@ -410,8 +410,8 @@ public class ExportImportTest {
TrackStatistics importedTrackStatistics = importedTrack.getTrackStatistics();
// Time
assertEquals(trackStatistics.getStartTime_ms(), trackStatistics.getStartTime_ms(), 0.01);
assertEquals(trackStatistics.getStopTime_ms(), trackStatistics.getStopTime_ms());
assertEquals(trackStatistics.getStartTime(), trackStatistics.getStartTime());
assertEquals(trackStatistics.getStopTime(), trackStatistics.getStopTime());
assertEquals(trackStatistics.getTotalTime(), trackStatistics.getTotalTime());
assertEquals(trackStatistics.getMovingTime(), trackStatistics.getMovingTime());
@@ -20,6 +20,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.time.Duration;
import de.dennisguse.opentracks.services.handlers.AdaptiveLocationListenerPolicy;
import static org.junit.Assert.assertEquals;
@@ -32,13 +34,13 @@ import static org.junit.Assert.assertEquals;
@RunWith(JUnit4.class)
public class AdaptiveLocationListenerPolicyTest {
private static final long MIN = 1000;
private static final long MAX = 3000;
private static final Duration MIN = Duration.ofMillis(1000);
private static final Duration MAX = Duration.ofMillis(3000);
private static final int MIN_DISTANCE = 10;
private static final long NEW_IDLE_TIME_BIG = 10000;
private static final long NEW_IDLE_TIME_NORMAL = 5000;
private static final long NEW_IDLE_TIME_SMALL = 2000;
private static final long NEW_IDLE_TIME_LESS_THAN_MIN = 500;
private static final Duration NEW_IDLE_TIME_BIG = Duration.ofMillis(10000);
private static final Duration NEW_IDLE_TIME_NORMAL = Duration.ofMillis(5000);
private static final Duration NEW_IDLE_TIME_SMALL = Duration.ofMillis(2000);
private static final Duration NEW_IDLE_TIME_LESS_THAN_MIN = Duration.ofMillis(500);
private AdaptiveLocationListenerPolicy adaptiveLocationListenerPolicy;
@Before
@@ -62,7 +64,7 @@ public class AdaptiveLocationListenerPolicyTest {
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_NORMAL);
// First get the half of NEW_IDLE_TIME_NORMAL, and then round it to the nearest second.
assertEquals((NEW_IDLE_TIME_NORMAL / 2 / 1000) * 1000, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
assertEquals(NEW_IDLE_TIME_NORMAL.dividedBy(2).getSeconds(), adaptiveLocationListenerPolicy.getDesiredPollingInterval().getSeconds());
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_SMALL);
assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
@@ -19,6 +19,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.Instant;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -239,7 +240,7 @@ public class TrackRecordingServiceTestLooper {
assertEquals(trackId, track.getId());
TrackStatistics trackStatistics = track.getTrackStatistics();
assertNotNull(trackStatistics);
assertTrue(trackStatistics.getStartTime_ms() > 0);
assertTrue(trackStatistics.getStopTime_ms() >= trackStatistics.getStartTime_ms());
assertTrue(trackStatistics.getStartTime().isAfter(Instant.ofEpochMilli(0)));
assertTrue(trackStatistics.getStopTime().isAfter(trackStatistics.getStartTime()));
}
}
@@ -20,6 +20,9 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.time.Duration;
import java.time.Instant;
import static org.junit.Assert.assertEquals;
/**
@@ -41,14 +44,14 @@ public class TrackStatisticsTest {
@Test
public void testMerge() {
TrackStatistics statistics2 = new TrackStatistics();
statistics.setStartTime_ms(1000L); // Resulting start time
statistics.setStopTime_ms(2500L);
statistics2.setStartTime_ms(3000L);
statistics2.setStopTime_ms(4000L); // Resulting stop time
statistics.setTotalTime(1500L);
statistics2.setTotalTime(1000L); // Result: 1500+1000
statistics.setMovingTime(700L);
statistics2.setMovingTime(600L); // Result: 700+600
statistics.setStartTime(Instant.ofEpochMilli(1000)); // Resulting start time
statistics.setStopTime(Instant.ofEpochMilli(2500));
statistics2.setStartTime(Instant.ofEpochMilli(3000));
statistics2.setStopTime(Instant.ofEpochMilli(4000)); // Resulting stop time
statistics.setTotalTime(Duration.ofMillis(1500));
statistics2.setTotalTime(Duration.ofMillis(1000)); // Result: 1500+1000
statistics.setMovingTime(Duration.ofMillis(700));
statistics2.setMovingTime(Duration.ofMillis(600)); // Result: 700+600
statistics.setTotalDistance(750.0);
statistics2.setTotalDistance(350.0); // Result: 750+350
statistics.setTotalElevationGain(50.0f);
@@ -62,13 +65,13 @@ public class TrackStatisticsTest {
statistics.merge(statistics2);
assertEquals(1000L, statistics.getStartTime_ms());
assertEquals(4000L, statistics.getStopTime_ms());
assertEquals(2500L, statistics.getTotalTime());
assertEquals(1300L, statistics.getMovingTime());
assertEquals(Instant.ofEpochMilli(1000), statistics.getStartTime());
assertEquals(Instant.ofEpochMilli(4000), statistics.getStopTime());
assertEquals(Duration.ofMillis(2500), statistics.getTotalTime());
assertEquals(Duration.ofMillis(1300), statistics.getMovingTime());
assertEquals(1100.0, statistics.getTotalDistance(), 0.001);
assertEquals(900.0, statistics.getTotalElevationGain(), 0.001);
assertEquals(statistics.getTotalDistance() / (statistics.getMovingTime() / 1000.0), statistics.getMaxSpeed(), 0.001);
assertEquals(statistics.getTotalDistance() / statistics.getMovingTime().getSeconds(), statistics.getMaxSpeed(), 0.001);
assertEquals(1200.0, statistics.getMinElevation(), 0.001);
assertEquals(3575.0, statistics.getMaxElevation(), 0.001);
}
@@ -76,14 +79,14 @@ public class TrackStatisticsTest {
@Test
public void testGetAverageSpeed() {
statistics.setTotalDistance(1000.0);
statistics.setTotalTime(50000); // in milliseconds
statistics.setTotalTime(Duration.ofMillis(50000));
assertEquals(20.0, statistics.getAverageSpeed(), 0.001);
}
@Test
public void testGetAverageMovingSpeed() {
statistics.setTotalDistance(1000.0);
statistics.setMovingTime(20000); // in milliseconds
statistics.setMovingTime(Duration.ofMillis(20000));
assertEquals(50.0, statistics.getAverageMovingSpeed(), 0.001);
}
}
@@ -0,0 +1,38 @@
package de.dennisguse.opentracks.stats;
import org.junit.Test;
import java.time.Duration;
import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.content.data.Track;
import static org.junit.Assert.assertEquals;
public class TrackStatisticsUpdaterTest {
@Test
public void addTrackPoint() {
// given
TestDataUtil.TrackData data = TestDataUtil.createTestingTrack(new Track.Id(1));
// when
TrackStatisticsUpdater updater = new TrackStatisticsUpdater();
data.trackPoints.forEach(it -> updater.addTrackPoint(it, 50));
// then
TrackStatistics statistics = updater.getTrackStatistics();
assertEquals(56.90, statistics.getTotalDistance(), 0.01);
assertEquals(Duration.ofSeconds(9), statistics.getTotalTime());
assertEquals(Duration.ofSeconds(4), statistics.getMovingTime());
assertEquals(2.5, statistics.getMinElevation(), 0.01);
assertEquals(27.5, statistics.getMaxElevation(), 0.01);
assertEquals(18.0, statistics.getTotalElevationGain(), 0.01);
assertEquals(72.0, statistics.getTotalElevationLoss(), 0.01);
assertEquals(14.226, statistics.getMaxSpeed(), 0.01);
assertEquals(14.226, statistics.getAverageMovingSpeed(), 0.01);
assertEquals(6.322, statistics.getAverageSpeed(), 0.01);
}
}
@@ -8,6 +8,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.Duration;
import java.util.List;
import de.dennisguse.opentracks.R;
@@ -28,8 +29,8 @@ public class AnnouncementUtilsTest {
public void getAnnouncement() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(20000);
stats.setTotalTime(600000);
stats.setMovingTime(300000);
stats.setTotalTime(Duration.ofMillis(600000));
stats.setMovingTime(Duration.ofMillis(300000));
stats.setMaxSpeed(100);
stats.setTotalElevationGain(6000f);
@@ -44,8 +45,8 @@ public class AnnouncementUtilsTest {
public void getAnnouncement_withInterval() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(20000);
stats.setTotalTime(600000);
stats.setMovingTime(300000);
stats.setTotalTime(Duration.ofMillis(600000));
stats.setMovingTime(Duration.ofMillis(300000));
stats.setMaxSpeed(100);
stats.setTotalElevationGain(6000f);
@@ -24,6 +24,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.Duration;
import java.util.GregorianCalendar;
import java.util.TimeZone;
@@ -40,55 +41,32 @@ public class StringUtilsTest {
private final Context context = ApplicationProvider.getApplicationContext();
/**
* Tests {@link StringUtils#formatDateTimeIso8601(long)}.
*/
@Test
public void testFormatDateTimeIso8601() {
assertEquals("1970-01-01T00:00:12.345Z", StringUtils.formatDateTimeIso8601(12345));
}
/**
* Tests {@link StringUtils#formatElapsedTime(long)}.
* Tests {@link StringUtils#formatElapsedTime(Duration)}.
*/
@Test
public void testformatElapsedTime() {
// 1 second
assertEquals("00:01", StringUtils.formatElapsedTime(1000));
// 10 seconds
assertEquals("00:10", StringUtils.formatElapsedTime(10000));
// 1 minute
assertEquals("01:00", StringUtils.formatElapsedTime(60000));
// 10 minutes
assertEquals("10:00", StringUtils.formatElapsedTime(600000));
// 1 hour
assertEquals("1:00:00", StringUtils.formatElapsedTime(3600000));
// 10 hours
assertEquals("10:00:00", StringUtils.formatElapsedTime(36000000));
// 100 hours
assertEquals("100:00:00", StringUtils.formatElapsedTime(360000000));
assertEquals("00:01", StringUtils.formatElapsedTime(Duration.ofMillis(1000)));
assertEquals("00:10", StringUtils.formatElapsedTime(Duration.ofMillis(10000)));
assertEquals("01:00", StringUtils.formatElapsedTime(Duration.ofMillis(60000)));
assertEquals("10:00", StringUtils.formatElapsedTime(Duration.ofMillis(600000)));
assertEquals("1:00:00", StringUtils.formatElapsedTime(Duration.ofMillis(3600000)));
assertEquals("10:00:00", StringUtils.formatElapsedTime(Duration.ofMillis(36000000)));
assertEquals("100:00:00", StringUtils.formatElapsedTime(Duration.ofMillis(360000000)));
}
/**
* Tests {@link StringUtils#formatElapsedTimeWithHour(long)}.
* Tests {@link StringUtils#formatElapsedTimeWithHour(Duration)}.
*/
@Test
public void testformatElapsedTimeWithHour() {
// 1 second
assertEquals("0:00:01", StringUtils.formatElapsedTimeWithHour(1000));
// 10 seconds
assertEquals("0:00:10", StringUtils.formatElapsedTimeWithHour(10000));
// 1 minute
assertEquals("0:01:00", StringUtils.formatElapsedTimeWithHour(60000));
// 10 minutes
assertEquals("0:10:00", StringUtils.formatElapsedTimeWithHour(600000));
// 1 hour
assertEquals("1:00:00", StringUtils.formatElapsedTimeWithHour(3600000));
// 10 hours
assertEquals("10:00:00", StringUtils.formatElapsedTimeWithHour(36000000));
// 100 hours
assertEquals("100:00:00", StringUtils.formatElapsedTimeWithHour(360000000));
assertEquals("0:00:01", StringUtils.formatElapsedTimeWithHour(Duration.ofMillis(1000)));
assertEquals("0:00:10", StringUtils.formatElapsedTimeWithHour(Duration.ofMillis(10000)));
assertEquals("0:01:00", StringUtils.formatElapsedTimeWithHour(Duration.ofMillis(60000)));
assertEquals("0:10:00", StringUtils.formatElapsedTimeWithHour(Duration.ofMillis(600000)));
assertEquals("1:00:00", StringUtils.formatElapsedTimeWithHour(Duration.ofMillis(3600000)));
assertEquals("10:00:00", StringUtils.formatElapsedTimeWithHour(Duration.ofMillis(36000000)));
assertEquals("100:00:00", StringUtils.formatElapsedTimeWithHour(Duration.ofMillis(360000000)));
}
/**
@@ -170,29 +148,7 @@ public class StringUtilsTest {
// This comparision tends to be flaky (difference of 1ms)
// Assert.assertEquals(calendar.getTimeInMillis(), StringUtils.parseTime(xmlDateTime));
assertTrue(calendar.getTimeInMillis() + " vs. " + StringUtils.parseTime(xmlDateTime), Math.abs(calendar.getTimeInMillis() - StringUtils.parseTime(xmlDateTime)) <= 1);
}
/**
* Tests {@link StringUtils#getTimeParts(long)} with a positive number.
*/
@Test
public void testGetTimeParts_positive() {
int[] parts = StringUtils.getTimeParts(61000);
assertEquals(1, parts[0]);
assertEquals(1, parts[1]);
assertEquals(0, parts[2]);
}
/**
* Tests {@link StringUtils#getTimeParts(long)} with a negative number.
*/
@Test
public void testGetTimeParts_negative() {
int[] parts = StringUtils.getTimeParts(-61000);
assertEquals(-1, parts[0]);
assertEquals(-1, parts[1]);
assertEquals(0, parts[2]);
assertTrue(calendar.getTimeInMillis() + " vs. " + StringUtils.parseTime(xmlDateTime), Math.abs(calendar.getTimeInMillis() - StringUtils.parseTime(xmlDateTime).toEpochMilli()) <= 1);
}
@Test
@@ -25,7 +25,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.time.Instant;
import java.util.Locale;
import de.dennisguse.opentracks.R;
@@ -42,7 +42,7 @@ import static org.junit.Assert.assertEquals;
public class TrackNameUtilsTest {
private static final Track.Id TRACK_ID = new Track.Id(1L);
private static final long START_TIME = 1288213406000L;
private static final Instant START_TIME = Instant.ofEpochMilli(1288213406000L);
private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
@@ -62,7 +62,7 @@ public class TrackNameUtilsTest {
public void testTrackName_date_iso_8601() {
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_iso_8601_value));
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(TrackNameUtils.ISO_8601_FORMAT, Locale.US);
assertEquals(simpleDateFormat.format(new Date(START_TIME)), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
assertEquals(simpleDateFormat.format(START_TIME.toEpochMilli()), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
}
/**
@@ -8,6 +8,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import de.dennisguse.opentracks.R;
@@ -27,15 +29,15 @@ public class AggregatedStatisticsTest {
* Create a TrackStatistics object.
*
* @param totalDistance distance in meters.
* @param totalTime total time in milliseconds.
* @param totalTime total time in milliseconds. //TODO Duration
* @return TrackStatistics object.
*/
private static Track createTrack(Context context, long totalDistance, long totalTime, String category) {
TrackStatistics statistics = new TrackStatistics();
statistics.setStartTime_ms(1000L); // Resulting start time
statistics.setStopTime_ms(1000L + totalTime);
statistics.setTotalTime(totalTime);
statistics.setMovingTime(totalTime);
statistics.setStartTime(Instant.ofEpochMilli(1000L)); // Resulting start time
statistics.setStopTime(Instant.ofEpochMilli(1000L + totalTime));
statistics.setTotalTime(Duration.ofMillis(totalTime));
statistics.setMovingTime(Duration.ofMillis(totalTime));
statistics.setTotalDistance(totalDistance);
statistics.setTotalElevationGain(50.0f);
statistics.setMaxSpeed(50.0); // Resulting max speed
@@ -68,7 +70,7 @@ public class AggregatedStatisticsTest {
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
assertEquals(totalTime, statistics2.getMovingTime());
assertEquals(Duration.ofMillis(totalTime), statistics2.getMovingTime());
}
@Test
@@ -122,7 +124,7 @@ public class AggregatedStatisticsTest {
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
assertEquals(totalTime * 2L, statistics2.getMovingTime());
assertEquals(Duration.ofMillis(totalTime * 2), statistics2.getMovingTime());
}
@Test
@@ -156,19 +158,19 @@ public class AggregatedStatisticsTest {
{
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
assertEquals(totalTime, statistics2.getMovingTime());
assertEquals(Duration.ofMillis(totalTime), statistics2.getMovingTime());
}
{
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
assertEquals(totalTime, statistics2.getMovingTime());
assertEquals(Duration.ofMillis(totalTime), statistics2.getMovingTime());
}
{
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
assertEquals(totalTime, statistics2.getMovingTime());
assertEquals(Duration.ofMillis(totalTime), statistics2.getMovingTime());
}
}
@@ -219,28 +221,28 @@ public class AggregatedStatisticsTest {
{
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
assertEquals(totalDistance * 5, statistics2.getTotalDistance(), 0);
assertEquals(totalTime * 5, statistics2.getMovingTime());
assertEquals(Duration.ofMillis(totalTime * 5), statistics2.getMovingTime());
}
// Running.
{
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
assertEquals(totalTime * 2, statistics2.getMovingTime());
assertEquals(Duration.ofMillis(totalTime * 2), statistics2.getMovingTime());
}
// Walking.
{
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
assertEquals(totalTime * 2, statistics2.getMovingTime());
assertEquals(Duration.ofMillis(totalTime * 2), statistics2.getMovingTime());
}
// Driving.
{
TrackStatistics statistics2 = aggregatedStatistics.get(driving).getTrackStatistics();
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
assertEquals(totalTime, statistics2.getMovingTime());
assertEquals(Duration.ofMillis(totalTime), statistics2.getMovingTime());
}
// Check order
@@ -116,17 +116,17 @@ public class IntervalStatisticsTest {
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, distanceInterval);
List<IntervalStatistics.Interval> intervalList = intervalStatistics.getIntervalList();
double totalDistance = 0d;
long totalTime = 0L;
float totalTime = 0L;
float totalGain = 0f;
for (IntervalStatistics.Interval i : intervalList) {
totalDistance += i.getDistance_m();
totalTime += ((i.getDistance_m() / i.getSpeed_ms()) * UnitConversions.S_TO_MS);
totalTime += i.getDistance_m() / i.getSpeed_ms();
totalGain += i.getGain_m();
}
// then
assertEquals(trackStatistics.getTotalDistance(), totalDistance, 0.01);
assertEquals(trackStatistics.getTotalTime() * UnitConversions.MS_TO_S, totalTime * UnitConversions.MS_TO_S, 0.1);
assertEquals(trackStatistics.getTotalTime().toMillis(), totalTime * UnitConversions.S_TO_MS, 1);
assertEquals(intervalList.size(), (int) Math.ceil(trackStatistics.getTotalDistance() / distanceInterval));
assertEquals(totalGain, trackPoints.size() * TestDataUtil.ELEVATION_GAIN, 0.1);
for (int i = 0; i < intervalList.size() - 1; i++) {
@@ -24,6 +24,8 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import java.time.Duration;
import de.dennisguse.opentracks.databinding.TrackControllerBinding;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
@@ -53,8 +55,7 @@ public class TrackController implements View.OnTouchListener {
private boolean isRecording;
private boolean isPaused;
private long totalTime = 0;
private long totalTimeTimestamp = 0;
private Duration totalTime;
private boolean isResumed = false;
private final Callback callback;
@@ -63,7 +64,7 @@ public class TrackController implements View.OnTouchListener {
private final Runnable updateTotalTimeRunnable = new Runnable() {
public void run() {
if (isResumed && isRecording && !isPaused) {
viewBinding.trackControllerTotalTime.setText(StringUtils.formatElapsedTimeWithHour(System.currentTimeMillis() - totalTimeTimestamp + totalTime));
viewBinding.trackControllerTotalTime.setText(StringUtils.formatElapsedTimeWithHour(totalTime));
handlerUpdateTotalTime.postDelayed(this, UnitConversions.ONE_SECOND_MS);
}
}
@@ -191,9 +192,10 @@ public class TrackController implements View.OnTouchListener {
viewBinding.trackControllerTotalTime.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
if (isRecording) {
totalTime = getTotalTime();
viewBinding.trackControllerTotalTime.setText(StringUtils.formatElapsedTimeWithHour(totalTime));
if (totalTime != null) {
viewBinding.trackControllerTotalTime.setText(StringUtils.formatElapsedTimeWithHour(totalTime));
}
if (!isPaused) {
totalTimeTimestamp = System.currentTimeMillis();
handlerUpdateTotalTime.postDelayed(updateTotalTimeRunnable, UnitConversions.ONE_SECOND_MS);
}
}
@@ -220,9 +222,9 @@ public class TrackController implements View.OnTouchListener {
/**
* Gets the total time for the current recording track.
*/
private long getTotalTime() {
private Duration getTotalTime() {
TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
return trackRecordingService != null ? trackRecordingService.getTotalTime() : 0L;
return trackRecordingService != null ? trackRecordingService.getTotalTime() : null;
}
private void showStatusSetDefaultText() {
@@ -44,6 +44,8 @@ import androidx.loader.app.LoaderManager;
import androidx.loader.content.CursorLoader;
import androidx.loader.content.Loader;
import java.time.Duration;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.databinding.TrackListBinding;
@@ -224,7 +226,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
String icon = cursor.getString(iconIndex);
int iconId = TrackIconUtils.getIconDrawable(icon);
String name = cursor.getString(nameIndex);
String totalTime = StringUtils.formatElapsedTime(cursor.getLong(totalTimeIndex));
String totalTime = StringUtils.formatElapsedTime(Duration.ofMillis(cursor.getLong(totalTimeIndex)));
String totalDistance = StringUtils.formatDistance(TrackListActivity.this, cursor.getDouble(totalDistanceIndex), metricUnits);
int markerCount = cursor.getInt(markerCountIndex);
long startTime = cursor.getLong(startTimeIndex);
@@ -35,7 +35,7 @@ public class ChartPoint {
}
timeOrDistance = distance;
} else {
timeOrDistance = trackStatistics.getTotalTime();
timeOrDistance = trackStatistics.getTotalTime().toMillis();
}
elevation = trackStatisticsUpdater.getSmoothedElevation();
@@ -38,6 +38,7 @@ import androidx.core.content.ContextCompat;
import androidx.core.view.GestureDetectorCompat;
import java.text.NumberFormat;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
@@ -740,7 +741,7 @@ public class ChartView extends View {
* @param spacing the spacing between x axis and marker
*/
private void drawXAxisMarker(Canvas canvas, double value, NumberFormat numberFormat, int spacing) {
String marker = chartByDistance ? numberFormat.format(value) : StringUtils.formatElapsedTime((long) value);
String marker = chartByDistance ? numberFormat.format(value) : StringUtils.formatElapsedTime((Duration.ofMillis((long) value)));
Rect rect = getRect(xAxisMarkerPaint, marker);
canvas.drawText(marker, getX(value), topBorder + effectiveHeight + spacing + rect.height(), xAxisMarkerPaint);
}
@@ -21,6 +21,8 @@ import android.util.Pair;
import androidx.annotation.VisibleForTesting;
import java.time.Duration;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.stats.TrackStatistics;
@@ -146,7 +148,7 @@ public class DescriptionGenerator {
}
// Recorded time
builder.append(context.getString(R.string.description_recorded_time, StringUtils.formatDateTime(context, stats.getStartTime_ms())));
builder.append(context.getString(R.string.description_recorded_time, StringUtils.formatDateTime(context, stats.getStartTime())));
builder.append(lineBreak);
return builder.toString();
@@ -177,7 +179,7 @@ public class DescriptionGenerator {
* @param lineBreak line break string
*/
@VisibleForTesting
void writeTime(long time, StringBuilder builder, int resId, String lineBreak) {
void writeTime(Duration time, StringBuilder builder, int resId, String lineBreak) {
builder.append(context.getString(resId, StringUtils.formatElapsedTime(time)));
builder.append(lineBreak);
}
@@ -25,6 +25,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import java.time.Instant;
import java.util.Objects;
/**
@@ -43,7 +44,7 @@ public final class Marker {
private final Track.Id trackId;
//TODO It is the distance from the track starting point; rename to something more meaningful
private double length = 0.0;
private long duration = 0;
private long duration = 0; //TODO Duration
@Deprecated //TODO Replace by storing latitude/longitude directly.
private final Location location;
@@ -95,6 +96,10 @@ public final class Marker {
this.id = id;
}
public Instant getTime() {
return Instant.ofEpochMilli(location.getTime());
}
public String getName() {
return name;
}
@@ -21,6 +21,7 @@ import android.os.Parcel;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.time.Instant;
import java.util.Objects;
/**
@@ -43,7 +44,7 @@ public class TrackPoint {
private TrackPoint.Id id;
private long time_ms;
private Instant time;
private Double latitude;
private Double longitude;
private Float accuracy;
@@ -101,49 +102,49 @@ public class TrackPoint {
this.speed_mps = location.getSpeed();
this.accuracy = location.getAccuracy();
setTime(System.currentTimeMillis());
setTime(Instant.now());
}
public TrackPoint(@NonNull Type type, long time_ms) {
public TrackPoint(@NonNull Type type, Instant time) {
this(type);
this.time_ms = time_ms;
this.time = time;
}
public TrackPoint(double latitude, double longitude, Double altitude, long time) {
public TrackPoint(double latitude, double longitude, Double altitude, Instant time) {
this(Type.TRACKPOINT);
this.latitude = latitude;
this.longitude = longitude;
this.altitude_m = altitude;
this.time_ms = time;
this.time = time;
}
@Deprecated //See #316
public static TrackPoint createSegmentStartManual() {
return createSegmentStartManualWithTime(System.currentTimeMillis());
return createSegmentStartManualWithTime(Instant.now());
}
public static TrackPoint createSegmentStartManualWithTime(long time) {
public static TrackPoint createSegmentStartManualWithTime(Instant time) {
return new TrackPoint(Type.SEGMENT_START_MANUAL, time);
}
@Deprecated //See #316
public static TrackPoint createSegmentStartAutomatic() {
return createSegmentStartAutomaticWithTime(System.currentTimeMillis());
return createSegmentStartAutomaticWithTime(Instant.now());
}
public static TrackPoint createSegmentStartAutomaticWithTime(long time) {
public static TrackPoint createSegmentStartAutomaticWithTime(Instant time) {
return new TrackPoint(Type.SEGMENT_START_AUTOMATIC, time);
}
public static TrackPoint createSegmentEnd() {
return createSegmentEndWithTime(System.currentTimeMillis());
return createSegmentEndWithTime(Instant.now());
}
public static TrackPoint createSegmentEndWithTime(@NonNull TrackPoint trackPoint) {
return createSegmentEndWithTime(trackPoint.getTime());
}
public static TrackPoint createSegmentEndWithTime(long time) {
public static TrackPoint createSegmentEndWithTime(Instant time) {
return new TrackPoint(Type.SEGMENT_END_MANUAL, time);
}
@@ -183,7 +184,7 @@ public class TrackPoint {
@Nullable
public Location getLocation() {
Location location = new Location("");
location.setTime(time_ms);
location.setTime(time.toEpochMilli());
if (hasLocation()) {
location.setLatitude(latitude);
location.setLongitude(longitude);
@@ -241,12 +242,12 @@ public class TrackPoint {
this.longitude = longitude;
}
public long getTime() {
return time_ms;
public Instant getTime() {
return time;
}
public void setTime(long time) {
this.time_ms = time;
public void setTime(Instant time) {
this.time = time;
}
public boolean hasAltitude() {
@@ -29,6 +29,8 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import java.io.File;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@@ -118,19 +120,19 @@ public class ContentProviderUtils {
track.setCategory(cursor.getString(categoryIndex));
}
if (!cursor.isNull(startTimeIndex)) {
trackStatistics.setStartTime_ms(cursor.getLong(startTimeIndex));
trackStatistics.setStartTime(Instant.ofEpochMilli(cursor.getLong(startTimeIndex)));
}
if (!cursor.isNull(stopTimeIndex)) {
trackStatistics.setStopTime_ms(cursor.getLong(stopTimeIndex));
trackStatistics.setStopTime(Instant.ofEpochMilli(cursor.getLong(stopTimeIndex)));
}
if (!cursor.isNull(totalDistanceIndex)) {
trackStatistics.setTotalDistance(cursor.getFloat(totalDistanceIndex));
}
if (!cursor.isNull(totalTimeIndex)) {
trackStatistics.setTotalTime(cursor.getLong(totalTimeIndex));
trackStatistics.setTotalTime(Duration.ofMillis(cursor.getLong(totalTimeIndex)));
}
if (!cursor.isNull(movingTimeIndex)) {
trackStatistics.setMovingTime(cursor.getLong(movingTimeIndex));
trackStatistics.setMovingTime(Duration.ofMillis(cursor.getLong(movingTimeIndex)));
}
if (!cursor.isNull(maxSpeedIndex)) {
trackStatistics.setMaxSpeed(cursor.getFloat(maxSpeedIndex));
@@ -270,11 +272,11 @@ public class ContentProviderUtils {
values.put(TracksColumns.NAME, track.getName());
values.put(TracksColumns.DESCRIPTION, track.getDescription());
values.put(TracksColumns.CATEGORY, track.getCategory());
values.put(TracksColumns.STARTTIME, trackStatistics.getStartTime_ms());
values.put(TracksColumns.STOPTIME, trackStatistics.getStopTime_ms());
values.put(TracksColumns.STARTTIME, trackStatistics.getStartTime().toEpochMilli());
values.put(TracksColumns.STOPTIME, trackStatistics.getStopTime().toEpochMilli());
values.put(TracksColumns.TOTALDISTANCE, trackStatistics.getTotalDistance());
values.put(TracksColumns.TOTALTIME, trackStatistics.getTotalTime());
values.put(TracksColumns.MOVINGTIME, trackStatistics.getMovingTime());
values.put(TracksColumns.TOTALTIME, trackStatistics.getTotalTime().toMillis());
values.put(TracksColumns.MOVINGTIME, trackStatistics.getMovingTime().toMillis());
values.put(TracksColumns.AVGSPEED, trackStatistics.getAverageSpeed());
values.put(TracksColumns.AVGMOVINGSPEED, trackStatistics.getAverageMovingSpeed());
values.put(TracksColumns.MAXSPEED, trackStatistics.getMaxSpeed());
@@ -533,7 +535,7 @@ public class ContentProviderUtils {
trackPoint.setLatitude(((double) cursor.getInt(indexes.latitudeIndex)) / 1E6);
}
if (!cursor.isNull(indexes.timeIndex)) {
trackPoint.setTime(cursor.getLong(indexes.timeIndex));
trackPoint.setTime(Instant.ofEpochMilli(cursor.getLong(indexes.timeIndex)));
}
if (!cursor.isNull(indexes.altitudeIndex)) {
trackPoint.setAltitude(cursor.getFloat(indexes.altitudeIndex));
@@ -704,7 +706,7 @@ public class ContentProviderUtils {
values.put(TrackPointsColumns.LONGITUDE, (int) (trackPoint.getLongitude() * 1E6));
values.put(TrackPointsColumns.LATITUDE, (int) (trackPoint.getLatitude() * 1E6));
}
values.put(TrackPointsColumns.TIME, trackPoint.getTime());
values.put(TrackPointsColumns.TIME, trackPoint.getTime().toEpochMilli());
if (trackPoint.hasAltitude()) {
values.put(TrackPointsColumns.ALTITUDE, trackPoint.getAltitude());
}
@@ -56,7 +56,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
private static final String KEY_CHART_VIEW_BY_DISTANCE_KEY = "chartViewByDistance";
public static Fragment newInstance(boolean chartByDistance) {
public static ChartFragment newInstance(boolean chartByDistance) {
Bundle bundle = new Bundle();
bundle.putBoolean(KEY_CHART_VIEW_BY_DISTANCE_KEY, chartByDistance);
@@ -14,6 +14,8 @@ import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@@ -407,7 +409,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
}
private void updateTotalTime() {
long totalTime;
Duration totalTime;
if (isSelectedTrackRecording()) {
totalTime = calculateTotalTime();
} else {
@@ -420,12 +422,12 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
* Return time from service.
* If service isn't bound then use lastTrackStatistics for calculate it.
*/
private long calculateTotalTime() {
private Duration calculateTotalTime() {
TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
if (trackRecordingService != null) {
return trackRecordingService.getTotalTime();
} else {
return System.currentTimeMillis() - lastTrackStatistics.getStopTime_ms() + lastTrackStatistics.getTotalTime();
return Duration.between(lastTrackStatistics.getStopTime().plus(lastTrackStatistics.getTotalTime()), Instant.now());
}
}
@@ -22,6 +22,8 @@ import android.util.Log;
import androidx.annotation.NonNull;
import java.io.OutputStream;
import java.time.Duration;
import java.time.Instant;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track;
@@ -71,9 +73,9 @@ public class FileTrackExporter implements TrackExporter {
trackWriter.writeMultiTrackBegin();
}
//TODO Why use startTime of first track for the others?
long startTime = tracks[0].getTrackStatistics().getStartTime_ms();
Instant startTime = tracks[0].getTrackStatistics().getStartTime();
for (Track track : tracks) {
long offset = track.getTrackStatistics().getStartTime_ms() - startTime;
Duration offset = Duration.between(track.getTrackStatistics().getStartTime(), startTime);
writeLocations(track, offset);
}
if (hasMultipleTracks) {
@@ -120,7 +122,7 @@ public class FileTrackExporter implements TrackExporter {
/**
* Writes the locations.
*/
private void writeLocations(Track track, long offset) throws InterruptedException {
private void writeLocations(Track track, Duration offset) throws InterruptedException {
boolean wroteTrack = false;
boolean wroteSegment = false;
@@ -180,9 +182,9 @@ public class FileTrackExporter implements TrackExporter {
* @param offset the time offset
*/
//TODO Why?
private void setLocationTime(TrackPoint trackPoint, long offset) {
private void setLocationTime(TrackPoint trackPoint, Duration offset) {
if (trackPoint != null) {
trackPoint.setTime(trackPoint.getTime() - offset);
trackPoint.setTime(trackPoint.getTime().minus(offset));
}
}
}
@@ -21,6 +21,7 @@ import android.location.Location;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.NumberFormat;
import java.time.Instant;
import java.util.Locale;
import de.dennisguse.opentracks.content.data.Marker;
@@ -137,7 +138,7 @@ public class GpxTrackWriter implements TrackWriter {
if (location.hasAltitude()) {
printWriter.println("<ele>" + ELEVATION_FORMAT.format(location.getAltitude()) + "</ele>");
}
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</time>");
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(Instant.ofEpochMilli(location.getTime())) + "</time>");
printWriter.println("<name>" + StringUtils.formatCData(marker.getName()) + "</name>");
printWriter.println("<desc>" + StringUtils.formatCData(marker.getDescription()) + "</desc>");
printWriter.println("<type>" + StringUtils.formatCData(marker.getCategory()) + "</type>");
@@ -20,6 +20,7 @@ import android.location.Location;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -385,9 +386,9 @@ public class KmlTrackWriter implements TrackWriter {
*/
private String getTime(Location location) {
if (exportTrackDetail) {
return StringUtils.formatDateTimeIso8601(location.getTime());
return StringUtils.formatDateTimeIso8601(Instant.ofEpochMilli(location.getTime()));
} else {
return StringUtils.formatDateTimeIso8601(location.getTime() - startTrackPoint.getTime());
return StringUtils.formatDateTimeIso8601(Instant.ofEpochMilli(location.getTime() - startTrackPoint.getTime().toEpochMilli()));
}
}
@@ -28,8 +28,9 @@ import org.xml.sax.helpers.DefaultHandler;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
@@ -50,7 +51,6 @@ import de.dennisguse.opentracks.util.LocationUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.StringUtils;
import de.dennisguse.opentracks.util.TrackIconUtils;
import de.dennisguse.opentracks.util.UnitConversions;
/**
* Abstract class for file track importers.
@@ -185,9 +185,9 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
}
if (marker.getLocation().getTime() > trackPoint.getTime()) {
if ((marker.getTime()).isAfter(trackPoint.getTime())) {
trackPoint = null;
} else if (marker.getLocation().getTime() < trackPoint.getTime()) {
} else if (marker.getTime().isBefore(trackPoint.getTime())) {
Log.w(TAG, "Ignoring marker: current trackPoint was after marker.");
marker = null;
} else {
@@ -203,7 +203,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
String markerDescription = marker.getDescription();
String icon = context.getString(R.string.marker_icon_url);
double length = trackStatisticsUpdater.getTrackStatistics().getTotalDistance();
long duration = trackStatisticsUpdater.getTrackStatistics().getTotalTime();
long duration = trackStatisticsUpdater.getTrackStatistics().getTotalTime().toMillis();
// Insert marker
Marker newMarker = new Marker(marker.getName(), markerDescription, marker.getCategory(), icon, track.getId(), length, duration, trackPoint.getLocation(), marker.getPhotoUrl());
@@ -338,8 +338,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
TrackPoint trackPoint = createTrackPoint();
if (trackPoint.hasLocation()) {
long time = trackPoint.getTime();
Date d = new Date(time);
Instant time = trackPoint.getTime();
if (trackPoint.getLatitude() == 100) {
//TODO Remove by 31st December 2021.
trackPoint = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL);
@@ -355,12 +354,12 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
}
// Calculate derived attributes from the previous point
if (trackData.lastLocationInCurrentSegment != null && trackData.lastLocationInCurrentSegment.getTime() != 0) {
if (trackData.lastLocationInCurrentSegment != null && trackData.lastLocationInCurrentSegment.getTime() == null) {
if (!trackPoint.hasSpeed()) {
long timeDifference = trackPoint.getTime() - trackData.lastLocationInCurrentSegment.getTime();
Duration timeDifference = Duration.between(trackData.lastLocationInCurrentSegment.getTime(), trackPoint.getTime());
// Check for negative time change
if (timeDifference <= 0) {
if (timeDifference.isNegative()) {
Log.w(TAG, "Time difference not positive.");
} else {
@@ -368,9 +367,8 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
* We don't have a speed and bearing in GPX, make something up from the last two points.
* GPS points tend to have some inherent imprecision, speed and bearing will likely be off, so the statistics for things like max speed will also be off.
*/
double duration = timeDifference * UnitConversions.MS_TO_S;
if (trackPoint.hasLocation() && trackData.lastLocationInCurrentSegment.hasLocation()) {
double speed = trackData.lastLocationInCurrentSegment.distanceTo(trackPoint) / duration;
float speed = trackData.lastLocationInCurrentSegment.distanceTo(trackPoint) / timeDifference.toMillis();
trackPoint.setSpeed((float) speed);
}
}
@@ -35,6 +35,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.core.app.TaskStackBuilder;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@@ -228,12 +230,12 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
}
//TODO Throw exception, when not recording.
public long getTotalTime() {
public Duration getTotalTime() {
if (trackStatisticsUpdater == null) {
return 0;
return Duration.ofSeconds(0);
}
if (!isPaused()) {
trackStatisticsUpdater.updateTime(System.currentTimeMillis());
trackStatisticsUpdater.updateTime(Instant.now());
}
return trackStatisticsUpdater.getTrackStatistics().getTotalTime();
}
@@ -264,7 +266,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
TrackStatistics stats = trackStatisticsUpdater.getTrackStatistics();
double length = stats.getTotalDistance();
long duration = stats.getTotalTime();
long duration = stats.getTotalTime().toMillis();
// Insert marker
Marker marker = new Marker(name, description, category, icon, recordingTrackId, length, duration, trackPoint.getLocation(), photoUrl);
@@ -325,7 +327,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
}
// Sync the real time setting the stop time with current time.
track.getTrackStatistics().setStopTime_ms(System.currentTimeMillis());
track.getTrackStatistics().setStopTime(Instant.now());
trackStatisticsUpdater = new TrackStatisticsUpdater(track.getTrackStatistics());
insertTrackPoint(track, TrackPoint.createSegmentStartManual());
@@ -637,7 +639,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
*/
private void insertTrackPointIfNewer(@NonNull Track track, @NonNull TrackPoint trackPoint) {
TrackPoint lastValidTrackPoint = getLastValidTrackPointInCurrentSegment(track.getId());
if (TrackPointUtils.equalTime(trackPoint, lastValidTrackPoint)) {
if (lastValidTrackPoint != null && trackPoint.getTime().equals(lastValidTrackPoint.getTime())) {
// Do not insert if inserted already
Log.w(TAG, "Ignore insertTrackPoint. trackPoint time same as last valid track point time.");
return;
@@ -678,7 +680,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
* @param track the track
*/
private void updateTrackTotalTime(Track track) {
trackStatisticsUpdater.updateTime(System.currentTimeMillis());
trackStatisticsUpdater.updateTime(Instant.now());
track.setTrackStatistics(trackStatisticsUpdater.getTrackStatistics());
contentProviderUtils.updateTrack(track);
}
@@ -2,6 +2,8 @@ package de.dennisguse.opentracks.services;
import androidx.annotation.VisibleForTesting;
import java.time.Duration;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
@@ -85,7 +87,7 @@ class TrackRecordingServiceBinder extends android.os.Binder implements TrackReco
}
@Override
public long getTotalTime() {
public Duration getTotalTime() {
return trackRecordingService.getTotalTime();
}
@@ -17,6 +17,8 @@ package de.dennisguse.opentracks.services;
import androidx.annotation.VisibleForTesting;
import java.time.Duration;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
@@ -59,8 +61,7 @@ public interface TrackRecordingServiceInterface {
/**
* Gets the total time for the current recording track. Returns 0 if not recording.
*/
//TODO milliseconds?
long getTotalTime();
Duration getTotalTime();
Marker.Id insertMarker(String name, String category, String description, String photoUrl);
@@ -16,6 +16,8 @@
package de.dennisguse.opentracks.services.handlers;
import java.time.Duration;
/**
* This is a simple location listener policy that will always dictate the same polling interval.
*
@@ -23,19 +25,19 @@ package de.dennisguse.opentracks.services.handlers;
*/
public class AbsoluteLocationListenerPolicy implements LocationListenerPolicy {
private final long interval;
private final Duration interval;
/**
* Constructor.
*
* @param interval the interval to request for gps signal
*/
public AbsoluteLocationListenerPolicy(long interval) {
public AbsoluteLocationListenerPolicy(Duration interval) {
this.interval = interval;
}
@Override
public long getDesiredPollingInterval() {
public Duration getDesiredPollingInterval() {
return interval;
}
@@ -45,7 +47,7 @@ public class AbsoluteLocationListenerPolicy implements LocationListenerPolicy {
}
@Override
public void updateIdleTime(long idleTime) {
public void updateIdleTime(Duration idleTime) {
// Ignore
}
}
@@ -16,6 +16,8 @@
package de.dennisguse.opentracks.services.handlers;
import java.time.Duration;
/**
* A {@link LocationListenerPolicy} that will change based on how long the user has been stationary.
* This policy will dictate a policy based on a min, max and idle time.
@@ -25,35 +27,41 @@ package de.dennisguse.opentracks.services.handlers;
*/
public class AdaptiveLocationListenerPolicy implements LocationListenerPolicy {
private final long minInterval_ms;
private final long maxInterval_ms;
private final Duration minInterval;
private final Duration maxInterval;
private final int minDistance_m;
// The time the user has been idle at the current location, in milliseconds.
private long idleTime;
private Duration idleTime;
/**
* Creates a policy that will be bounded by the given minInterval_ms and maxInterval_ms.
*
* @param minInterval_ms the smallest interval this policy will dictate
* @param maxInterval_ms the largest interval this policy will dictate
* @param minInterval the smallest interval this policy will dictate
* @param maxInterval the largest interval this policy will dictate
* @param minDistance_m the minimum distance
*/
public AdaptiveLocationListenerPolicy(long minInterval_ms, long maxInterval_ms, int minDistance_m) {
this.minInterval_ms = minInterval_ms;
this.maxInterval_ms = maxInterval_ms;
public AdaptiveLocationListenerPolicy(Duration minInterval, Duration maxInterval, int minDistance_m) {
this.minInterval = minInterval;
this.maxInterval = maxInterval;
this.minDistance_m = minDistance_m;
}
/*
* Returns an interval half of the idle time, but bounded by minInterval_ms and maxInterval_ms.
* Returns an interval half of the idle time, but bounded by minInterval and maxInterval.
*/
@Override
public long getDesiredPollingInterval() {
long desiredInterval_ms = idleTime / 2;
public Duration getDesiredPollingInterval() {
Duration desiredInterval = idleTime.dividedBy(2);
// Round to second to avoid setting the interval too often
desiredInterval_ms = (desiredInterval_ms / 1000) * 1000;
return Math.max(Math.min(maxInterval_ms, desiredInterval_ms), minInterval_ms);
desiredInterval = Duration.ofSeconds(desiredInterval.getSeconds());
if (minInterval.compareTo(desiredInterval) > 0) {
return minInterval;
} else if (maxInterval.compareTo(desiredInterval) < 0) {
return maxInterval;
}
return desiredInterval;
}
@Override
@@ -62,7 +70,7 @@ public class AdaptiveLocationListenerPolicy implements LocationListenerPolicy {
}
@Override
public void updateIdleTime(long newIdleTime) {
public void updateIdleTime(Duration newIdleTime) {
idleTime = newIdleTime;
}
}
@@ -6,9 +6,11 @@ import android.os.Handler;
import androidx.annotation.Nullable;
import java.time.Duration;
import java.time.Instant;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.UnitConversions;
/**
* This class handle GPS status according to received locations and some thresholds.
@@ -17,13 +19,13 @@ class GpsStatus {
private static final String TAG = GpsStatus.class.getSimpleName();
// The quantity of milliseconds that GpsStatus waits from minimal interval to consider GPS lost.
private static final int SIGNAL_LOST_THRESHOLD = (int) (10 * UnitConversions.S_TO_MS);
// The duration that GpsStatus waits from minimal interval to consider GPS lost.
private static final Duration SIGNAL_LOST_THRESHOLD = Duration.ofSeconds(10);
// Threshold for accuracy.
private double signalBadThreshold;
// Threshold for time without points.
private int signalLostThreshold;
private Duration signalLostThreshold;
private GpsStatusValue gpsStatus = GpsStatusValue.GPS_NONE;
private GpsStatusListener client;
@@ -46,7 +48,7 @@ class GpsStatus {
public void run() {
if (gpsStatus != null && !stopped) {
onLocationChanged(null);
gpsStatusHandler.postDelayed(gpsStatusRunner, getIntervalThreshold());
gpsStatusHandler.postDelayed(gpsStatusRunner, getIntervalThreshold().toMillis());
}
}
@@ -63,11 +65,11 @@ class GpsStatus {
* @param client The client.
* @param minRecordingInterval Value of min recording interval preference.
*/
public GpsStatus(Context context, GpsStatusListener client, int minRecordingInterval) {
public GpsStatus(Context context, GpsStatusListener client, Duration minRecordingInterval) {
this.client = client;
this.context = context;
signalBadThreshold = PreferencesUtils.getRecordingDistanceInterval(context);
signalLostThreshold = minRecordingInterval > 0 ? minRecordingInterval * (int) UnitConversions.ONE_SECOND_MS + SIGNAL_LOST_THRESHOLD : SIGNAL_LOST_THRESHOLD;
signalLostThreshold = !minRecordingInterval.isNegative() ? SIGNAL_LOST_THRESHOLD.plus(minRecordingInterval) : SIGNAL_LOST_THRESHOLD;
gpsStatusHandler = new Handler();
}
@@ -97,7 +99,7 @@ class GpsStatus {
* @param value Minimal recording interval preference value in seconds or an special value: -1, -2, 0.
*/
public void onMinRecordingIntervalChanged(int value) {
signalLostThreshold = value > 0 ? value * (int) UnitConversions.ONE_SECOND_MS + SIGNAL_LOST_THRESHOLD : SIGNAL_LOST_THRESHOLD;
signalLostThreshold = value > 0 ? SIGNAL_LOST_THRESHOLD.plus(Duration.ofSeconds(value)) : SIGNAL_LOST_THRESHOLD;
}
/**
@@ -119,7 +121,7 @@ class GpsStatus {
if (trackPoint != null) {
// Update trackPoint's time to the current time millis when trackPoint has been received.
trackPoint.setTime(System.currentTimeMillis());
trackPoint.setTime(Instant.now());
lastValidTrackPoint = trackPoint;
}
lastTrackPoint = trackPoint;
@@ -133,7 +135,7 @@ class GpsStatus {
* Also, it'll run the runnable if signal is bad or stop it if the signal is lost.
*/
private void checkStatusFromLastLocation() {
if (System.currentTimeMillis() - lastTrackPoint.getTime() > signalLostThreshold && gpsStatus != GpsStatusValue.GPS_SIGNAL_LOST) {
if (Duration.between(lastTrackPoint.getTime(), Instant.now()).compareTo(signalLostThreshold) > 0 && gpsStatus != GpsStatusValue.GPS_SIGNAL_LOST) {
// Too much time without receiving signal -> signal lost.
GpsStatusValue oldStatus = gpsStatus;
gpsStatus = GpsStatusValue.GPS_SIGNAL_LOST;
@@ -160,7 +162,8 @@ class GpsStatus {
* If there is any change then it does the change.
*/
private void checkStatusFromLastValidLocation() {
if (System.currentTimeMillis() - lastValidTrackPoint.getTime() > signalLostThreshold) {
Duration elapsed = Duration.between(lastValidTrackPoint.getTime(), Instant.now());
if (signalLostThreshold.minus(elapsed).isNegative()) {
// Too much time without locations -> lost signal? (wait signalLostThreshold from last valid location).
GpsStatusValue oldStatus = gpsStatus;
gpsStatus = GpsStatusValue.GPS_SIGNAL_LOST;
@@ -222,7 +225,7 @@ class GpsStatus {
}
}
public int getIntervalThreshold() {
public Duration getIntervalThreshold() {
return signalLostThreshold;
}
@@ -10,12 +10,13 @@ import android.util.Log;
import androidx.annotation.NonNull;
import java.time.Duration;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.util.LocationUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.TrackPointUtils;
import de.dennisguse.opentracks.util.UnitConversions;
class LocationHandler implements HandlerServer.Handler, LocationListener, GpsStatus.GpsStatusListener {
@@ -25,7 +26,7 @@ class LocationHandler implements HandlerServer.Handler, LocationListener, GpsSta
private final HandlerServer handlerServer;
private GpsStatus gpsStatus;
private LocationListenerPolicy locationListenerPolicy;
private long currentRecordingInterval;
private Duration currentRecordingInterval;
private int recordingGpsAccuracy;
private TrackPoint lastValidTrackPoint;
@@ -35,7 +36,7 @@ class LocationHandler implements HandlerServer.Handler, LocationListener, GpsSta
@Override
public void onStart(Context context) {
gpsStatus = new GpsStatus(context, this, PreferencesUtils.getMinRecordingInterval(context));
gpsStatus = new GpsStatus(context, this, Duration.ofSeconds(PreferencesUtils.getMinRecordingInterval(context)));
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
registerLocationListener();
}
@@ -56,12 +57,12 @@ class LocationHandler implements HandlerServer.Handler, LocationListener, GpsSta
int minRecordingInterval = PreferencesUtils.getMinRecordingInterval(context);
if (minRecordingInterval == PreferencesUtils.getMinRecordingIntervalAdaptBatteryLife(context)) {
// Choose battery life over moving time accuracy.
locationListenerPolicy = new AdaptiveLocationListenerPolicy(30 * UnitConversions.ONE_SECOND_MS, 5 * UnitConversions.ONE_MINUTE_MS, 5);
locationListenerPolicy = new AdaptiveLocationListenerPolicy(Duration.ofSeconds(30), Duration.ofSeconds(5), 5);
} else if (minRecordingInterval == PreferencesUtils.getMinRecordingIntervalAdaptAccuracy(context)) {
// Get all the updates.
locationListenerPolicy = new AdaptiveLocationListenerPolicy(UnitConversions.ONE_SECOND_MS, 30 * UnitConversions.ONE_SECOND_MS, 0);
locationListenerPolicy = new AdaptiveLocationListenerPolicy(Duration.ofSeconds(1), Duration.ofSeconds(30), 0);
} else {
locationListenerPolicy = new AbsoluteLocationListenerPolicy(minRecordingInterval * UnitConversions.ONE_SECOND_MS);
locationListenerPolicy = new AbsoluteLocationListenerPolicy(Duration.ofSeconds(minRecordingInterval));
}
if (locationManager != null) {
@@ -108,9 +109,9 @@ class LocationHandler implements HandlerServer.Handler, LocationListener, GpsSta
return;
}
long idleTime = 0L;
if (TrackPointUtils.after(trackPoint, lastValidTrackPoint)) {
idleTime = trackPoint.getTime() - lastValidTrackPoint.getTime();
Duration idleTime = Duration.ofSeconds(0);
if (lastValidTrackPoint != null && trackPoint.getTime().isAfter(lastValidTrackPoint.getTime())) {
idleTime = Duration.between(lastValidTrackPoint.getTime(), trackPoint.getTime());
}
locationListenerPolicy.updateIdleTime(idleTime);
@@ -146,9 +147,9 @@ class LocationHandler implements HandlerServer.Handler, LocationListener, GpsSta
return;
}
try {
long interval = locationListenerPolicy.getDesiredPollingInterval();
Duration interval = locationListenerPolicy.getDesiredPollingInterval();
currentRecordingInterval = interval;
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, interval, locationListenerPolicy.getMinDistance_m(), this);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, interval.toMillis(), locationListenerPolicy.getMinDistance_m(), this);
} catch (SecurityException e) {
Log.e(TAG, "Could not register location listener; permissions not granted.", e);
}
@@ -16,6 +16,8 @@
package de.dennisguse.opentracks.services.handlers;
import java.time.Duration;
/**
* This is an interface for classes that will manage the location listener policy.
*
@@ -28,7 +30,7 @@ interface LocationListenerPolicy {
*
* @return the polling interval
*/
long getDesiredPollingInterval();
Duration getDesiredPollingInterval();
/**
* Returns the minimum distance between updates.
@@ -40,5 +42,5 @@ interface LocationListenerPolicy {
*
* @param idleTime the time that the user has been idle at his current location
*/
void updateIdleTime(long idleTime);
void updateIdleTime(Duration idleTime);
}
@@ -17,6 +17,8 @@ package de.dennisguse.opentracks.services.tasks;
import android.util.Log;
import java.time.Duration;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.services.TrackRecordingService;
import de.dennisguse.opentracks.stats.TrackStatistics;
@@ -92,7 +94,7 @@ public class PeriodicTaskExecutor {
if (timerTaskExecutor == null) {
timerTaskExecutor = new TimerTaskExecutor(periodicTask, trackRecordingService);
}
timerTaskExecutor.scheduleTask(taskFrequency * UnitConversions.ONE_MINUTE_MS);
timerTaskExecutor.scheduleTask(Duration.ofSeconds(taskFrequency));
} else {
// For distance periodic task
calculateNextTaskDistance();
@@ -16,6 +16,7 @@
package de.dennisguse.opentracks.services.tasks;
import java.time.Duration;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
@@ -42,15 +43,9 @@ class TimerTaskExecutor {
}
/**
* Schedules the periodic task in milliseconds.
*
* @param interval_ms the interval_ms
* Schedules the periodic task.
*/
void scheduleTask(long interval_ms) {
if (interval_ms <= 0) {
return;
}
void scheduleTask(Duration interval) {
if (!trackRecordingService.isRecording() || trackRecordingService.isPaused()) {
return;
}
@@ -69,8 +64,10 @@ class TimerTaskExecutor {
}
};
timer = new Timer(TimerTaskExecutor.class.getSimpleName());
long next = System.currentTimeMillis() + interval_ms - (trackStatistics.getTotalTime() % interval_ms);
timer.scheduleAtFixedRate(timerTask, new Date(next), interval_ms);
//TODO Simplify: far too complicated for it's purpose
long next = System.currentTimeMillis() + interval.toMillis() - (trackStatistics.getTotalTime().toMillis() % interval.toMillis());
timer.scheduleAtFixedRate(timerTask, new Date(next), interval.toMillis());
}
void shutdown() {
@@ -19,6 +19,9 @@ package de.dennisguse.opentracks.stats;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.time.Duration;
import java.time.Instant;
/**
* Statistical data about a {@link de.dennisguse.opentracks.content.data.Track}.
* The data in this class should be filled out by {@link TrackStatisticsUpdater}.
@@ -33,15 +36,15 @@ public class TrackStatistics {
private final ExtremityMonitor elevationExtremities = new ExtremityMonitor();
// The track start time.
private long startTime_ms;
private Instant startTime;
// The track stop time.
private long stopTime_ms;
private Instant stopTime;
private double totalDistance_m;
// Updated when new points are received, may be stale.
private long totalTime_ms;
private Duration totalTime;
// Based on when we believe the user is traveling.
private long movingTime_ms;
private Duration movingTime;
// The maximum speed (meters/second) that we believe is valid.
private double maxSpeed_mps;
// The total elevation gained (meters).
@@ -60,11 +63,11 @@ public class TrackStatistics {
* @param other another statistics data object to copy from
*/
public TrackStatistics(TrackStatistics other) {
startTime_ms = other.startTime_ms;
stopTime_ms = other.stopTime_ms;
startTime = other.startTime;
stopTime = other.stopTime;
totalDistance_m = other.totalDistance_m;
totalTime_ms = other.totalTime_ms;
movingTime_ms = other.movingTime_ms;
totalTime = other.totalTime;
movingTime = other.movingTime;
maxSpeed_mps = other.maxSpeed_mps;
elevationExtremities.set(other.elevationExtremities.getMin(), other.elevationExtremities.getMax());
totalElevationGain_m = other.totalElevationGain_m;
@@ -78,11 +81,11 @@ public class TrackStatistics {
* @param other another statistics data object
*/
public void merge(TrackStatistics other) {
startTime_ms = Math.min(startTime_ms, other.startTime_ms);
stopTime_ms = Math.max(stopTime_ms, other.stopTime_ms);
startTime = startTime.isBefore(other.startTime) ? startTime : other.startTime;
stopTime = stopTime.isAfter(other.stopTime) ? stopTime : other.stopTime;
totalDistance_m += other.totalDistance_m;
totalTime_ms += other.totalTime_ms;
movingTime_ms += other.movingTime_ms;
totalTime = totalTime.plus(other.totalTime);
movingTime = movingTime.plus(other.movingTime);
maxSpeed_mps = Math.max(maxSpeed_mps, other.maxSpeed_mps);
if (other.elevationExtremities.hasData()) {
elevationExtremities.update(other.elevationExtremities.getMin());
@@ -109,44 +112,41 @@ public class TrackStatistics {
}
public void reset() {
setStartTime_ms(0);
setStartTime(Instant.ofEpochMilli(0)); //TODO should probably initialized with null
setTotalDistance(0);
setTotalTime(0);
setMovingTime(0);
setTotalTime(Duration.ofSeconds(0));
setMovingTime(Duration.ofSeconds(0));
setMaxSpeed(0);
setTotalElevationGain(null);
}
public void reset(long startTime_ms) {
public void reset(Instant startTime) {
reset();
setStartTime_ms(startTime_ms);
setStartTime(startTime);
}
/**
* Gets the track start time. The number of milliseconds since epoch.
*/
public long getStartTime_ms() {
return startTime_ms;
public Instant getStartTime() {
return startTime;
}
/**
* Should only be called on start.
*/
public void setStartTime_ms(long startTime_ms) {
this.startTime_ms = startTime_ms;
setStopTime_ms(startTime_ms);
public void setStartTime(Instant startTime) {
this.startTime = startTime;
setStopTime(startTime);
}
public long getStopTime_ms() {
return stopTime_ms;
public Instant getStopTime() {
return stopTime;
}
public void setStopTime_ms(long stopTime_ms) {
if (stopTime_ms < startTime_ms) {
throw new RuntimeException("stopTime cannot be less than startTime: " + startTime_ms + " " + stopTime_ms);
public void setStopTime(Instant stopTime) {
if (stopTime.isBefore(startTime)) {
throw new RuntimeException("stopTime cannot be less than startTime: " + startTime + " " + stopTime);
}
this.stopTime_ms = stopTime_ms;
this.stopTime = stopTime;
}
public double getTotalDistance() {
@@ -164,26 +164,26 @@ public class TrackStatistics {
/**
* Gets the total time in milliseconds that this track has been active.
* This statistic is only updated when a new point is added to the statistics, so it may be off.
* If you need to calculate the proper total time, use {@link #getStartTime_ms} with the current time.
* If you need to calculate the proper total time, use {@link #getStartTime} with the current time.
*/
public long getTotalTime() {
return totalTime_ms;
public Duration getTotalTime() {
return totalTime;
}
public void setTotalTime(long totalTime_ms) {
this.totalTime_ms = totalTime_ms;
public void setTotalTime(Duration totalTime) {
this.totalTime = totalTime;
}
public long getMovingTime() {
return movingTime_ms;
public Duration getMovingTime() {
return movingTime;
}
public void setMovingTime(long movingTime_ms) {
this.movingTime_ms = movingTime_ms;
public void setMovingTime(Duration movingTime) {
this.movingTime = movingTime;
}
public void addMovingTime(long time_ms) {
movingTime_ms += time_ms;
public void addMovingTime(Duration time) {
movingTime = movingTime.plus(time);
}
/**
@@ -191,20 +191,20 @@ public class TrackStatistics {
* This calculation only takes into account the displacement until the last point that was accounted for in statistics.
*/
public double getAverageSpeed() {
if (totalTime_ms == 0L) {
if (totalTime.isZero()) {
return 0.0;
}
return totalDistance_m / ((double) totalTime_ms / 1000.0);
return totalDistance_m / (double) totalTime.getSeconds();
}
/**
* Gets the average moving speed in meters/second.
*/
public double getAverageMovingSpeed() {
if (movingTime_ms == 0L) {
if (movingTime.isZero()) {
return 0.0;
}
return totalDistance_m / ((double) movingTime_ms / 1000.0);
return totalDistance_m / (double) movingTime.getSeconds();
}
/**
@@ -317,7 +317,7 @@ public class TrackStatistics {
@NonNull
@Override
public String toString() {
return "TrackStatistics { Start Time: " + getStartTime_ms() + "; Stop Time: " + getStopTime_ms()
return "TrackStatistics { Start Time: " + getStartTime() + "; Stop Time: " + getStopTime()
+ "; Total Distance: " + getTotalDistance() + "; Total Time: " + getTotalTime()
+ "; Moving Time: " + getMovingTime() + "; Max Speed: " + getMaxSpeed()
+ "; Min Elevation: " + getMinElevation() + "; Max Elevation: " + getMaxElevation()
@@ -21,6 +21,9 @@ import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import java.time.Duration;
import java.time.Instant;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
import de.dennisguse.opentracks.util.TrackPointUtils;
@@ -88,9 +91,9 @@ public class TrackStatisticsUpdater {
trackInitialized = true;
}
public void updateTime(long time_ms) {
currentSegment.setStopTime_ms(time_ms);
currentSegment.setTotalTime(time_ms - currentSegment.getStartTime_ms());
public void updateTime(Instant time) {
currentSegment.setStopTime(time);
currentSegment.setTotalTime(Duration.between(currentSegment.getStartTime(), time));
}
public TrackStatistics getTrackStatistics() {
@@ -108,11 +111,11 @@ public class TrackStatisticsUpdater {
*/
public void addTrackPoint(TrackPoint trackPoint, int minRecordingDistance) {
if (!trackInitialized) {
trackStatistics.setStartTime_ms(trackPoint.getTime());
trackStatistics.setStartTime(trackPoint.getTime());
trackInitialized = true;
}
if (!segmentInitialized) {
currentSegment.setStartTime_ms(trackPoint.getTime());
currentSegment.setStartTime(trackPoint.getTime());
segmentInitialized = true;
}
@@ -162,8 +165,8 @@ public class TrackStatisticsUpdater {
lastTrackPoint = trackPoint;
return;
}
long movingTime = trackPoint.getTime() - lastTrackPoint.getTime();
if (movingTime < 0) {
Duration movingTime = Duration.between(lastTrackPoint.getTime(), trackPoint.getTime());
if (movingTime.isNegative()) {
lastTrackPoint = trackPoint;
return;
}
@@ -252,9 +255,9 @@ public class TrackStatisticsUpdater {
}
// See if the speed seems physically likely. Ignore any speeds that imply acceleration greater than 2g.
long timeDifference = trackPoint.getTime() - lastTrackPoint.getTime();
Duration timeDifference = Duration.between(lastTrackPoint.getTime(), trackPoint.getTime());
double speedDifference = Math.abs(lastTrackPoint.getSpeed() - trackPoint.getSpeed());
if (speedDifference > MAX_ACCELERATION * timeDifference) {
if (speedDifference > MAX_ACCELERATION * timeDifference.toMillis()) {
return false;
}
@@ -262,7 +265,7 @@ public class TrackStatisticsUpdater {
if (speedBuffer_ms.isFull()) {
double average = speedBuffer_ms.getAverage();
double diff = Math.abs(average - trackPoint.getSpeed());
return (trackPoint.getSpeed() < average * 10) && (diff < MAX_ACCELERATION * timeDifference);
return (trackPoint.getSpeed() < average * 10) && (diff < MAX_ACCELERATION * timeDifference.toMillis());
}
return true;
@@ -4,13 +4,16 @@ import android.content.Context;
import androidx.annotation.Nullable;
import java.time.Duration;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
public class AnnouncementUtils {
private AnnouncementUtils() {}
private AnnouncementUtils() {
}
public static String getAnnouncement(Context context, TrackStatistics trackStatistics, String category, @Nullable IntervalStatistics.Interval currentInterval) {
boolean metricUnits = PreferencesUtils.isMetricUnits(context);
@@ -41,11 +44,11 @@ public class AnnouncementUtils {
} else {
double timePerDistance = distancePerTime == 0 ? 0.0 : 1 / distancePerTime;
int paceId = metricUnits ? R.string.voice_pace_per_kilometer : R.string.voice_pace_per_mile;
long time = Math.round(timePerDistance * UnitConversions.HR_TO_MIN * UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS);
Duration time = Duration.ofMillis((long) (timePerDistance * UnitConversions.HR_TO_MIN * UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS));
rate = context.getString(paceId, getAnnounceTime(context, time));
double currentTimePerDistance = currentDistancePerTime == 0 ? 0.0 : 1 / currentDistancePerTime;
long currentTime = Math.round(currentTimePerDistance * UnitConversions.HR_TO_MIN * UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS);
Duration currentTime = Duration.ofMillis((long) (currentTimePerDistance * UnitConversions.HR_TO_MIN * UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS));
currentRate = context.getString(paceId, getAnnounceTime(context, currentTime));
currentRateMsg = context.getString(R.string.voice_pace_lap, currentRate);
}
@@ -58,23 +61,25 @@ public class AnnouncementUtils {
return context.getString(R.string.voice_template, totalDistance, getAnnounceTime(context, trackStatistics.getMovingTime()), rate) + currentRateMsg;
}
private static String getAnnounceTime(Context context, long time) {
int[] parts = StringUtils.getTimeParts(time);
String seconds = context.getResources()
.getQuantityString(R.plurals.voiceSeconds, parts[0], parts[0]);
String minutes = context.getResources()
.getQuantityString(R.plurals.voiceMinutes, parts[1], parts[1]);
String hours = context.getResources()
.getQuantityString(R.plurals.voiceHours, parts[2], parts[2]);
StringBuilder sb = new StringBuilder();
if (parts[2] != 0) {
sb.append(hours);
sb.append(" ");
//TODO We might need to localize this using strings.xml if order is relevant.
private static String getAnnounceTime(Context context, Duration duration) {
String result = "";
int hours = (int) (duration.getSeconds() / (60 * 60));
int minutes = (int) (duration.getSeconds() / 60);
int seconds = (int) (duration.getSeconds() % 60);
if (hours != 0) {
String hoursText = context.getResources()
.getQuantityString(R.plurals.voiceHours, hours, hours);
result += hoursText + " ";
}
sb.append(minutes);
sb.append(" ");
sb.append(seconds);
return sb.toString();
String minutesText = context.getResources()
.getQuantityString(R.plurals.voiceMinutes, minutes, minutes);
String secondsText = context.getResources()
.getQuantityString(R.plurals.voiceSeconds, seconds, seconds);
return result + minutesText + " " + secondsText;
}
/**
@@ -17,6 +17,9 @@ package de.dennisguse.opentracks.util;
import android.location.Location;
import java.time.Duration;
import java.time.Instant;
import de.dennisguse.opentracks.content.data.TrackPoint;
/**
@@ -28,8 +31,7 @@ public class LocationUtils {
private static final String TAG = LocationUtils.class.getSimpleName();
// 1 minute in milliseconds
private static final long MAX_LOCATION_AGE_MS = (long) (UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS);
private static final Duration MAX_LOCATION_AGE = Duration.ofMinutes(1);
private LocationUtils() {
}
@@ -48,6 +50,8 @@ public class LocationUtils {
}
public static boolean isTrackPointOld(TrackPoint trackPoint) {
return System.currentTimeMillis() - trackPoint.getTime() > MAX_LOCATION_AGE_MS;
return Duration.between(trackPoint.getTime(), Instant.now())
.minus(MAX_LOCATION_AGE)
.isNegative();
}
}
@@ -22,8 +22,11 @@ import android.text.format.DateUtils;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.NonNull;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
@@ -50,41 +53,32 @@ public class StringUtils {
}
/**
* Formats the date and time_ms based on user's phone date/time_ms preferences.
*
* @param context the context
* @param time_ms the time_ms in milliseconds
* Formats the date and time based on user's phone date/time preferences.
*/
public static String formatDateTime(Context context, long time_ms) {
return DateUtils.formatDateTime(context, time_ms, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE)
+ " " + DateUtils.formatDateTime(context, time_ms, DateUtils.FORMAT_SHOW_TIME);
public static String formatDateTime(Context context, Instant time) {
return DateUtils.formatDateTime(context, time.toEpochMilli(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE)
+ " " + DateUtils.formatDateTime(context, time.toEpochMilli(), DateUtils.FORMAT_SHOW_TIME);
}
/**
* Formats the time using the ISO 8601 date time_ms format with fractional seconds in UTC time zone.
*
* @param time_ms the time in milliseconds
* Formats the time using the ISO 8601 date time format with fractional seconds in UTC time zone.
*/
public static String formatDateTimeIso8601(long time_ms) {
return Instant.ofEpochMilli(time_ms).toString();
public static String formatDateTimeIso8601(@NonNull Instant time) {
return time.toString();
}
/**
* Formats the elapsed timed in the form "MM:SS" or "H:MM:SS".
*
* @param time_ms the time in milliseconds
*/
public static String formatElapsedTime(long time_ms) {
return DateUtils.formatElapsedTime((long) (time_ms * UnitConversions.MS_TO_S));
public static String formatElapsedTime(@NonNull Duration time) {
return DateUtils.formatElapsedTime(time.getSeconds());
}
/**
* Formats the elapsed time in the form "H:MM:SS".
*
* @param time_ms the time in milliseconds
*/
public static String formatElapsedTimeWithHour(long time_ms) {
String value = formatElapsedTime(time_ms);
public static String formatElapsedTimeWithHour(@NonNull Duration time) {
String value = formatElapsedTime(time);
return TextUtils.split(value, ":").length == 2 ? "0:" + value : value;
}
@@ -289,45 +283,20 @@ public class StringUtils {
*
* @param xmlDateTime the XML date time string
*/
public static long parseTime(String xmlDateTime) {
public static Instant parseTime(String xmlDateTime) {
try {
TemporalAccessor t = DateTimeFormatter.ISO_DATE_TIME.parseBest(xmlDateTime, ZonedDateTime::from, LocalDateTime::from);
if (t instanceof LocalDateTime) {
Log.w(TAG, "Date does not contain timezone information: using UTC.");
t = ((LocalDateTime) t).atZone(ZoneOffset.UTC);
}
return Instant.from(t).toEpochMilli();
return Instant.from(t);
} catch (Exception e) {
Log.e(TAG, "Invalid XML dateTime value");
throw e;
}
}
/**
* Gets the time as an array of three integers.
* Index 0 contains the number of seconds, index 1 contains the number of minutes, and index 2 contains the number of hours.
*
* @param time the time in milliseconds
* @return an array of 3 elements.
*/
public static int[] getTimeParts(long time) {
if (time < 0) {
int[] parts = getTimeParts(time * -1);
parts[0] *= -1;
parts[1] *= -1;
parts[2] *= -1;
return parts;
}
int[] parts = new int[3];
long seconds = (long) (time * UnitConversions.MS_TO_S);
parts[0] = (int) (seconds % 60);
int minutes = (int) (seconds / 60);
parts[1] = minutes % 60;
parts[2] = minutes / 60;
return parts;
}
/**
* Gets the frequency display options.
*
@@ -21,6 +21,7 @@ import android.content.Context;
import androidx.annotation.VisibleForTesting;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Locale;
import de.dennisguse.opentracks.R;
@@ -40,20 +41,13 @@ public class TrackNameUtils {
private TrackNameUtils() {
}
/**
* Gets the track name.
*
* @param context the context
* @param trackId the track id
* @param startTime the track start time
*/
public static String getTrackName(Context context, Track.Id trackId, long startTime) {
public static String getTrackName(Context context, Track.Id trackId, Instant startTime) {
String trackName = PreferencesUtils.getString(context, R.string.track_name_key, context.getString(R.string.track_name_default));
if (trackName.equals(context.getString(R.string.settings_recording_track_name_date_local_value))) {
return StringUtils.formatDateTime(context, startTime);
} else if (trackName.equals(context.getString(R.string.settings_recording_track_name_date_iso_8601_value))) {
return new SimpleDateFormat(ISO_8601_FORMAT, Locale.US).format(startTime);
return new SimpleDateFormat(ISO_8601_FORMAT, Locale.US).format(startTime.toEpochMilli());
} else {
return context.getString(R.string.track_name_format, trackId.getId());
}
@@ -18,22 +18,6 @@ public class TrackPointUtils {
return trackPoint.hasSpeed() && trackPoint.getSpeed() >= MAX_NO_MOVEMENT_SPEED;
}
public static boolean equalTime(TrackPoint t1, TrackPoint t2) {
if (t1 == null || t2 == null) {
return false;
}
return t1.getTime() == t2.getTime();
}
public static boolean after(TrackPoint t1, TrackPoint t2) {
if (t1 == null || t2 == null) {
return false;
}
return t1.getTime() > t2.getTime();
}
/**
* Is accuracy better than threshold?
*/
@@ -2,6 +2,7 @@ package de.dennisguse.opentracks.viewmodels;
import androidx.annotation.NonNull;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
@@ -33,7 +34,7 @@ public class IntervalStatistics {
if (trackPoint.hasLocation() && prevTrackPoint.hasLocation()) {
interval.distance_m += prevTrackPoint.distanceTo(trackPoint);
interval.time_ms += trackPoint.getTime() - prevTrackPoint.getTime();
interval.time = interval.time.plus(Duration.between(prevTrackPoint.getTime(), trackPoint.getTime()));
interval.gain_m += trackPoint.hasElevationGain() ? trackPoint.getElevationGain() : 0;
interval.loss_m += trackPoint.hasElevationLoss() ? trackPoint.getElevationLoss() : 0;
@@ -44,7 +45,7 @@ public class IntervalStatistics {
intervalList.add(adjustedInterval);
interval = new Interval(interval.distance_m - adjustedInterval.distance_m, interval.time_ms - adjustedInterval.time_ms);
interval = new Interval(interval.distance_m - adjustedInterval.distance_m, interval.time.minus(adjustedInterval.time));
}
}
}
@@ -81,22 +82,21 @@ public class IntervalStatistics {
public static class Interval {
private float distance_m = 0f;
private float time_ms = 0f;
private Duration time = Duration.ofSeconds(0);
private float gain_m = 0f;
private float loss_m = 0f;
public Interval() {}
public Interval() {
}
public Interval(float distance_m, float time_ms) {
public Interval(float distance_m, Duration time) {
this.distance_m = distance_m;
this.time_ms = time_ms;
this.gain_m = 0f;
this.loss_m = 0f;
this.time = time;
}
public Interval(Interval i) {
distance_m = i.distance_m;
time_ms = i.time_ms;
time = i.time;
gain_m = i.gain_m;
loss_m = i.loss_m;
}
@@ -107,7 +107,7 @@ public class IntervalStatistics {
public void adjust(float adjustFactor) {
distance_m *= adjustFactor;
time_ms *= adjustFactor;
time = Duration.ofMillis((long) (time.toMillis() * adjustFactor));
}
/**
@@ -117,7 +117,7 @@ public class IntervalStatistics {
if (distance_m == 0f) {
return 0f;
}
return distance_m / (float) (time_ms * UnitConversions.MS_TO_S);
return (distance_m / (time.toMillis() * (float) UnitConversions.MS_TO_S));
}
public float getGain_m() {