Compare commits

...

28 Commits

Author SHA1 Message Date
Dennis Guse 1672a5c722 Release: v3.17.3 2021-04-27 21:03:44 +02:00
Román Martínez bf552a9219 Bugfix: crash deleting when activity restarts. Fixes #705. 2021-04-27 19:54:02 +02:00
Dennis Guse 348f697b56 TrackRecordingActivity needs to update TrackDataHub onResume().
Part of #719.
2021-04-27 19:02:53 +02:00
Dennis Guse 887d4700d4 TrackDataHub computes TrackStatistics and passes it to listeners.
Part of #719.
2021-04-27 19:02:53 +02:00
Dennis Guse 391dfd30e2 TrackDataHub does not need to register for status changes of TrackRecordingService.
This is handled by TrackRecordingActivity alone.
Part of #719.
2021-04-27 19:02:53 +02:00
Dennis Guse b416857001 TrackRecordingService log warnings if unexpected methods are invoked. 2021-04-27 19:02:53 +02:00
Dennis Guse df3720ef0d Bugfix: TrackDataManager.unregisterTrackDataListener was not removing listener. 2021-04-27 19:02:53 +02:00
Román Martínez b784a47af6 Bugfix: track stats always show pace. Fixes #726. 2021-04-25 15:21:08 +02:00
Dennis Guse 0f3156e71b i18n: French error message has no zero. 2021-04-22 18:36:45 +02:00
Dennis Guse 0035c85922 AboutActivity: back via top left arrow is working now.
Fixes #727.
2021-04-22 18:23:18 +02:00
Román Martínez 976a50ca15 Bugfixed spanish translation. Fixes #725. 2021-04-22 09:52:46 +02:00
mondstern c0ad53eef1 Translated using Weblate (Portuguese)
Currently translated at 78.6% (416 of 529 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/pt/
2021-04-21 19:57:41 +02:00
Quang Trung 1192b02c0a Translated using Weblate (Vietnamese)
Currently translated at 78.6% (416 of 529 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/vi/
2021-04-21 19:57:41 +02:00
mondstern 7a3abe9962 Translated using Weblate (Hungarian)
Currently translated at 100.0% (529 of 529 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/hu/
2021-04-21 19:57:41 +02:00
mondstern 60b989c13a Translated using Weblate (French)
Currently translated at 100.0% (529 of 529 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/fr/
2021-04-21 19:57:41 +02:00
mondstern 340f16dc99 Translated using Weblate (French)
Currently translated at 91.1% (482 of 529 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/fr/
2021-04-21 19:57:41 +02:00
Max Schallert 7cf29f7cea Translated using Weblate (German)
Currently translated at 95.4% (505 of 529 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/de/
2021-04-21 19:57:41 +02:00
dial25sd e39352cc9e Translated using Weblate (German)
Currently translated at 95.4% (505 of 529 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/de/
2021-04-21 19:57:41 +02:00
Dennis Guse e2964e805e Bugfix: NPE in voice announcements using pace.
Fixes #724.
2021-04-21 19:57:29 +02:00
Dennis Guse 8cab43d558 Release: v3.17.2 2021-04-19 19:38:31 +02:00
Dennis Guse 13f2a09652 Bugfix: AnnouncementPeriodicTask does not need to register as TrackRecordingService.Listener. 2021-04-19 19:29:51 +02:00
Dennis Guse 7d4072c0d8 Cleanup: ChartPoint uses null instead of NaN. 2021-04-18 17:50:16 +02:00
Dennis Guse 310b35713d ChartFragmentTest: added test for small changes. 2021-04-18 14:39:48 +02:00
Dennis Guse 7e6237de3a Release: v3.17.1 2021-04-17 22:40:05 +02:00
Román Martínez 772dad4837 announce called after getting recordingTrackId. Fixed a potencial race condition. 2021-04-17 22:26:23 +02:00
Román Martínez 99126fa357 Distance interval bugfixed: interval options are km/mi values. 2021-04-17 21:16:32 +02:00
Román Martínez b2c91d7592 registerListener for barometric sensor uses SENSOR_DELAY_FASTEST instead of nanoseconds. Fixes #714. 2021-04-17 19:34:05 +02:00
Rodrigo Oliveira 3c5a1be16b Fix #711: declare TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE on queries element
This enables the use of TextToSpeech engine on Android 11.
Ref.: https://developer.android.com/reference/android/speech/tts/TextToSpeech
2021-04-17 19:31:26 +02:00
39 changed files with 709 additions and 485 deletions
+2 -2
View File
@@ -51,8 +51,8 @@ android {
defaultConfig {
applicationId "de.dennisguse.opentracks"
versionCode 3981
versionName "v3.17.0"
versionCode 4009
versionName "v3.17.3"
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
@@ -0,0 +1,7 @@
v3.17.1: OpenTracks
Bugfix:
- Interval length in meters instead of kilometers (introduced in v3.17.0)
- Elevation gain/loss not recorded (introduced in v3.17.0)
- Announcements not working on Android 11
@@ -0,0 +1,8 @@
v3.17.2: OpenTracks
WARNING: The v3.17.0+ releases contain a memory leak.
This issues is NOT FIXED by this release.
It is recommended to not upgrade if you still use a release before v3.17.0
Bugfix:
- Crash while using voice announcements
@@ -0,0 +1,11 @@
v3.17.3: OpenTracks
WARNING: The v3.17.0+ releases contain a memory leak.
This issues is NOT FIXED by this release.
It is recommended to not upgrade if you still use a release before v3.17.0
Bugfix:
- Crash if using pace for voice announcements (introduced in v3.17.0)
- Statistics showed pace initially (introduced in v3.17.0)
- Rare crash when deleting tracks
@@ -0,0 +1,94 @@
package de.dennisguse.opentracks.chart;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.Duration;
import de.dennisguse.opentracks.content.data.Distance;
import de.dennisguse.opentracks.content.data.Speed;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.fragments.TrackStubUtils;
import de.dennisguse.opentracks.stats.TrackStatistics;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@RunWith(AndroidJUnit4.class)
public class ChartPointTest {
@Test
public void create_by_time() {
// given
TrackStatistics statistics = new TrackStatistics();
statistics.setTotalTime(Duration.ofSeconds(1000));
// when
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), 0, false, false);
// then
assertEquals(1000000, (long) point.getTimeOrDistance());
}
@Test
public void create_by_distance() {
// given
TrackStatistics statistics = new TrackStatistics();
statistics.setTotalDistance(Distance.of(1000));
// when
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), 0, true, true);
// then
assertEquals(1, (long) point.getTimeOrDistance());
}
@Test
public void create_get_altitude_speed_and_pace() {
// given
TrackStatistics statistics = new TrackStatistics();
// when
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), 50, false, true);
// then
assertEquals(0.05, point.getAltitude(), 0.01);
assertEquals(36, point.getSpeed(), 0.01);
assertEquals(1.66, point.getPace(), 0.01);
}
@Test
public void create_sensorNotAvailable() {
// given
TrackStatistics statistics = new TrackStatistics();
// when
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), 50, false, true);
// then
assertNull(point.getHeartRate());
assertNull(point.getCadence());
assertNull(point.getPower());
}
@Test
public void create_sensorAvailable() {
// given
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
trackPoint.setHeartRate_bpm(100f);
trackPoint.setCyclingCadence_rpm(101f);
trackPoint.setPower(102f);
TrackStatistics statistics = new TrackStatistics();
// when
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), 50, false, true);
// then
assertEquals(100.0, point.getHeartRate(), 0.01);
assertEquals(101.0, point.getCadence(), 0.01);
assertEquals(102.0, point.getPower(), 0.01);
}
}
@@ -44,7 +44,7 @@ public class ChartValueSeriesTest {
ApplicationProvider.getApplicationContext(),
Integer.MIN_VALUE,
Integer.MAX_VALUE,
new int[]{100, 1000},
new int[]{1, 100, 1000},
R.string.description_altitude_metric,
R.string.description_altitude_imperial,
R.color.chart_altitude_fill,
@@ -52,7 +52,7 @@ public class ChartValueSeriesTest {
15,
18) {
@Override
double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
return chartPoint.getAltitude();
}
@@ -77,6 +77,17 @@ public class ChartValueSeriesTest {
assertFalse(series.isEnabled());
}
@Test
public void testVerySmallUpdates() {
series.update(new ChartPoint(1f));
series.update(new ChartPoint(2f));
series.update(new ChartPoint(3f));
series.updateDimension();
assertEquals(1, series.getInterval());
assertEquals(1, series.getMinMarkerValue());
assertEquals(6, series.getMaxMarkerValue());
}
@Test
public void testSmallUpdates() {
series.update(new ChartPoint(0));
@@ -16,7 +16,6 @@
package de.dennisguse.opentracks.fragments;
import android.location.Location;
import android.os.Looper;
import androidx.test.core.app.ApplicationProvider;
@@ -28,23 +27,15 @@ 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.Distance;
import de.dennisguse.opentracks.content.data.Speed;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.util.UnitConversions;
import static org.junit.Assert.assertEquals;
/**
* Tests {@link ChartFragment}.
*
* @author Youtao Liu
*/
//TODO Add tests that check ChartFragment
@RunWith(AndroidJUnit4.class)
public class ChartFragmentTest {
@@ -71,261 +62,8 @@ public class ChartFragmentTest {
chartFragment.setRecordingDistanceInterval(Distance.of(50));
}
/**
* Tests the logic to get the incorrect values of sensor in {@link ChartFragment#createPendingPoint(TrackPoint)}.
*/
@Test
public void testCreatePendingPoint_sensorIncorrect() {
// given
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
// when
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
// then
assertEquals(Float.NaN, point.getHeartRate(), 0.01);
assertEquals(Float.NaN, point.getCadence(), 0.01);
assertEquals(Float.NaN, point.getPower(), 0.01);
}
/**
* Tests the logic to get the correct values of sensor in {@link ChartFragment#createPendingPoint(TrackPoint)}.
*/
@Test
public void testCreatePendingPoint_sensorCorrect() {
// given
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
trackPoint.setHeartRate_bpm(100f);
trackPoint.setCyclingCadence_rpm(101f);
trackPoint.setPower(102f);
// when
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
// then
assertEquals(100.0, point.getHeartRate(), 0.01);
assertEquals(101.0, point.getCadence(), 0.01);
assertEquals(102.0, point.getPower(), 0.01);
}
/**
* Tests the logic to get the value of metric Distance in {@link ChartFragment#createPendingPoint(TrackPoint)}.
*/
@Test
public void testCreatePendingPoint_distanceMetric() {
chartFragment.setChartByDistance(true);
// Resets last location and writes first location.
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
// The second is a same location, just different time.
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
point = chartFragment.createPendingPoint(trackPoint2);
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
// The third location is a new location, and use metric.
TrackPoint trackPoint3 = TrackStubUtils.createDefaultTrackPoint();
trackPoint3.setLatitude(23);
point = chartFragment.createPendingPoint(trackPoint3);
// Computes the distance between Latitude 22 and 23.
float[] results = new float[4];
Location.distanceBetween(trackPoint2.getLatitude(), trackPoint2.getLongitude(),
trackPoint3.getLatitude(), trackPoint3.getLongitude(), results);
double distance1 = results[0] * UnitConversions.M_TO_KM;
assertEquals(distance1, point.getTimeOrDistance(), 0.01);
// The fourth location is a new location, and use metric.
TrackPoint trackPoint4 = TrackStubUtils.createDefaultTrackPoint();
trackPoint4.setLatitude(24);
point = chartFragment.createPendingPoint(trackPoint4);
// Computes the distance between Latitude 23 and 24.
Location.distanceBetween(trackPoint3.getLatitude(), trackPoint3.getLongitude(),
trackPoint4.getLatitude(), trackPoint4.getLongitude(), results);
double distance2 = results[0] * UnitConversions.M_TO_KM;
assertEquals((distance1 + distance2), point.getTimeOrDistance(), 0.01);
}
/**
* Tests the logic to get the value of imperial Distance in {@link ChartFragment#createPendingPoint(TrackPoint)}.
*/
@Test
public void testCreatePendingPoint_distanceImperial() {
// By distance.
chartFragment.setChartByDistance(true);
// Setups to use imperial.
chartFragment.setMetricUnits(false);
// The first is a same location, just different time.
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
// The second location is a new location, and use imperial.
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
trackPoint2.setLatitude(23);
point = chartFragment.createPendingPoint(trackPoint2);
/*
* Computes the distance between Latitude 22 and 23.
* And for we set using * imperial, the distance should be multiplied by UnitConversions.KM_TO_MI.
*/
float[] results = new float[4];
Location.distanceBetween(trackPoint1.getLatitude(), trackPoint1.getLongitude(), trackPoint2.getLatitude(), trackPoint2.getLongitude(), results);
double distance1 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
assertEquals(distance1, point.getTimeOrDistance(), 0.01);
// The third location is a new location, and use imperial.
TrackPoint trackPoint3 = TrackStubUtils.createDefaultTrackPoint();
trackPoint3.setLatitude(24);
point = chartFragment.createPendingPoint(trackPoint3);
/*
* Computes the distance between Latitude 23 and 24.
* And for we set using * imperial, the distance should be multiplied by UnitConversions.KM_TO_MI.
*/
Location.distanceBetween(trackPoint2.getLatitude(), trackPoint2.getLongitude(), trackPoint3.getLatitude(), trackPoint3.getLongitude(), results);
double distance2 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
assertEquals(distance1 + distance2, point.getTimeOrDistance(), 0.01);
}
/**
* Tests the logic to get the values of time in {@link ChartFragment#createPendingPoint(TrackPoint)}.
*/
@Test
public void testCreatePendingPoint_time() {
// given
chartFragment.setChartByDistance(false);
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
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);
Duration timeSpan = Duration.ofMillis(222);
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
trackPoint2.setTime(Instant.ofEpochMilli(TrackStubUtils.INITIAL_TIME).plus(timeSpan));
point = chartFragment.createPendingPoint(trackPoint2);
assertEquals(timeSpan, Duration.ofMillis((long) point.getTimeOrDistance()));
}
/**
* Tests the logic to get the value of altitude in {@link ChartFragment#createPendingPoint(TrackPoint)} by one and two points.
*/
@Test
public void testCreatePendingPoint_altitude() {
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
/*
* At first, clear old points of altitude, so give true to the second parameter.
* Then only one value INITIAL_ALTITUDE in buffer.
*/
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point.getAltitude(), 0.01);
/*
* Send another value to buffer, now there are two values, INITIAL_ALTITUDE and INITIAL_ALTITUDE * 2.
*/
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
trackPoint2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
point = chartFragment.createPendingPoint(trackPoint2);
assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, point.getAltitude(), 0.01);
}
/**
* Tests the logic to get the value of speed in {@link ChartFragment#createPendingPoint(TrackPoint)}.
* In this test, firstly remove all points in memory, and then fill in two points one by one.
* The speed values of these points are 129, 130.
*/
@Test
public void testCreatePendingPoint_speed() {
/*
* At first, clear old points of speed, so give true to the second parameter.
* It will not be filled in to the speed buffer.
*/
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setSpeed(Speed.of(128.5f));
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
assertEquals(0.0, point.getSpeed(), 0.01);
/*
* Tests the logic when both metricUnits and reportSpeed are true.
* This location will be filled into speed buffer.
*/
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
/*
* 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().plusMillis(222));
trackPoint2.setSpeed(Speed.of(130f));
point = chartFragment.createPendingPoint(trackPoint2);
assertEquals(130.0 * UnitConversions.MPS_TO_KMH, point.getSpeed(), 0.01);
}
/**
* Tests the logic to compute speed when use Imperial.
*/
@Test
public void testCreatePendingPoint_speedImperial() {
chartFragment.setMetricUnits(false);
// First data point is not added to the speed buffer
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setSpeed(Speed.of(100.0f));
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
assertEquals(0.0, point.getSpeed(), 0.01);
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
/*
* 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().plusMillis(222));
trackPoint2.setSpeed(Speed.of(102f));
point = chartFragment.createPendingPoint(trackPoint2);
assertEquals(102.0 * UnitConversions.MPS_TO_KMH * UnitConversions.KM_TO_MI, point.getSpeed(), 0.01);
}
/**
* Tests the logic to get pace value when reportSpeed is false.
*/
@Test
public void testCreatePendingPoint_pace_nonZeroSpeed() {
chartFragment.setReportSpeed(false);
// First data point is not added to the speed buffer
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setSpeed(Speed.of(100.0f));
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
assertEquals(0.0, point.getSpeed(), 0.01);
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
/*
* 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().plusMillis(222));
trackPoint2.setSpeed(Speed.of(102f));
point = chartFragment.createPendingPoint(trackPoint2);
assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MPS_TO_KMH), point.getPace(), 0.01);
}
/**
* Tests the logic to get pace value when reportSpeed is false and average speed is zero.
*/
@Test
public void testCreatePendingPoint_pace_zeroSpeed() {
chartFragment.setReportSpeed(false);
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
trackPoint.setSpeed(Speed.of(0f));
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
assertEquals(0.0, point.getPace(), 0.01);
public void nothing() {
//TODO
}
}
@@ -27,7 +27,7 @@ public class AnnouncementUtilsTest {
private final Context context = ApplicationProvider.getApplicationContext();
@Test
public void getAnnouncement_metric() {
public void getAnnouncement_metric_speed() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(20000));
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
@@ -43,7 +43,7 @@ public class AnnouncementUtilsTest {
}
@Test
public void getAnnouncement_withInterval_metric() {
public void getAnnouncement_withInterval_metric_speed() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(20000));
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
@@ -61,4 +61,112 @@ public class AnnouncementUtilsTest {
// then
assertEquals("OpenTracks total distance 20.00 kilometers in 1 hour 5 minutes 10 seconds at 18.4 kilometers per hour Lap speed of 51.2 kilometers per hour", announcement);
}
@Test
public void getAnnouncement_metric_pace() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(20000));
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
stats.setMaxSpeed(Speed.of(100));
stats.setTotalAltitudeGain(6000f);
// when
String announcement = AnnouncementUtils.getAnnouncement(context, stats, true, false, null);
// then
assertEquals("OpenTracks total distance 20.00 kilometers in 1 hour 5 minutes 10 seconds at 3 minutes 15 seconds per kilometer", announcement);
}
@Test
public void getAnnouncement_withInterval_metric_pace() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(20000));
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
stats.setMaxSpeed(Speed.of(100));
stats.setTotalAltitudeGain(6000f);
List<TrackPoint> trackPoints = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 10).second;
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, Distance.of(1000));
IntervalStatistics.Interval lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
// when
String announcement = AnnouncementUtils.getAnnouncement(context, stats, true, false, lastInterval);
// then
assertEquals("OpenTracks total distance 20.00 kilometers in 1 hour 5 minutes 10 seconds at 3 minutes 15 seconds per kilometer Lap time of 1 minute 10 seconds per kilometer", announcement);
}
@Test
public void getAnnouncement_imperial_speed() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(20000));
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
stats.setMaxSpeed(Speed.of(100));
stats.setTotalAltitudeGain(6000f);
// when
String announcement = AnnouncementUtils.getAnnouncement(context, stats, false, true, null);
// then
assertEquals("OpenTracks total distance 12.43 miles in 1 hour 5 minutes 10 seconds at 11.4 miles per hour", announcement);
}
@Test
public void getAnnouncement_withInterval_imperial_speed() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(20000));
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
stats.setMaxSpeed(Speed.of(100));
stats.setTotalAltitudeGain(6000f);
List<TrackPoint> trackPoints = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 10).second;
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, Distance.of(1000));
IntervalStatistics.Interval lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
// when
String announcement = AnnouncementUtils.getAnnouncement(context, stats, false, true, lastInterval);
// then
assertEquals("OpenTracks total distance 12.43 miles in 1 hour 5 minutes 10 seconds at 11.4 miles per hour Lap speed of 31.8 miles per hour", announcement);
}
@Test
public void getAnnouncement_imperial_pace() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(20000));
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
stats.setMaxSpeed(Speed.of(100));
stats.setTotalAltitudeGain(6000f);
// when
String announcement = AnnouncementUtils.getAnnouncement(context, stats, false, false, null);
// then
assertEquals("OpenTracks total distance 12.43 miles in 1 hour 5 minutes 10 seconds at 5 minutes 15 seconds per mile", announcement);
}
@Test
public void getAnnouncement_withInterval_imperial_pace() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(20000));
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
stats.setMaxSpeed(Speed.of(100));
stats.setTotalAltitudeGain(6000f);
List<TrackPoint> trackPoints = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 10).second;
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, Distance.of(1000));
IntervalStatistics.Interval lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
// when
String announcement = AnnouncementUtils.getAnnouncement(context, stats, false, false, lastInterval);
// then
assertEquals("OpenTracks total distance 12.43 miles in 1 hour 5 minutes 10 seconds at 5 minutes 15 seconds per mile Lap time of 1 minute 53 seconds per mile", announcement);
}
}
+6
View File
@@ -351,4 +351,10 @@ limitations under the License.
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" />
</application>
<queries>
<intent>
<action android:name="android.intent.action.TTS_SERVICE" />
</intent>
</queries>
</manifest>
@@ -1,7 +1,6 @@
package de.dennisguse.opentracks;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import androidx.annotation.Nullable;
@@ -32,9 +31,4 @@ public class AboutActivity extends AbstractActivity {
viewBinding = AboutBinding.inflate(getLayoutInflater());
return viewBinding.getRoot();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return false;
}
}
@@ -29,7 +29,7 @@ import de.dennisguse.opentracks.util.IntentUtils;
/**
* An abstract class for the following common tasks across
* {@link TrackListActivity}, {@link TrackRecordedActivity}, and {@link TrackRecordedActivity}:
* {@link TrackListActivity} and {@link TrackRecordedActivity}:
* <p>
* - share track <br>
* - delete tracks <br>
@@ -64,26 +64,29 @@ public class TrackDeleteActivity extends AbstractActivity {
runOnUiThread(TrackDeleteActivity.this::onAsyncTaskCompleted);
});
}
@Override
protected void onStart() {
super.onStart();
deleteThread.start();
}
@Override
protected void onStop() {
super.onStop();
deleteThread.interrupt();
}
@Override
protected void onDestroy() {
super.onDestroy();
viewBinding = null;
}
@Override
protected void onStart() {
super.onStart();
if (deleteThread.getState() == Thread.State.TERMINATED) {
onAsyncTaskCompleted();
}
}
@Override
protected void onStop() {
super.onStop();
deleteThread.interrupt();
}
@Override
protected View getRootView() {
viewBinding = TrackDeleteBinding.inflate(getLayoutInflater());
@@ -53,6 +53,7 @@ import de.dennisguse.opentracks.util.IntentUtils;
* @author Leif Hendrik Wilden
* @author Rodrigo Damazio
*/
//TODO Should not use TrackRecordingServiceConnection; only used to determine if there is NO current recording, to enable resume functionality.
public class TrackRecordedActivity extends AbstractListActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller, TrackActivityDataHubInterface, TrackRecordingServiceStatus.Listener {
private static final String TAG = TrackRecordedActivity.class.getSimpleName();
@@ -299,7 +300,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
}
}
public void startPostponedEnterTransitionWith(View viewIcon, View viewName) {
public void startPostponedEnterTransitionWith(View viewIcon) {
ViewCompat.setTransitionName(viewIcon, TrackRecordedActivity.VIEW_TRACK_ICON);
startPostponedEnterTransition();
}
@@ -87,6 +87,8 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
// A recording track is on.
trackDataHub.loadTrack(trackId);
trackDataHub.setRecordingTrackId(trackId);
trackController.update(true, false);
trackController.onResume(true, recordingTrackPaused);
}
@@ -209,6 +211,8 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
if (trackId != null) {
trackDataHub.loadTrack(trackId);
trackDataHub.setRecordingTrackId(trackId);
trackDataHub.setRecordingTrackPaused(recordingTrackPaused);
trackController.onResume(true, recordingTrackPaused);
}
@@ -404,17 +408,20 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
public void onTrackRecordingPaused(boolean isPaused) {
if (recordingTrackPaused != isPaused) {
trackController.update(true, isPaused);
trackDataHub.setRecordingTrackPaused(isPaused);
}
recordingTrackPaused = isPaused;
setLockscreenPolicy();
setScreenOnPolicy();
}
//TODO Why should this be interesting? This activity is only used for currently recording tracks.
@Override
public void onTrackRecordingId(Track.Id newTrackId) {
if (newTrackId != null && !newTrackId.equals(trackId)) {
trackId = newTrackId;
trackController.update(true, recordingTrackPaused);
trackDataHub.setRecordingTrackId(newTrackId);
}
}
}
@@ -3,9 +3,10 @@ package de.dennisguse.opentracks.chart;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import de.dennisguse.opentracks.content.data.Distance;
import de.dennisguse.opentracks.content.data.Speed;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
import de.dennisguse.opentracks.util.UnitConversions;
public class ChartPoint {
@@ -13,44 +14,38 @@ public class ChartPoint {
private double timeOrDistance;
//Y-axis
private double altitude;
private double speed;
private double pace;
private double heartRate = Double.NaN;
private double cadence = Double.NaN;
private double power = Double.NaN;
private final double altitude;
private Double speed;
private Double pace;
private Double heartRate;
private Double cadence;
private Double power;
@Deprecated
@VisibleForTesting
ChartPoint(double altitude) {
this.altitude = altitude;
}
public ChartPoint(@NonNull TrackStatisticsUpdater trackStatisticsUpdater, TrackPoint trackPoint, boolean chartByDistance, boolean metricUnits) {
TrackStatistics trackStatistics = trackStatisticsUpdater.getTrackStatistics();
public ChartPoint(@NonNull TrackStatistics trackStatistics, @NonNull TrackPoint trackPoint, Speed smoothedSpeed, double smoothedAltitude_m, boolean chartByDistance, boolean metricUnits) {
if (chartByDistance) {
timeOrDistance = trackStatistics.getTotalDistance().to(metricUnits);
} else {
timeOrDistance = trackStatistics.getTotalTime().toMillis();
}
altitude = trackStatisticsUpdater.getSmoothedAltitude();
if (!metricUnits) {
altitude *= UnitConversions.M_TO_FT;
}
altitude = Distance.of(smoothedAltitude_m).to(metricUnits);
speed = trackStatisticsUpdater.getSmoothedSpeed().to(metricUnits);
pace = trackStatisticsUpdater.getSmoothedSpeed().toPace(metricUnits).toMillis() * UnitConversions.MS_TO_S * UnitConversions.S_TO_MIN;
if (trackPoint != null) {
if (trackPoint.hasHeartRate()) {
heartRate = trackPoint.getHeartRate_bpm();
}
if (trackPoint.hasCyclingCadence()) {
cadence = trackPoint.getCyclingCadence_rpm();
}
if (trackPoint.hasPower()) {
power = trackPoint.getPower();
}
speed = smoothedSpeed.to(metricUnits);
pace = smoothedSpeed.toPace(metricUnits).toMillis() * UnitConversions.MS_TO_S * UnitConversions.S_TO_MIN;
if (trackPoint.hasHeartRate()) {
heartRate = (double) trackPoint.getHeartRate_bpm();
}
if (trackPoint.hasCyclingCadence()) {
cadence = (double) trackPoint.getCyclingCadence_rpm();
}
if (trackPoint.hasPower()) {
power = (double) trackPoint.getPower();
}
}
@@ -58,39 +53,27 @@ public class ChartPoint {
return timeOrDistance;
}
public double getAltitude() {
public Double getAltitude() {
return altitude;
}
public double getSpeed() {
public Double getSpeed() {
return speed;
}
public double getPace() {
public Double getPace() {
return pace;
}
public boolean hasHeartRate() {
return Double.isNaN(heartRate);
}
public double getHeartRate() {
public Double getHeartRate() {
return heartRate;
}
public boolean hasCadence() {
return Double.isNaN(cadence);
}
public double getCadence() {
public Double getCadence() {
return cadence;
}
public boolean hasPower() {
return Double.isNaN(power);
}
public double getPower() {
public Double getPower() {
return power;
}
}
@@ -132,10 +132,10 @@ abstract class ChartValueSeries {
}
}
abstract double extractDataFromChartPoint(@NonNull ChartPoint chartPoint);
abstract Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint);
boolean isChartPointValid(@NonNull ChartPoint chartPoint) {
return !Double.isNaN(extractDataFromChartPoint(chartPoint));
return extractDataFromChartPoint(chartPoint) != null;
}
protected abstract boolean drawIfChartPointHasNoData();
@@ -206,7 +206,7 @@ public class ChartView extends View {
fontSizeSmall,
fontSizeMedium) {
@Override
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
return chartPoint.getAltitude();
}
@@ -227,7 +227,7 @@ public class ChartView extends View {
fontSizeSmall,
fontSizeMedium) {
@Override
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
return chartPoint.getSpeed();
}
@@ -249,7 +249,7 @@ public class ChartView extends View {
fontSizeSmall,
fontSizeMedium) {
@Override
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
return chartPoint.getPace();
}
@@ -271,7 +271,7 @@ public class ChartView extends View {
fontSizeSmall,
fontSizeMedium) {
@Override
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
return chartPoint.getHeartRate();
}
@@ -292,7 +292,7 @@ public class ChartView extends View {
fontSizeSmall,
fontSizeMedium) {
@Override
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
return chartPoint.getCadence();
}
@@ -312,7 +312,7 @@ public class ChartView extends View {
fontSizeSmall,
fontSizeMedium) {
@Override
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
return chartPoint.getPower();
}
@@ -30,6 +30,7 @@ import androidx.annotation.VisibleForTesting;
import java.util.Collections;
import java.util.Set;
import de.dennisguse.opentracks.content.data.Distance;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.MarkerColumns;
import de.dennisguse.opentracks.content.data.Track;
@@ -38,9 +39,7 @@ import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
import de.dennisguse.opentracks.services.TrackRecordingService;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.services.TrackRecordingServiceStatus;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
/**
* Track data hub.
@@ -50,7 +49,8 @@ import de.dennisguse.opentracks.services.TrackRecordingServiceStatus;
*
* @author Rodrigo Damazio
*/
public class TrackDataHub implements TrackRecordingServiceStatus.Listener {
//TODO register contentobserver only for exact URL (incl. trackId) to not filter here.
public class TrackDataHub {
/**
* Target number of track points displayed by the diagrams (recommended).
@@ -75,11 +75,12 @@ public class TrackDataHub implements TrackRecordingServiceStatus.Listener {
private HandlerThread handlerThread;
private Handler handler;
private TrackRecordingServiceConnection trackRecordingServiceConnection;
// Preference values
private Track.Id selectedTrackId;
@Deprecated
//TODO Is there a better way to handle this? e.g., passing information rather from Activity than from Activity to TrackDataHub
private Track.Id recordingTrackId;
@Deprecated
//TODO Is there a better way to handle this? e.g., passing information rather from Activity than from Activity to TrackDataHub
private boolean recordingTrackPaused;
// Track points sampling state
@@ -92,13 +93,6 @@ public class TrackDataHub implements TrackRecordingServiceStatus.Listener {
private ContentObserver markersTableObserver;
private ContentObserver trackPointsTableObserver;
private final Runnable bindCallback = () -> {
TrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
if (service != null) {
service.addListener(TrackDataHub.this);
}
};
public TrackDataHub(Context context) {
this(context, new TrackDataManager(), new ContentProviderUtils(context), TARGET_DISPLAYED_TRACKPOINTS);
}
@@ -148,9 +142,6 @@ public class TrackDataHub implements TrackRecordingServiceStatus.Listener {
};
contentResolver.registerContentObserver(TrackPointsColumns.CONTENT_URI_BY_ID, false, trackPointsTableObserver);
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindCallback);
trackRecordingServiceConnection.bind(context);
handler.post(() -> {
if (started) {
loadDataForAll();
@@ -177,8 +168,6 @@ public class TrackDataHub implements TrackRecordingServiceStatus.Listener {
handlerThread = null;
}
handler = null;
trackRecordingServiceConnection.unbind(context);
}
public void loadTrack(final @NonNull Track.Id trackId) {
@@ -362,6 +351,8 @@ public class TrackDataHub implements TrackRecordingServiceStatus.Listener {
next = new TrackPoint.Id(localLastSeenTrackPointIdId.getId() + 1); //TODO startTrackPointId + 1 is an assumption assumption; should be derived from the DB.
}
TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
TrackPoint trackPoint = null;
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(selectedTrackId, next)) {
@@ -383,15 +374,16 @@ public class TrackDataHub implements TrackRecordingServiceStatus.Listener {
samplingFrequency = 1 + (int) (numTotalPoints / targetNumPoints);
}
trackStatisticsUpdater.addTrackPoint(trackPoint, Distance.of(50)); //TODO
// Also include the last point if the selected track is not recording.
if ((localNumLoadedTrackPoints % samplingFrequency == 0) || (trackPointId == lastTrackPointId && !isSelectedTrackRecording())) {
for (TrackDataListener trackDataListener : sampledInListeners) {
trackDataListener.onSampledInTrackPoint(trackPoint);
trackDataListener.onSampledInTrackPoint(trackPoint, trackStatisticsUpdater.getTrackStatistics(), trackStatisticsUpdater.getSmoothedSpeed(), trackStatisticsUpdater.getSmoothedAltitude());
}
} else {
for (TrackDataListener trackDataListener : sampledOutListeners) {
trackDataListener.onSampledOutTrackPoint(trackPoint);
trackDataListener.onSampledOutTrackPoint(trackPoint, trackStatisticsUpdater.getTrackStatistics());
}
}
@@ -411,7 +403,7 @@ public class TrackDataHub implements TrackRecordingServiceStatus.Listener {
if (trackPoint != null) {
for (TrackDataListener listener : sampledInListeners) {
listener.onNewTrackPointsDone(trackPoint);
listener.onNewTrackPointsDone(trackPoint, trackStatisticsUpdater.getTrackStatistics());
}
}
}
@@ -425,13 +417,11 @@ public class TrackDataHub implements TrackRecordingServiceStatus.Listener {
lastSeenTrackPointId = null;
}
@Override
public void onTrackRecordingPaused(boolean isPaused) {
recordingTrackPaused = isPaused;
public void setRecordingTrackId(Track.Id recordingTrackId) {
this.recordingTrackId = recordingTrackId;
}
@Override
public void onTrackRecordingId(Track.Id trackId) {
recordingTrackId = trackId;
public void setRecordingTrackPaused(boolean recordingTrackPaused) {
this.recordingTrackPaused = recordingTrackPaused;
}
}
@@ -19,8 +19,10 @@ package de.dennisguse.opentracks.content;
import androidx.annotation.NonNull;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Speed;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.stats.TrackStatistics;
/**
* Listener for track data changes.
@@ -47,20 +49,23 @@ public interface TrackDataListener {
*
* @param trackPoint the trackPoint
*/
void onSampledInTrackPoint(@NonNull TrackPoint trackPoint);
default void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics trackStatistics, Speed smoothedSpeed, double smoothedAltitude_m) {
}
/**
* Called when a sampled out track point is read.
*
* @param trackPoint the trackPoint
*/
void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint);
default void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics trackStatistics) {
}
/**
* Called when finish sending new track points.
* This gets called after every batch of calls to {@link #onSampledInTrackPoint(TrackPoint)} and {@link #onSampledOutTrackPoint(TrackPoint)}.
* This gets called after every batch of calls to {@link #onSampledInTrackPoint(TrackPoint, TrackStatistics, Speed, double)} and {@link #onSampledOutTrackPoint(TrackPoint, TrackStatistics)}.
*/
void onNewTrackPointsDone(@NonNull TrackPoint lastTrackPoint);
default void onNewTrackPointsDone(@NonNull TrackPoint lastTrackPoint, @NonNull TrackStatistics trackStatistics) {
}
/**
* Called to clear previously sent markers.
@@ -41,10 +41,10 @@ class TrackDataManager {
}
void unregisterTrackDataListener(TrackDataListener trackDataListener) {
dataListenerTracks.add(trackDataListener);
dataListenerMarkers.add(trackDataListener);
dataListenerTrackPoints_SampledIn.add(trackDataListener);
dataListenerTrackPoints_SampledOut.add(trackDataListener);
dataListenerTracks.remove(trackDataListener);
dataListenerMarkers.remove(trackDataListener);
dataListenerTrackPoints_SampledIn.remove(trackDataListener);
dataListenerTrackPoints_SampledOut.remove(trackDataListener);
}
boolean hasListeners() {
@@ -20,9 +20,13 @@ public class Distance {
return of(distance_mile * UnitConversions.MI_TO_M);
}
public static Distance ofKilometer(double distance_km) {
return of(distance_km * UnitConversions.KM_TO_M);
}
public static Distance one(boolean metricUnit) {
if (metricUnit) {
return Distance.of(1);
return Distance.ofKilometer(1);
} else {
return Distance.ofMile(1);
}
@@ -39,6 +39,7 @@ import de.dennisguse.opentracks.content.TrackDataHub;
import de.dennisguse.opentracks.content.TrackDataListener;
import de.dennisguse.opentracks.content.data.Distance;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Speed;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.databinding.ChartBinding;
@@ -72,7 +73,6 @@ public class ChartFragment extends Fragment implements TrackDataListener {
private TrackDataHub trackDataHub;
// Stats gathered from the received data
private TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
private final List<ChartPoint> pendingPoints = new ArrayList<>();
private String category = "";
@@ -217,7 +217,6 @@ public class ChartFragment extends Fragment implements TrackDataListener {
@Override
public void clearTrackPoints() {
if (isResumed()) {
trackStatisticsUpdater = new TrackStatisticsUpdater();
pendingPoints.clear();
chartView.reset();
runOnUiThread(() -> {
@@ -228,24 +227,15 @@ public class ChartFragment extends Fragment implements TrackDataListener {
}
}
@Override
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint) {
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics trackStatistics, Speed smoothedSpeed, double smoothedAltitude_m) {
if (isResumed()) {
pendingPoints.add(createPendingPoint(trackPoint));
ChartPoint point = new ChartPoint(trackStatistics, trackPoint, smoothedSpeed, smoothedAltitude_m, chartByDistance, chartView.getMetricUnits());
pendingPoints.add(point);
}
}
@Override
public void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint) {
if (isResumed()) {
if (trackStatisticsUpdater != null) {
trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
}
}
}
@Override
public void onNewTrackPointsDone(@NonNull TrackPoint unused) {
public void onNewTrackPointsDone(@NonNull TrackPoint unused, @NonNull TrackStatistics alsoUnused) {
if (isResumed()) {
chartView.addChartPoints(pendingPoints);
pendingPoints.clear();
@@ -306,6 +296,8 @@ public class ChartFragment extends Fragment implements TrackDataListener {
* Returns true if the selected track is recording.
* Needs to be synchronized because trackDataHub can be accessed by multiple threads.
*/
@Deprecated
//TODO Should not be dynamic but instead set while instantiating, i.e., newFragment().
private synchronized boolean isSelectedTrackRecording() {
return trackDataHub != null && trackDataHub.isSelectedTrackRecording();
}
@@ -322,12 +314,6 @@ public class ChartFragment extends Fragment implements TrackDataListener {
}
}
@VisibleForTesting
ChartPoint createPendingPoint(@NonNull TrackPoint trackPoint) {
trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
return new ChartPoint(trackStatisticsUpdater, trackPoint, chartByDistance, chartView.getMetricUnits());
}
@VisibleForTesting
void setChartView(ChartView view) {
chartView = view;
@@ -25,9 +25,11 @@ import de.dennisguse.opentracks.adapters.IntervalStatisticsAdapter;
import de.dennisguse.opentracks.content.TrackDataHub;
import de.dennisguse.opentracks.content.TrackDataListener;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Speed;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.databinding.IntervalListViewBinding;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
import de.dennisguse.opentracks.viewmodels.IntervalStatisticsModel;
@@ -229,21 +231,21 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
}
@Override
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint) {
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics unused, Speed unused2, double unused3) {
if (isResumed()) {
viewModel.add(trackPoint);
}
}
@Override
public void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint) {
public void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics unused) {
if (isResumed()) {
viewModel.add(trackPoint);
}
}
@Override
public void onNewTrackPointsDone(@NonNull TrackPoint unused) {
public void onNewTrackPointsDone(@NonNull TrackPoint unused, @NonNull TrackStatistics alsoUnused) {
if (isResumed()) {
runOnUiThread(viewModel::onNewTrackPoints);
}
@@ -18,6 +18,7 @@ package de.dennisguse.opentracks.fragments;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
@@ -54,6 +55,8 @@ import de.dennisguse.opentracks.viewmodels.SensorDataModel;
*/
public class StatisticsRecordedFragment extends Fragment {
private static final String TAG = StatisticsRecordedFragment.class.getSimpleName();
private static final String TRACK_ID_KEY = "trackId";
public static StatisticsRecordedFragment newInstance(Track.Id trackId) {
@@ -154,19 +157,25 @@ public class StatisticsRecordedFragment extends Fragment {
getActivity().runOnUiThread(() -> {
if (isResumed()) {
Track track = contentProviderUtils.getTrack(trackId);
sensorStatistics = contentProviderUtils.getSensorStats(trackId);
if ((this.track == null && track != null) || (this.track != null && track != null && !this.track.getCategory().equals(track.getCategory()))) {
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, getString(R.string.stats_rate_key));
if (track == null) {
Log.e(TAG, "track cannot be null");
getActivity().finish();
return;
}
sensorStatistics = contentProviderUtils.getSensorStats(trackId);
boolean prefsChanged = this.track == null || (!this.track.getCategory().equals(track.getCategory()));
this.track = track;
if (prefsChanged) {
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, getString(R.string.stats_rate_key));
}
loadTrackDescription(track);
updateUI();
updateSensorUI();
((TrackRecordedActivity) getActivity()).startPostponedEnterTransitionWith(viewBinding.statsActivityTypeIcon, viewBinding.statsNameValue);
((TrackRecordedActivity) getActivity()).startPostponedEnterTransitionWith(viewBinding.statsActivityTypeIcon);
}
});
}
@@ -34,6 +34,7 @@ import de.dennisguse.opentracks.content.sensor.SensorDataSet;
import de.dennisguse.opentracks.databinding.StatisticsRecordingBinding;
import de.dennisguse.opentracks.services.TrackRecordingService;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.StringUtils;
import de.dennisguse.opentracks.util.TrackIconUtils;
@@ -211,17 +212,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
}
@Override
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint) {
// We don't care.
}
@Override
public void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint) {
// We don't care.
}
@Override
public void onNewTrackPointsDone(@NonNull TrackPoint newLastTrackPoint) {
public void onNewTrackPointsDone(@NonNull TrackPoint newLastTrackPoint, @NonNull TrackStatistics trackStatistics) {
if (isResumed()) {
getActivity().runOnUiThread(() -> {
if (isResumed()) {
@@ -388,7 +379,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
}
// Set time
if (track != null && track.getTrackStatistics() != null) {
if (track != null) {
viewBinding.statsMovingTimeValue.setText(StringUtils.formatElapsedTime(track.getTrackStatistics().getMovingTime()));
updateTotalTime();
}
@@ -192,6 +192,10 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
return serviceStatus.getRecordingTrackPaused();
}
public Track.Id getRecordingTrackId() {
return serviceStatus.getRecordingTrackId();
}
public TrackStatistics getTrackStatistics() {
if (trackStatisticsUpdater == null) {
return null;
@@ -252,7 +256,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
*/
public Track.Id startNewTrack() {
if (isRecording()) {
Log.d(TAG, "Ignore startNewTrack. Already recording.");
Log.w(TAG, "Ignore startNewTrack. Already recording.");
return null;
}
@@ -312,7 +316,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public void resumeCurrentTrack() {
if (!isRecording() || !isPaused()) {
Log.d(TAG, "Ignore resumeCurrentTrack. Not recording or not paused.");
Log.w(TAG, "Ignore resumeCurrentTrack. Not recording or not paused.");
return;
}
@@ -361,10 +365,10 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
}
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public Track.Id endCurrentTrack() {
public void endCurrentTrack() {
if (!isRecording()) {
Log.d(TAG, "Ignore endCurrentTrack. Not recording.");
return null;
Log.w(TAG, "Ignore endCurrentTrack. Not recording.");
return;
}
// Need to remember the recordingTrackId before setting it to null
@@ -390,14 +394,12 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
ExportUtils.postWorkoutExport(this, track, new ExportServiceResultReceiver(new Handler(), this));
endRecording(true);
return trackId;
}
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public void pauseCurrentTrack() {
if (!isRecording() || isPaused()) {
Log.d(TAG, "Ignore pauseCurrentTrack. Not recording or paused.");
Log.w(TAG, "Ignore pauseCurrentTrack. Not recording or paused.");
return;
}
@@ -569,6 +571,12 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
serviceStatus.addListener(listener);
}
//TODO Check that this is used everywhere, where addListener is called!
//Otherwise, we keep objects in referenced and waste memory until this service instance is terminated.
public void removeListener(@NonNull TrackRecordingServiceStatus.Listener listener) {
serviceStatus.removeListener(listener);
}
/**
* Inserts a trackPoint if this trackPoint is different than lastValidTrackPoint.
*
@@ -19,7 +19,7 @@ public class TrackRecordingServiceStatus {
private final List<Listener> listeners = new ArrayList<>();
public void addListener(@NonNull Listener listener) {
void addListener(@NonNull Listener listener) {
if (this.listeners.contains(listener)) {
return;
}
@@ -28,6 +28,10 @@ public class TrackRecordingServiceStatus {
listener.onTrackRecordingPaused(recordingTrackPaused);
}
void removeListener(@NonNull Listener listener) {
this.listeners.remove(listener);
}
public boolean getRecordingTrackPaused() {
return this.recordingTrackPaused;
}
@@ -40,7 +40,7 @@ public class AltitudeSumManager implements SensorEventListener {
Log.w(TAG, "No pressure sensor available.");
isConnected = false;
} else {
isConnected = sensorManager.registerListener(this, pressureSensor, (int) SAMPLING_RATE.toNanos());
isConnected = sensorManager.registerListener(this, pressureSensor, SensorManager.SENSOR_DELAY_FASTEST);
}
lastAcceptedPressureValue_hPa = Float.NaN;
@@ -24,6 +24,7 @@ import android.speech.tts.UtteranceProgressListener;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import de.dennisguse.opentracks.content.data.Distance;
@@ -32,7 +33,6 @@ import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
import de.dennisguse.opentracks.services.TrackRecordingService;
import de.dennisguse.opentracks.services.TrackRecordingServiceStatus;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.AnnouncementUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
@@ -58,8 +58,6 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
private final ContentProviderUtils contentProviderUtils;
private Track.Id recordingTrackId;
private final AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
@@ -142,13 +140,8 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
Log.e(TAG, "TrackRecordingService is null.");
return;
}
trackRecordingService.addListener(new TrackRecordingServiceStatus.Listener() {
@Override
public void onTrackRecordingId(Track.Id trackId) {
recordingTrackId = trackId;
}
});
announce(trackRecordingService.getTrackStatistics());
announce(trackRecordingService.getRecordingTrackId(), trackRecordingService.getTrackStatistics());
}
/**
@@ -156,7 +149,7 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
*
* @param trackStatistics the track statistics
*/
private void announce(TrackStatistics trackStatistics) {
private void announce(Track.Id trackId, TrackStatistics trackStatistics) {
if (trackStatistics == null) {
Log.e(TAG, "TrackStatistics is null.");
return;
@@ -175,13 +168,18 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
}
}
if (audioManager.getMode() == AudioManager.MODE_IN_CALL || audioManager.getMode() == AudioManager.MODE_IN_COMMUNICATION) {
if (Arrays.asList(AudioManager.MODE_IN_CALL, AudioManager.MODE_IN_COMMUNICATION)
.contains(audioManager.getMode())) {
Log.i(TAG, "Speech is not allowed at this time.");
return;
}
Track track = contentProviderUtils.getTrack(recordingTrackId);
String category = track != null ? track.getCategory() : "";
Track track = contentProviderUtils.getTrack(trackId);
if (track == null) {
Log.i(TAG, "It doesn't exists a track with trackid = " + track);
return;
}
String category = track.getCategory();
//TODO Querying all TrackPoints all the time is inefficient; use TrackDataHub or something else.
TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null);
@@ -18,6 +18,7 @@ package de.dennisguse.opentracks.stats;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import java.time.Duration;
import java.time.Instant;
@@ -193,6 +194,7 @@ public class TrackStatistics {
this.movingTime = movingTime;
}
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public void addMovingTime(Duration time) {
movingTime = movingTime.plus(time);
}
@@ -265,6 +267,7 @@ public class TrackStatistics {
this.totalAltitudeGain_m = totalAltitudeGain_m;
}
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public void addTotalAltitudeGain(float gain_m) {
if (totalAltitudeGain_m == null) {
totalAltitudeGain_m = 0f;
@@ -285,6 +288,7 @@ public class TrackStatistics {
this.totalAltitudeLoss_m = totalAltitudeLoss_m;
}
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public void addTotalAltitudeLoss(float loss_m) {
if (totalAltitudeLoss_m == null) {
totalAltitudeLoss_m = 0f;
@@ -106,7 +106,7 @@ public class TrackStatisticsUpdater {
*/
public void addTrackPoint(TrackPoint trackPoint, Distance minGPSDistance) {
internalAddTrackPoint(trackPoint, minGPSDistance);
Log.d(TAG, this.toString());
Log.v(TAG, this.toString());
}
private void internalAddTrackPoint(TrackPoint trackPoint, Distance minGPSDistance) {
@@ -43,7 +43,7 @@ public class AnnouncementUtils {
int paceId = isMetricUnits ? R.string.voice_pace_per_kilometer : R.string.voice_pace_per_mile;
rate = context.getString(paceId, getAnnounceTime(context, time));
Duration currentTime = currentDistancePerTime.toPace(isMetricUnits);
Duration currentTime = currentDistancePerTime != null ? currentDistancePerTime.toPace(isMetricUnits) : Duration.ofMillis(0);
currentRate = context.getString(paceId, getAnnounceTime(context, currentTime));
currentRateMsg = context.getString(R.string.voice_pace_lap, currentRate);
}
@@ -35,7 +35,7 @@ public class IntervalStatisticsModel extends AndroidViewModel {
}
intervalsLiveData = new MutableLiveData<>();
distanceInterval = interval.getValue();
distanceInterval = interval.getDistance(metricUnits);
loadIntervalStatistics();
}
return intervalsLiveData;
@@ -73,7 +73,7 @@ public class IntervalStatisticsModel extends AndroidViewModel {
interval = IntervalOption.OPTION_1;
}
distanceInterval = interval.getValue();
distanceInterval = interval.getDistance(metricUnits);
loadIntervalStatistics();
}
}
@@ -91,19 +91,21 @@ public class IntervalStatisticsModel extends AndroidViewModel {
OPTION_20(20),
OPTION_50(50);
private final Distance value;
private final int multiplier;
IntervalOption(int value) {
this.value = Distance.of(value);
IntervalOption(int multiplier) {
this.multiplier = multiplier;
}
public Distance getValue() {
return value;
public Distance getDistance(boolean metricUnits) {
return Distance
.one(metricUnits)
.multipliedBy(multiplier);
}
@Override
public String toString() {
return "" + (int) value.toM(); //TODO Somehow IntervalsFragment relies on a parsable Integer.
return "" + multiplier; //TODO Somehow IntervalsFragment relies on a parsable Integer.
}
}
}
+5 -3
View File
@@ -217,8 +217,8 @@ g) das tun, was Deiner Meinung nach OpenTracks hilft.</string>
<string name="gps_location_access">\"Standortzugriff\"</string>
<string name="gps_disabled">GPS ist nicht aktiviert. Tippen Sie hier, um zu %1$s wechseln. Drücken Sie \"Zurück\", um zurückzugehen.</string>
<string name="gps_disabled_my_location">Der aktuelle Standort kann nicht ermittelt werden. Aktivieren Sie mehr Standortquellen unter \"%1$s\" und vergewissern Sie sich, dass der Himmel sichtbar ist. Bitte versuchen Sie es später erneut.</string>
<string name="gps_not_found">Das Gerät sendet kein GPS-Signal.</string>
<string name="gps_starting">GPS wird gestartet.</string>
<string name="gps_not_found">Das Gerät empfängt kein GPS-Signal</string>
<string name="gps_starting">GPS wird gestartet</string>
<string name="gps_wait_for_better_signal">Warten auf besseres GPS-Signal</string>
<string name="gps_wait_for_signal">Warten auf GPS-Signal</string>
<!-- Image -->
@@ -584,9 +584,11 @@ g) das tun, was Deiner Meinung nach OpenTracks hilft.</string>
<string name="export_progress_summary_overwrite_msg">Streckenaufzeichnung überschrieben</string>
<string name="export_progress_summary_skip_msg">Strecke übersprungen</string>
<string name="export_progress_summary_errors_msg">Fehler</string>
<string name="generic_click_twice_cancel">Klicke nochmal zurück um abzubrechen.</string>
<string name="generic_click_twice_cancel">Klicke nochmal auf zurück um abzubrechen</string>
<string name="import_error_list_dialog_title">Dateien wurden nicht importiert</string>
<string name="voice_speed_lap">Rundengeschwindigkeit %1$s</string>
<string name="voice_pace_lap">Rundenzeit %1$s</string>
<string name="toast_message_instant_track_was_exported">Letzte Aufzeichnung exportiert</string>
<string name="export_track_already_exists_msg">Der Track %1$s existiert bereits im Zielverzeichnis.</string>
<string name="gps_fixed_and_ready">GPS-Position bestimmt und bereit</string>
</resources>
+1 -1
View File
@@ -600,7 +600,7 @@ Si el dispositivo GPS reporta datos inexactos (por ejemplo, ubicación, velocida
<string name="value_pace_mile">%1$d:%2$02d min/mi</string>
<string name="sensor_state_cadence_max">Cadencia máxima</string>
<string name="sensor_state_cadence_avg">Cadencia media</string>
<string name="value_float_kilometer_hour">%1$.2f kp/h</string>
<string name="value_float_kilometer_hour">%1$.2f km/h</string>
<string name="export_progress_summary_overwrite_msg">Pistas sobrescritas</string>
<string name="export_progress_summary_skip_msg">Pistas omitidas</string>
<string name="export_progress_summary_errors_msg">Errores</string>
+78 -3
View File
@@ -13,7 +13,8 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--><resources>
-->
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="about_preference_title">À propos d\'OpenTracks</string>
<string name="about_description"><i>OpenTracks</i> est une application de suivi sportif qui respecte totalement votre vie privée.</string>
<string name="about_url">Page web du projet : <a href="%1$s">%1$s</a></string>
@@ -291,7 +292,7 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
<string name="sensor_state_cadence_value">%1$d tr/min</string>
<string name="sensor_state_disconnected">Déconnecté</string>
<string name="sensor_state_heart_rate">Fréquence cardiaque</string>
<string name="sensor_state_heart_rate_value">%1$d bpm</string>
<string name="sensor_state_heart_rate_value">%1$d ppm</string>
<string name="sensor_state_power">Puissance</string>
<string name="sensor_state_power_value">%1$d W</string>
<string name="sensor_not_known">Capteur %1$s est inconnu. Veuillez vérifier les paramètres.</string>
@@ -358,7 +359,7 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
<string name="stats_average_speed">Vitesse moyenne</string>
<string name="stats_altitude">Altitude</string>
<string name="stats_fastest_pace">Allure la plus rapide</string>
<string name="stats_gain">Gain</string>
<string name="stats_gain">Gagner</string>
<string name="stats_grade">Inclinaison</string>
<string name="stats_latitude">Latitude</string>
<string name="stats_longitude">Longitude</string>
@@ -537,4 +538,78 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
<!-- Share image -->
<string name="share_image_subject">J aimerais partager une image avec vous</string>
<string name="share_image_body">Je pense que cette image pourrait vous intéresser.</string>
<string name="track_pace_notification">Vitesse : %1$s</string>
<string name="track_speed_notification">Vitesse : %1$s</string>
<string name="track_detail_intervals_tab">Intervalles</string>
<string name="interval_list_empty_message">Il n\'y a pas encore d\'intervalle</string>
<string name="aggregated_stats_empty_message">Enregistrez votre première piste pour voir les statistiques agrégées</string>
<string name="value_float_kilometer_hour">%1$.2f km/h</string>
<string name="value_float_mile_hour">%1$.2f mph</string>
<string name="value_pace_kilometer">%1$d:%2$02d min/km</string>
<string name="value_pace_mile">%1$d:%2$02d min/mi</string>
<string name="voice_pace_lap">Temps au tour de %1$s</string>
<string name="voice_speed_lap">Vitesse du tour de %1$s</string>
<string name="toast_message_instant_track_was_exported">Dernière piste exportée</string>
<string name="settings_default_export_uri_title">Répertoire d\'exportation des pistes</string>
<string name="instant_export_enabled_title">Exportation instantanée après l\'entraînement</string>
<string name="stats_intervals">Intervalles</string>
<string name="stats_loss">Perte</string>
<string name="settings_prevent_reimport_tracks_title">Empêcher la réimportation de pistes</string>
<string name="settings_night_mode_option_night">Nuit</string>
<string name="settings_night_mode_option_day">Jour</string>
<string name="settings_night_mode_option_system">Système</string>
<string name="stats_split_interval">Intervalle partagé</string>
<string name="instant_export_enabled_summary">Exportation de la piste vers le stockage après la fin de l\'enregistrement</string>
<string name="hold_to_pause">Tenir pour faire une pause</string>
<string name="hold_to_stop">Maintenir pour arrêter</string>
<string name="track_distance_notification">Distanciation : %1$s</string>
<string name="open_track_as_KMZ">Ouvert comme KMZ</string>
<string name="open_track_as_GPX">Ouvert en tant que GPX</string>
<string name="open_track_as_trackfileformat">Ouvrir comme %1$s</string>
<string name="open_track_using_dashboard">Ouvrir en utilisant OpenTracks-Dashboard</string>
<string name="activity_type_escooter">éscooter</string>
<string name="settings_sensor_wheel_circumference">Circonférence de roue (millimètre)</string>
<string name="export_error_post_workout">L\'exportation du post-travail a échoué, veuillez vérifier le répertoire d\'exportation.</string>
<string name="export_all_do_it_for_all">le faire pour tous les conflits</string>
<string name="export_track_errors">Pistes non exportées</string>
<string name="import_unable_to_import_file">Impossible d\'importer le fichier : %1$s</string>
<string name="import_no_kml_file_found">Pas de doc.fichier kml trouvé dans KMZ</string>
<string name="import_error_info">%1$s : %2$s</string>
<string name="import_error_list_dialog_title">Les fichiers ne sont pas importées</string>
<string name="settings_night_mode_title">Thème de l\'Interface utilisateur</string>
<string name="generic_completed">Compléter</string>
<string name="sensor_state_cadence_max">Cadence maximale</string>
<string name="settings_recording_fullscreen_on_while_recording_title">Plein écran</string>
<string name="settings_recording_fullscreen_on_while_recording_summary">Pendant l\'enregistrement, présent en plein écran.</string>
<string name="sensor_state_power_avg">Puissance moyenne</string>
<string name="sensor_state_heart_rate_max">Fréquence cardiaque max</string>
<string name="sensor_state_heart_rate_avg">Rythme cardiaque moyen</string>
<string name="sensor_state_cadence_avg">Cadence moyenne</string>
<string name="generic_click_twice_cancel">Cliquez sur retour pour annuler</string>
<string name="export_progress_summary_new_msg">Nouvelles pistes exportées</string>
<string name="export_progress_summary_skip_msg">Pistes sautées</string>
<string name="export_progress_summary_errors_msg">Erreurs</string>
<string name="generic_overwrite">Écraser</string>
<string name="generic_show_errors">Afficher les erreurs</string>
<string name="generic_separator_done_total">/</string>
<string name="generic_skip">Sauter</string>
<string name="generic_open_track">Voie ouverte</string>
<string name="gps_disabled_msg">GPS désactivé</string>
<string name="gps_fixed_and_ready">GPS fixe et prêt</string>
<string name="import_thread_interrupted">Fil interrompu</string>
<string name="import_unsupported_format">Format de fichier non pris en charge</string>
<string name="import_parser_error">Erreur de l\'analyseur : %1$s</string>
<string name="import_file_imported">Fichier %1$s importé</string>
<string name="import_prevent_reimport">Vous avez défini empêcher la réimportation de la piste</string>
<string name="import_progress_summary_ok_msg">Les fichiers importés</string>
<string name="import_progress_summary_exists_msg">Il existe déjà</string>
<string name="import_progress_summary_errors_msg">Erreur</string>
<plurals name="generic_completed_with_errors">
<item quantity="one" tools:ignore="ImpliedQuantity">Terminé avec une erreur</item>
<item quantity="other">Terminé avec des erreurs %1$d</item>
</plurals>
<string name="settings_default_trackfileformat">Format de fichier d\'exportation / partage</string>
<string name="description_altitude_loss">Perte d\'altitude : %1$d m (%2$d ft)</string>
<string name="export_track_already_exists_msg">La piste %1$s existe déjà dans le répertoire de destination.</string>
<string name="export_progress_summary_overwrite_msg">Pistes écrasées</string>
</resources>
+152 -9
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!--
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,8 +13,7 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
--><resources>
<!-- Activity Type -->
<string name="activity_type_airplane">repülőgép</string>
<string name="activity_type_atv">terepjáró</string>
@@ -63,7 +63,7 @@ limitations under the License.
<string name="activity_type_skate_boarding">gördeszkázás</string>
<string name="activity_type_skating">korcsolyázás</string>
<string name="activity_type_skiing">síelés</string>
<string name="activity_type_sky_jumping">sky jumping</string>
<string name="activity_type_sky_jumping">égi ugrás</string>
<string name="activity_type_sled">szánkó</string>
<string name="activity_type_snow_boarding">snowboard</string>
<string name="activity_type_snow_shoeing">hótalpazás</string>
@@ -75,7 +75,7 @@ limitations under the License.
<string name="activity_type_trail_hiking">túrázás</string>
<string name="activity_type_trail_running">terepfutás</string>
<string name="activity_type_train">vonat</string>
<string name="activity_type_ultimate_frisbee">ultimate frisbee</string>
<string name="activity_type_ultimate_frisbee">ultimate frizbi</string>
<string name="activity_type_unknown">ismeretlen</string>
<string name="activity_type_wakeboarding">wakeboard</string>
<string name="activity_type_walking">gyaloglás</string>
@@ -147,7 +147,7 @@ limitations under the License.
<string name="generic_ok">OK</string>
<string name="generic_paused">Szüneteltetett</string>
<string name="generic_progress_title">Kis türelmet…</string>
<string name="generic_recording">Rögzítés</string>
<string name="generic_recording">Rögzítés…</string>
<string name="generic_save">Mentés</string>
<string name="generic_success_title">Sikerült</string>
<string name="generic_tracks">Számok</string>
@@ -277,10 +277,11 @@ limitations under the License.
<string name="settings_stats_units_imperial">Angolszász (mérföld, láb)</string>
<string name="settings_stats_units_metric">Metrikus (km, m)</string>
<string name="settings_stats_units_title">Mértékegység választása</string>
<!-- Share Track -->
<string name="share_track_share_file">Útvonal-fájl megosztása</string>
<string name="share_track_share_file_body">Szerintem érdekelni fog ez a pálya.\n\nA pályát a Google Earth vagy más térképszoftver segítségével nézheted meg.\n\n%1$s</string>
<string name="share_track_share_file_body">Szerintem érdekelni fog ez a pálya.
\n
\n%1$s</string>
<string name="share_track_subject">Szeretnék megosztani veled egy pályát</string>
<!-- Stats -->
<string name="stats_average_moving_pace">Mozgás átlagos irama</string>
@@ -462,4 +463,146 @@ limitations under the License.
<string name="waypoint_type_waterfall">vízesés</string>
<string name="waypoint_type_water_fountain">szökőkút</string>
<string name="waypoint_type_waypoint">útpont</string>
</resources>
<string name="generic_overwrite">Felülírás</string>
<string name="generic_separator_done_total">/</string>
<string name="generic_skip">Ugrás</string>
<string name="about_url">A projekt weboldala: <a href="%1$s">%1$s</a></string>
<string name="about_version_name">Verzió neve: %1$s</string>
<string name="about_version_code">Verziókód: %1$d</string>
<string name="about_preference_title">Az OpenTracks</string>
<string name="help_data_google_play_services_title">Az OpenTracks igényel/használja a Google Play szolgáltatást\?</string>
<string name="help_track_export_title">Hogyan tudom exportálni/megosztani a felvett zeneszámokat\?</string>
<string name="help_storage_pictures_content">Ezeket az alkalmazás belső tárhelyén tárolja (azaz csak az OpenTracks-en belül érhető el).</string>
<string name="about_libraries">Könyvtárak</string>
<string name="help_feature_title">Az OpenTracks a következő funkciókkal rendelkezik:</string>
<string name="help_storage_pictures_title">Hogyan tárolja az OpenTracks a jelölőként készített képeket\?</string>
<string name="help_track_map_title">Hogyan jelenítsünk meg egy pályát a térképen\?</string>
<string name="help_gps_troubleshooting_title">Az OpenTracks nem rögzíti a helyszíneket\?</string>
<string name="help_support_title">Hogyan tudom támogatni az OpenTracks-t\?</string>
<string name="open_track_using_dashboard">Megnyitás az OpenTracks-Dashboard segítségével</string>
<string name="open_track_as_KMZ">Megnyitás KMZ-ként</string>
<string name="open_track_as_GPX">Megnyitás GPX-ként</string>
<string name="open_track_as_trackfileformat">Megnyitás %1$s néven</string>
<string name="activity_type_escooter">eszkoter</string>
<string name="help_recording_content">Az OpenTracks a készülék GPS-érzékelőjét használja a hely folyamatos rögzítésére. Ha a GPS-eszköz pontatlan adatokat (pl. hely, sebesség, magasság) jelent, az OpenTracks nem tud mit tenni.</string>
<string name="help_data_storage_title">Hogyan tárolja az OpenTracks az érzékelőadatokat\?</string>
<string name="app_not_installed_show_on_map">Kérjük, telepítsen egy olyan alkalmazást, amely támogatja a térképadatok megjelenítését.</string>
<string name="import_error_list_dialog_title">Nem importált fájlok</string>
<string name="import_progress_summary_errors_msg">Hibák</string>
<string name="sensor_state_cadence_max">Max kadencia</string>
<string name="sensor_state_heart_rate_max">Átlagos pulzusszám</string>
<string name="stats_intervals">Intervallumok</string>
<string name="settings_sensor_wheel_circumference">Kerékkörfogat (mm)</string>
<string name="stats_split_interval">Osztott intervallum</string>
<string name="track_speed_notification">Sebesség: %1$s</string>
<string name="track_detail_intervals_tab">Intervallum</string>
<string name="track_detail_record_success">Most egy új szám felvétele</string>
<string name="track_detail_resume_success">Most folytatódik a pálya</string>
<string name="interval_list_empty_message">Még nincs intervallum</string>
<string name="value_float_mile_hour">%1$.2f mph</string>
<string name="value_pace_mile">%1$d:%2$02d min/mi</string>
<string name="settings_default_export_uri_title">Pálya export könyvtár</string>
<string name="instant_export_enabled_summary">Sáv exportálása a tárolóba a felvétel befejezése után</string>
<string name="toast_message_instant_track_was_exported">Utolsó exportált zeneszám</string>
<string name="hold_to_pause">Tartsa a szünethez</string>
<string name="hold_to_stop">Tartsa lenyomva a leállításhoz</string>
<string name="track_distance_notification">Távolság: %1$s</string>
<string name="value_pace_kilometer">%1$d:%2$02d min/km</string>
<string name="instant_export_enabled_title">Azonnali edzés utáni export</string>
<string name="track_pace_notification">Tempó: %1$s</string>
<string name="export_with_photos">fotókkal</string>
<string name="export_error_post_workout">Az edzés utáni exportálás sikertelen, kérjük, ellenőrizze az exportkönyvtárat.</string>
<string name="export_track_already_exists_msg">A %1$s cím már létezik a célkönyvtárban.</string>
<string name="export_all_do_it_for_all">minden konfliktus esetén</string>
<string name="export_track_errors">Nem exportált sávok</string>
<string name="import_thread_interrupted">Szál megszakadt</string>
<string name="import_unsupported_format">Nem támogatott fájlformátum</string>
<string name="description_default_speed_or_pace">Tevékenységtípus szerint</string>
<string name="export_without_photos">fotók nélkül</string>
<string name="value_float_kilometer_hour">%1$.2f km/h</string>
<string name="voice_speed_lap">%1$s körsebessége</string>
<string name="help_gps_troubleshooting_content"> Mit tehetsz:
\n a) ellenőrizze, hogy a GPS engedélyezve van-e,
\n b) javítsa a GPS-vételt (pl. menjen a szabadba),
\n c) ellenőrizze a <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a> alkalmazással, és
\n d) tiltsa le az energiatakarékosságot.
\n
\n Ha mindez nem segít, kérjük, jelentse a hibát. </string>
<string name="export_progress_summary_new_msg">Új pályák exportálása</string>
<string name="generic_completed">Befejezett</string>
<string name="sensor_state_heart_rate_avg">Átlagos pulzusszám</string>
<string name="sensor_state_power_avg">Átlagos teljesítmény</string>
<string name="sensor_not_known">A %1$s érzékelő ismeretlen. Kérjük, ellenőrizze a beállításokat.</string>
<string name="bluetooth_disabled">Kérjük, engedélyezze a Bluetooth funkciót.</string>
<string name="settings_default_trackfileformat">Export/megosztás fájlformátum</string>
<string name="settings_recording_show_on_lockscreen_while_recording_summary">Felvétel közben a statisztika megjelenítése a készülék feloldása nélkül.</string>
<string name="settings_recording_keep_screen_on_while_recording_title">Tartsa a képernyőt bekapcsolva</string>
<string name="settings_recording_keep_screen_on_while_recording_summary">Felvétel közben tartsa bekapcsolva a képernyőt.</string>
<string name="settings_recording_fullscreen_on_while_recording_title">Teljes képernyőn</string>
<string name="sensor_state_cadence_avg">Avg Kadencia</string>
<string name="settings_night_mode_title">UI téma</string>
<string name="settings_night_mode_option_system">Rendszer</string>
<string name="settings_night_mode_option_day">Nap</string>
<string name="settings_night_mode_option_night">Éjszaka</string>
<string name="settings_error_initial_values_restored">Sajnáljuk, de hiba történt. Minden beállítás visszaállt az alapértelmezett értékekre.</string>
<string name="settings_sensor_connecting">Csatlakoztatva %1$s</string>
<string name="settings_sensor_disconnected">Megszakadt a kapcsolat %1$s</string>
<string name="stats_loss">Veszteség</string>
<string name="track_recording_notification_accuracy">Helyszín pontossága: %1$s</string>
<string name="voice_pace_lap">Köridő %1$s</string>
<string name="export_progress_summary_overwrite_msg">Sávok felülírása</string>
<string name="export_progress_summary_skip_msg">Kihagyott sávok</string>
<string name="export_progress_summary_errors_msg">Hibák</string>
<string name="generic_open_track">Nyílt pálya</string>
<string name="generic_show_errors">Hibák megjelenítése</string>
<string name="generic_click_twice_cancel">Kattintson vissza a törléshez</string>
<string name="gps_disabled_msg">GPS letiltva</string>
<string name="gps_fixed_and_ready">GPS rögzítve és készen áll</string>
<string name="import_unable_to_import_file">Nem sikerült importálni a fájlt: %1$s</string>
<string name="import_no_kml_file_found">Nincs doc.kml fájl található a KMZ fájlban</string>
<string name="import_error_info">%1$s: %2$s</string>
<string name="import_file_imported">Fájl %1$s importálva</string>
<string name="import_prevent_reimport">Beállítottad, hogy megakadályozd az újraimportálást</string>
<string name="import_progress_summary_ok_msg">Importált fájlok</string>
<string name="import_progress_summary_exists_msg">Már létezik</string>
<string name="menu_insert_gallery_img">Galéria kép beillesztése</string>
<string name="menu_resume_track">Folytassa a pályát</string>
<string name="import_parser_error">Parser hiba: %1$s</string>
<string name="help_data_storage_content"> Az érzékelőadatokat rögzített helyenként tárolják. A pulzusszám-érzékelőre vonatkoztatva ez azt jelenti, hogy csak <i>egy</i> pulzusszámmérés kerül tárolásra helyenként. Ha mozdulatlanul áll vagy beltérben tartózkodik, akkor nem kerülnek rögzítésre az érzékelő adatai (de megjelenítésre kerülnek). </string>
<string name="help_support_content"> Megteheti:
\n a) terjesztheti az OpenTracks hírét,
\n b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">jellemzőket javasolhat</a>,
\n c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">hibákat jelenteni</a>,
\n d) segíthetsz a szoftverfejlesztésben,
\n e) vásárolhat a <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>-ban,
\n f) adományozni, és
\n g) tegyen bármit, ami Ön szerint segít az OpenTracks-nak. </string>
<plurals name="generic_completed_with_errors">
<item quantity="one">Egy hibával befejezve</item>
<item quantity="other">%1$d hibával befejezve</item>
</plurals>
<string name="description_altitude_loss">Magasságveszteség: %1$d m (%2$d ft)</string>
<string name="marker_add_photo_canceled">Törölt, nincs fotójelző hozzáadva</string>
<string name="permission_gps_failed">Az OpenTracks engedélyt igényel a GPS használatához.</string>
<string name="sensor_could_not_scan">Nem sikerült Bluetooth-eszközöket keresni (hiba %1$i).</string>
<string name="settings_recording_show_on_lockscreen_while_recording_title">Statisztikák megjelenítése a záróképernyőn</string>
<string name="settings_recording_fullscreen_on_while_recording_summary">Felvétel közben teljes képernyőn jelenik meg.</string>
<string name="settings_prevent_reimport_tracks_title">A pályák újraimportálásának megakadályozása</string>
<string name="aggregated_stats_empty_message">Az első pálya rögzítése az összesített statisztikák megtekintéséhez</string>
<string name="share_image_subject">Szeretnék megosztani veletek egy képet</string>
<string name="share_image_body">Azt hiszem, ez a kép érdekelheti önöket.</string>
<string name="help_recording_title">Hogyan rögzíti az OpenTracks az adatokat\?</string>
<string name="help_track_export_content"> A nyomvonalak KMZ, KML vagy GPX formátumban exportálhatók a Beállításokból. KMZ/KML minden adat; GPX csak a helyszínek. A <i>megosztás ikonon</i> keresztül megosztott pályák KMZ formátumban kerülnek megosztásra. </string>
<string name="help_track_map_content"> Használja a kis térkép ikont.
\n a) (élő frissítések) Telepítse a <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>. Ez online vagy offline térképeket biztosít a <a href="https://www.openstreetmap.org">OpenStreetMap</a> segítségével.
\n Valójában az OSMDashboard egy különálló alkalmazás, amely csak a megjelenítendő pálya(k) adataihoz fér hozzá (ideiglenesen).
\n b) Egy normál térképalkalmazáson keresztül (pl. <a href="http://osmand.net/">OsmAnd</a> és <a href="https://maps.me/">MAPS.ME</a>). Ezeknek az alkalmazásoknak támogatniuk kell a KMZ fájlformátumot (csak a helyeket és az időbélyegeket). Valójában egy alkalmazásnak támogatnia kell a <i>ACTION_VIEW</i> MIME <i>application/vnd.google-earth.kmz</i> formátumot. </string>
<string name="about_description">Az <i>OpenTracks</i> egy sportkövető alkalmazás, amely teljes mértékben tiszteletben tartja az Ön magánéletét.</string>
<string name="about_history">Az OpenTracks a <a href="https://code.google.com/archive/p/mytracks/"><i>Google\'s MyTracks</i></a> (2016-ban megszűnt) elágazása.</string>
<string name="help_feature_content">* GPS nyomvonalak létrehozása
\n * szívritmus-érzékelő használata (csak Bluetooth LE)
\n * hangbejelentések (idő vagy távolság alapján)
\n * jelölések létrehozása (képekkel együtt)
\n * teljesen offline működik
\n * pályák megjelenítése térképen (lásd alább)</string>
</resources>
+41 -15
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!--
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,8 +13,7 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
--><resources>
<!-- Activity Type -->
<string name="activity_type_airplane">avião</string>
<string name="activity_type_atv">quadriciclos</string>
@@ -30,7 +30,7 @@ limitations under the License.
<string name="activity_type_driving_bus">ônibus</string>
<string name="activity_type_driving_car">carro</string>
<string name="activity_type_ferry">balsa</string>
<string name="activity_type_frisbee">frisbee</string>
<string name="activity_type_frisbee">frízebee</string>
<string name="activity_type_gliding">asa-delta</string>
<string name="activity_type_hang_gliding">voo livre</string>
<string name="activity_type_helicopter">helicóptero</string>
@@ -75,7 +75,7 @@ limitations under the License.
<string name="activity_type_trail_hiking">caminhada em trilha</string>
<string name="activity_type_trail_running">corrida em trilha</string>
<string name="activity_type_train">treino</string>
<string name="activity_type_ultimate_frisbee">ultimate frisbee</string>
<string name="activity_type_ultimate_frisbee">frízebee final</string>
<string name="activity_type_unknown">desconhecida</string>
<string name="activity_type_wakeboarding">esqui aquático</string>
<string name="activity_type_walking">caminhada</string>
@@ -173,7 +173,7 @@ limitations under the License.
<string name="image_stop">Parar</string>
<string name="image_track">Rota</string>
<!-- Import -->
<string name="import_error">Importação: %1$d de %2$s de %3$s</string>
<string name="import_error">importados %1$d de %2$s desde %3$s</string>
<string name="import_no_directory">%1$s não existe</string>
<string name="import_no_file">Nenhum arquivo para importar. Conecte o dispositivo ao computador e coloque os
arquivos para importação em %1$s.</string>
@@ -185,7 +185,7 @@ limitations under the License.
<!-- Marker -->
<string name="marker_add_error">Não foi possível inserir um marcador. Não há sinal de GPS. Tente novamente.</string>
<string name="marker_add_success">Um marcador foi inserido</string>
<string name="marker_add_canceled">Cancelado. Nenhum marcador foi adicionado.</string>
<string name="marker_add_canceled">Cancelado. Nenhum marcador foi adicionado</string>
<string name="marker_delete_all_confirm_message">Todos os marcadores desta rota serão excluídos permanentemente do dispositivo</string>
<string name="marker_delete_one_confirm_message">O marcador será excluído permanentemente do dispositivo</string>
<string name="marker_delete_one_confirm_title">Excluir marcador?</string>
@@ -255,8 +255,8 @@ limitations under the License.
<string name="settings_recording_location_frequency_summary">%1$s entre os locais gravados</string>
<string name="settings_recording_max_recording_distance_summary">Um novo segmento será criado se a distância entre os dois locais for maior que %1$s</string>
<string name="settings_recording_max_recording_distance_title">Distância máxima</string>
<string name="settings_recording_min_recording_distance_title">Interv. distância grav.</string>
<string name="settings_recording_min_recording_interval_title">Interv. tempo de grav.</string>
<string name="settings_recording_min_recording_distance_title">Interv. distância grav</string>
<string name="settings_recording_min_recording_interval_title">Interv. tempo de grav</string>
<string name="settings_recording_min_required_accuracy_summary">O local será ignorado se a precisão for menor que %1$s</string>
<string name="settings_recording_min_required_accuracy_title">Precisão do GPS</string>
<string name="settings_recording_track_name_date_iso_8601_option">Data (ISO 8601)</string>
@@ -269,7 +269,7 @@ limitations under the License.
<string name="settings_reset_confirm_title">Redefinir agora?</string>
<string name="settings_reset_done">Todas as configurações foram revertidas para os valores padrão</string>
<!-- Settings Sensor -->
<string name="settings_sensor">Sensor</string>
<string name="settings_sensor">Sensores</string>
<string name="settings_sensor_bluetooth">Bluetooth</string>
<string name="settings_sensor_bluetooth_sensor">Sensor Bluetooth</string>
<!-- Settings Stats -->
@@ -278,11 +278,11 @@ limitations under the License.
<string name="settings_stats_units_imperial">Imperial (milhas, pés)</string>
<string name="settings_stats_units_metric">Métrica (km, m)</string>
<string name="settings_stats_units_title">Unidades preferenciais</string>
<!-- Share Track -->
<string name="share_track_share_file">Compartilhar arquivo da trilha</string>
<string name="share_track_share_file_body">Achei que você pudesse se interessar por essa trilha.\n\nPara visualizá-la, use o Google Earth ou outro
software de mapas.\n\n%1$s</string>
<string name="share_track_share_file_body">Achei que você pudesse se interessar por essa trilha.
\n
\n%1$s</string>
<string name="share_track_subject">Eu gostaria de compartilhar uma trilha com você</string>
<!-- Stats -->
<string name="stats_average_moving_pace">Ritmo médio de movimentação</string>
@@ -296,7 +296,7 @@ limitations under the License.
<string name="stats_latitude">Latitude</string>
<string name="stats_longitude">Longitude</string>
<string name="stats_max">Máx.</string>
<string name="stats_max_speed">Velocidade máx.</string>
<string name="stats_max_speed">Velocidade máx</string>
<string name="stats_min">Mín.</string>
<string name="stats_moving_time">Tempo decorrido</string>
<string name="stats_pace">Ritmo</string>
@@ -464,4 +464,30 @@ limitations under the License.
<string name="waypoint_type_waterfall">Cascata</string>
<string name="waypoint_type_water_fountain">fonte de água</string>
<string name="waypoint_type_waypoint">ponto de referência</string>
</resources>
<string name="help_track_map_title">Como mostrar uma pista num mapa\?</string>
<string name="help_feature_content">* criar pistas GPS
\n * utilizar um sensor do ritmo cardíaco (apenas Bluetooth LE)
\n * anúncios de voz (por tempo ou distância)
\n * criar marcadores (incl. imagens)
\n * funciona completamente offline
\n * mostrar pistas num mapa (ver abaixo)</string>
<string name="help_track_map_content"> Utilize o pequeno ícone do mapa.
\n a) (actualizações ao vivo) Instalar <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>. Fornece mapas online ou offline através de <a href="https://www.openstreetmap.org">OpenStreetMap</a>.
\n Na verdade, o OSMDashboard é uma aplicação separada que só pode aceder (temporariamente) aos dados da(s) faixa(s) que vão ser mostrados.
\n b) Através de uma aplicação de mapa normal (por exemplo, <a href="http://osmand.net/">OsmAnd</a> e <a href="https://maps.me/">MAPS.ME</a>). Estas aplicações precisam de suportar o formato de ficheiro KMZ (apenas locais e timestamps). De facto, uma aplicação precisa de suportar <i>ACTION_VIEW</i> com MIME <i>application/vnd.google-earth.kmz</i>. </string>
<string name="help_gps_troubleshooting_content"> O que se pode fazer:
\n a) verificar se o GPS está activado,
\n b) melhorar a recepção GPS (por exemplo, ir para o exterior),
\n c) verificar com <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a> app, e
\n d) desactivar a poupança de energia.
\n
\n Se tudo isto não ajudar, por favor, informe um erro. </string>
<string name="help_support_content"> Pode:
\n a) espalhar a palavra sobre OpenTracks,
\n b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">propose features</a>,
\n c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">reportar bugs</a>,
\n d) ajuda no desenvolvimento de software,
\n e) comprar em <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,
\n f) doar, e
\n g) fazer o que achar que ajuda a OpenTracks. </string>
</resources>
+4
View File
@@ -462,4 +462,8 @@ limitations under the License.
<string name="waypoint_type_water_fountain">đài phun nước</string>
<string name="waypoint_type_waypoint">điểm tham chiếu</string>
<string name="about_preference_title">Về OpenTracks</string>
<string name="about_version_name">Tên phiên bản: %1$s</string>
<string name="about_version_code">Mã phiên bản: %1$d</string>
<string name="about_libraries">Thư viện</string>
<string name="help_feature_title">OpenTracks có những tính năng sau đây:</string>
</resources>