Compare commits

...

235 Commits

Author SHA1 Message Date
Dennis Guse 9db2aff631 Release: v4.1.1 2022-09-29 20:23:21 +02:00
Xosé M b7cedab1fb Translated using Weblate (Galician)
Currently translated at 100.0% (502 of 502 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/gl/
2022-09-28 21:39:42 +02:00
metezd 0a193c8b5c Translated using Weblate (Turkish)
Currently translated at 97.8% (490 of 501 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/tr/
2022-09-28 21:39:42 +02:00
Mingu113 7049fab204 Translated using Weblate (Vietnamese)
Currently translated at 73.6% (70 of 95 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/app-store-metadata-files/vi/
2022-09-28 21:39:42 +02:00
Mingu113 80fa49bcf0 Translated using Weblate (Vietnamese)
Currently translated at 100.0% (501 of 501 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/vi/
2022-09-28 21:39:42 +02:00
Leon Marz a1eb5fb15f Check if the device has GPS
Fixes #1380
2022-09-28 21:39:33 +02:00
dependabot[bot] 9160b9b104 Bump reactivecircus/android-emulator-runner from 2.25.0 to 2.26.0
Bumps [reactivecircus/android-emulator-runner](https://github.com/reactivecircus/android-emulator-runner) from 2.25.0 to 2.26.0.
- [Release notes](https://github.com/reactivecircus/android-emulator-runner/releases)
- [Changelog](https://github.com/ReactiveCircus/android-emulator-runner/blob/main/CHANGELOG.md)
- [Commits](https://github.com/reactivecircus/android-emulator-runner/compare/v2.25.0...v2.26.0)

---
updated-dependencies:
- dependency-name: reactivecircus/android-emulator-runner
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-26 20:19:51 +02:00
Dennis Guse 85a73c3c85 Implement introduction screen.
Fixes #1316.
2022-09-18 15:44:49 +02:00
Dennis Guse 8389f9150a Update sensor documentation. 2022-09-17 17:08:50 +02:00
Dennis Guse 3075f4010f Release: v4.1.0 2022-09-15 23:14:53 +02:00
Dennis Guse 82e305e6a7 Upgrade to Android Gradle Plugin 7.3.0 2022-09-15 22:14:55 +02:00
Dennis Guse 6351a335c0 Merge pull request #1217 from OpenTracksApp/kml23#339
Upgrade to KML2.3
2022-09-13 23:51:10 +02:00
Dennis Guse 6538daedef README: add export format compatibility matrix.
Part of #339.
2022-09-13 23:50:43 +02:00
Dennis Guse 82fe859c84 KML: Export KML2.3 - KML2.2 and KML2.3 can be imported.
Fixes #339.
2022-09-13 23:20:06 +02:00
Dennis Guse f567b03be5 Bugfix: import all crashed with ClassCastException. 2022-09-11 14:37:35 +02:00
monyxie 7dc4e8dbc8 Translated using Weblate (Chinese (Simplified))
Currently translated at 72.6% (69 of 95 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/app-store-metadata-files/zh_Hans/
2022-09-10 23:11:09 +02:00
monyxie 2a272a887e Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (501 of 501 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/zh_Hans/
2022-09-10 23:11:09 +02:00
Xosé M 868509e00b Translated using Weblate (Galician)
Currently translated at 100.0% (501 of 501 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/gl/
2022-09-10 23:11:08 +02:00
FTno 477bdd5214 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (501 of 501 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/nb_NO/
2022-09-10 23:11:08 +02:00
Xosé M 286cec8d77 Translated using Weblate (Galician)
Currently translated at 100.0% (501 of 501 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/gl/
2022-09-10 23:11:08 +02:00
Dennis Guse 58d4ee7d7d Merge pull request #1372 from OpenTracksApp/cadence#1352
Cycling cadence from powermeter
2022-09-10 23:10:58 +02:00
dependabot[bot] 0e93a304f0 Bump appcompat from 1.5.0 to 1.5.1
Bumps appcompat from 1.5.0 to 1.5.1.

---
updated-dependencies:
- dependency-name: androidx.appcompat:appcompat
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-07 22:24:38 +02:00
dependabot[bot] 0adc4c7986 Bump mockito-android from 4.7.0 to 4.8.0
Bumps [mockito-android](https://github.com/mockito/mockito) from 4.7.0 to 4.8.0.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v4.7.0...v4.8.0)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-android
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-07 22:20:51 +02:00
Dennis Guse a93424629d BLE: process cycling cadence from powermeter.
Fixes #1352.
2022-09-04 19:55:32 +02:00
Dennis Guse da29766fd0 BLE: BluetoothConnectionManager supports multiple BLE services per sensor type (e.g., cadence).
Part of #1352.
2022-09-04 19:11:31 +02:00
Dennis Guse dc7afbd07f BLE: extract SensorDataCycling.
Part of #1352.
2022-09-04 12:11:09 +02:00
Dennis Guse 67b174feac BLE: do not try to connect twice.
Might had some interesting side effects.
Part of #1352.
2022-09-04 12:11:09 +02:00
Dennis Guse b6b81b5d3d BLE: extract sensor managers into separate files.
Part of #1352.
2022-09-04 12:11:09 +02:00
Dennis Guse 7d804b603d BLE: decode cadence from cycling powermeter.
Part of #1352.
2022-09-04 12:11:09 +02:00
Dennis Guse 8696a15ce7 Cleanup: introduced ServiceMeasurementUUID.
Part of #1352.
2022-09-04 11:54:26 +02:00
Dennis Guse 989fe39849 Cleanup: BLE preferences.
Part of #1352.
2022-09-04 11:31:42 +02:00
Dennis Guse ed1e432691 Merge remote-tracking branch 'weblate/main' 2022-09-03 23:58:15 +02:00
Dennis Guse 4f612e32d6 Voice announcements: "speed" -> "average moving speed" (en only).
Fixes #1346.
2022-09-03 23:26:09 +02:00
Dennis Guse e5d1e421a4 Release: v4.0.8 2022-09-03 10:21:38 +02:00
Surfoo 08ab8cc61d Translated using Weblate (French)
Currently translated at 100.0% (501 of 501 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/fr/
2022-08-28 17:01:16 +02:00
Dennis Guse c3b30815c8 SplashScreen: use Androidx library.
Improvement for Android 12+.
Fixes #1365.
2022-08-28 17:01:10 +02:00
Dennis Guse 5a4919efd2 PublicAPI: support setting the icon.
Fixes #1282.
2022-08-28 15:17:42 +02:00
dependabot[bot] bb549fdc93 Bump desugar_jdk_libs from 1.2.0 to 1.2.2
Bumps [desugar_jdk_libs](https://github.com/google/desugar_jdk_libs) from 1.2.0 to 1.2.2.
- [Release notes](https://github.com/google/desugar_jdk_libs/releases)
- [Changelog](https://github.com/google/desugar_jdk_libs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/desugar_jdk_libs/commits)

---
updated-dependencies:
- dependency-name: com.android.tools:desugar_jdk_libs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-25 21:58:51 +02:00
Dennis Guse 73e59cbba2 UI: show timezone if different than local.
Fixes #1296.
2022-08-22 18:43:53 +02:00
Dennis Guse 27727b6dcf Merge pull request #1310 from OpenTracksApp/time
Refactoring things regarding Duration, Speed, and Distance
2022-08-21 23:25:44 +02:00
Dennis Guse 06d2957407 Desugar: upgrade to AGP 7.3.0-rc01
Required for desugar 1.2.0)
2022-08-21 23:25:06 +02:00
Dennis Guse 8b08af6bcf Upgrade to Gradlew 7.5.1 2022-08-21 22:02:49 +02:00
Dennis Guse 78dad95ef6 Altitude: show always in m/ft.
Fixes #1319.
2022-08-21 22:02:49 +02:00
Dennis Guse be9d56721e Preferences: nicer formattings for distances. 2022-08-21 22:02:49 +02:00
Dennis Guse e5785dd3ac Distance now does all conversions itself. 2022-08-21 22:02:49 +02:00
Dennis Guse 11d912d14d Speed: use Distance for conversions. 2022-08-21 22:02:49 +02:00
Dennis Guse 807ac3fc9f Remove UnitConversions for time/durations. 2022-08-21 22:02:49 +02:00
Dennis Guse 45874d3749 Release: v4.0.7 2022-08-21 21:55:19 +02:00
Peter Storch 594f69320a Fix dependencies to lifecycle-viewmodel 2022-08-21 19:28:07 +02:00
dependabot[bot] d4927c9444 Bump appcompat from 1.4.2 to 1.5.0
Bumps appcompat from 1.4.2 to 1.5.0.

---
updated-dependencies:
- dependency-name: androidx.appcompat:appcompat
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-21 19:28:07 +02:00
Alexander Ivanov 00dbd056d6 Translated using Weblate (Russian)
Currently translated at 99.0% (496 of 501 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/ru/
2022-08-21 18:53:26 +02:00
M K 8f19ad8f66 Translated using Weblate (Arabic)
Currently translated at 53.6% (269 of 501 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/ar/
2022-08-21 18:53:26 +02:00
mondstern 2fdad54a7c Translated using Weblate (German)
Currently translated at 94.8% (475 of 501 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/de/
2022-08-21 18:53:26 +02:00
Peter Storch 5a5f040ed5 Fix #1340 mass track deletion time 2022-08-21 18:53:20 +02:00
Dennis Guse 0ada9a2463 Android13: request NOTIFICATION_POST permission.
Fixes #1353.
Part of #1210.
2022-08-18 08:07:37 +02:00
Dennis Guse 719bb981ec Bugfix: Public API stop does not start service in foreground.
Fixes #1354.
2022-08-17 18:43:27 +02:00
dependabot[bot] 3c95d8744d Bump mockito-android from 4.6.1 to 4.7.0
Bumps [mockito-android](https://github.com/mockito/mockito) from 4.6.1 to 4.7.0.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v4.6.1...v4.7.0)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-android
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-15 07:59:40 +02:00
Dennis Guse 63d1c9ee5e GPS: set signal lost threshold to 30s. 2022-08-14 20:18:37 +02:00
Dennis Guse 1ccd2e3c0a Release: v4.0.6 2022-08-07 19:47:54 +02:00
Dennis Guse cdbddabc1b Cleanup: use ContextCompat.getColor() 2022-08-04 22:11:44 +02:00
Dennis Guse 9f6ca83f27 Bug: PublicAPI needs to call startForegroundService (SDK API26+).
Fixes #1301.
2022-08-04 22:11:44 +02:00
dependabot[bot] f2ee99a477 Bump gradle from 7.2.1 to 7.2.2
Bumps gradle from 7.2.1 to 7.2.2.

---
updated-dependencies:
- dependency-name: com.android.tools.build:gradle
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-04 06:44:02 +02:00
Dennis Guse 7009ede095 EGM2008 offset lookup for negative longitudes was wrong. 2022-08-03 17:30:31 +02:00
Dennis Guse 2b530a1c53 Cleanup: unused imports. 2022-08-02 18:44:15 +02:00
Poussinou 33673f5921 Update FUNDING.yml 2022-07-31 14:59:56 +02:00
Dennis Guse a7398d43e3 Release: v4.0.5 2022-07-29 17:58:32 +02:00
Dennis Guse 59b016a4a3 PlayStore: use an inverted icon (app launcher only).
Part of #1329.
2022-07-29 17:56:28 +02:00
Dennis Guse 78aa5d51c6 Intervals: if gain/loss is not available show "-" instead of null.
Fixes #1338.
2022-07-26 22:30:08 +02:00
Quang Trung 95f584b554 Translated using Weblate (Vietnamese)
Currently translated at 100.0% (501 of 501 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/vi/
2022-07-21 15:04:36 +02:00
Dennis Guse cf759d526d README_TESTED_SENSORS.md: we now store sensor data without movement.
Part of #1088.
2022-07-18 07:35:46 +02:00
Dennis Guse 4c44ea12a4 Release: v4.0.4 2022-07-15 21:41:46 +02:00
dependabot[bot] 9d09865907 Bump reactivecircus/android-emulator-runner from 2.24.0 to 2.25.0
Bumps [reactivecircus/android-emulator-runner](https://github.com/reactivecircus/android-emulator-runner) from 2.24.0 to 2.25.0.
- [Release notes](https://github.com/reactivecircus/android-emulator-runner/releases)
- [Changelog](https://github.com/ReactiveCircus/android-emulator-runner/blob/main/CHANGELOG.md)
- [Commits](https://github.com/reactivecircus/android-emulator-runner/compare/v2.24.0...v2.25.0)

---
updated-dependencies:
- dependency-name: reactivecircus/android-emulator-runner
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-13 07:39:30 +02:00
Dennis Guse 5667d2d83e Store sensor data without distance.
Fixes #1088.
2022-07-11 22:56:11 +02:00
Dennis Guse 386f017fb7 VoiceAnnouncement: avoid using deprecated API. 2022-07-10 21:46:15 +02:00
Román Ginés 87594e822d Bugfixed: adding a mark with a photo after rotating in camera resulted in a crash. Fixes #1321. 2022-07-10 19:47:45 +02:00
Dennis Guse ee832c7bd2 Revert upgrade ot AGP 7.3.0-beta. 2022-07-10 17:59:32 +02:00
Dennis Guse 30042d5bc3 Merge branch 'translations' 2022-07-09 19:49:28 +02:00
Hiajen 73cbb7a29c Translated using Weblate (German)
Currently translated at 75.5% (68 of 90 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/app-store-metadata-files/de/
2022-07-09 19:48:52 +02:00
Hiajen c100fdde4f Translated using Weblate (German)
Currently translated at 94.8% (475 of 501 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/de/
2022-07-09 19:48:52 +02:00
Dennis Guse 04e1496084 Cleanup: adjusted Interval uses constructor.
Fixes #1320.
2022-07-09 15:52:57 +02:00
Dennis Guse 21d1852505 Test: import+export results in same timezone.
Part of #1296.
2022-07-08 00:16:48 +02:00
Dennis Guse 1502b0cfa7 Gain/loss: handle events in our TrackRecordingService.handler (avoids potential race conditions). 2022-07-07 18:27:56 +02:00
Dennis Guse e7d6f65508 Gain/loss: use 5s as sensor interval to save some energy. 2022-07-07 18:27:56 +02:00
Dennis Guse 0e69a444a5 Add a comma. 2022-07-07 18:26:27 +02:00
Román Ginés 3b62999fde Announcement computes intervals from last track point. Fixes #645.
Bugfixed: intervals distance wasn't computed correctly.
2022-07-06 22:54:45 +02:00
Dennis Guse 97431233ac Fix translations. 2022-07-05 21:01:09 +02:00
Uglješa d49675afc8 Translated using Weblate (Serbian)
Currently translated at 56.6% (284 of 501 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/sr/
2022-07-03 18:29:42 +02:00
vsevjednom-cz c1f7ab36a8 Translated using Weblate (Czech)
Currently translated at 94.2% (472 of 501 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/cs/
2022-07-03 18:29:42 +02:00
FTno eb14cc7bc6 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (500 of 500 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/nb_NO/
2022-07-03 18:29:42 +02:00
Xosé M a47b9164af Translated using Weblate (Galician)
Currently translated at 100.0% (500 of 500 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/gl/
2022-07-03 18:29:42 +02:00
Surfoo 502e3ecb21 Translated using Weblate (French)
Currently translated at 100.0% (500 of 500 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/fr/
2022-07-03 18:29:42 +02:00
metezd 71903f174a Translated using Weblate (Turkish)
Currently translated at 98.0% (490 of 500 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/tr/
2022-07-03 18:29:41 +02:00
Dennis Guse ab6606a295 Github actions: rename APK build job. 2022-07-03 18:29:38 +02:00
Dennis Guse c4e2023992 Public API: allow setting track properties (name, category, description) on StartRecording.
Fixes #1282.
2022-07-03 17:32:21 +02:00
Dennis Guse cb10bdda3f Settings: show package id in Public API settings. 2022-07-03 10:28:43 +02:00
Dennis Guse f913472aae Settings: idle speed threshold has now a summary. 2022-07-03 10:28:31 +02:00
Dennis Guse af4826c578 TrackStoppedActivity: update track data on resume track.
Fixes #1249.
2022-07-02 08:51:31 +02:00
Dennis Guse 63172ec66f Github Actions: downgrade triggers. 2022-07-01 20:40:38 +02:00
Dennis Guse 3d6c339509 Android13: use new Image picker.
Part of #1210.
2022-07-01 20:24:20 +02:00
Dennis Guse 49f86c4eb9 Android13: upgrade target sdk to 33.
Part of #1210.
2022-07-01 20:24:20 +02:00
Dennis Guse 7664bd248d Release: v4.0.3 2022-07-01 13:27:50 +02:00
Dennis Guse 73615aa0d6 Statistics: remove speed filtering (fix tests).
Part of #1003.
2022-07-01 13:24:18 +02:00
Dennis Guse 81bb6699d8 Statistics: remove speed filtering.
Part of #1003.
2022-06-30 14:03:33 +02:00
dependabot[bot] 93c67e3728 Bump desugar_jdk_libs from 1.1.5 to 1.1.6
Bumps [desugar_jdk_libs](https://github.com/google/desugar_jdk_libs) from 1.1.5 to 1.1.6.
- [Release notes](https://github.com/google/desugar_jdk_libs/releases)
- [Changelog](https://github.com/google/desugar_jdk_libs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/desugar_jdk_libs/commits)

---
updated-dependencies:
- dependency-name: com.android.tools:desugar_jdk_libs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-29 08:45:59 +02:00
Dennis Guse 993816a34a Github issue template for data not recorded.
Fixes #1295.
2022-06-28 16:18:41 +02:00
Šimon Hořánek 3c0999c3a0 Translated using Weblate (Czech)
Currently translated at 94.3% (471 of 499 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/cs/
2022-06-27 23:03:27 +02:00
metezd 4528e7148f Translated using Weblate (Turkish)
Currently translated at 98.1% (490 of 499 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/tr/
2022-06-27 23:03:27 +02:00
FTno 74042f5d85 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (499 of 499 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/nb_NO/
2022-06-27 23:03:27 +02:00
Dennis Guse 92f6fbe04a Prevent crash if no dashboard is installed. 2022-06-27 23:03:23 +02:00
Dennis Guse 5fa50250de Cleanup tests for StringUtils.parseTime().
Part of #1295.
2022-06-27 19:30:23 +02:00
Dennis Guse 8ee9daf7af Github issue template for data not recorded.
Fixes #1295.
2022-06-27 12:58:17 +02:00
Dennis Guse a54c8c1d25 Use Set.of(). 2022-06-22 20:31:41 +02:00
Dennis Guse 8137ec8de2 GPX exporter: only export trkseq if it actually contains data.
Fixes #1291.
2022-06-21 19:56:19 +02:00
Dennis Guse ae2a97576b Release: v4.0.2 2022-06-20 22:06:01 +02:00
Šimon Hořánek 3e830a086e Replace Start with Stop 2022-06-19 21:03:56 +02:00
Surfoo 618a1df3ea Translated using Weblate (French)
Currently translated at 82.9% (73 of 88 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/app-store-metadata-files/fr/
2022-06-19 17:34:42 +02:00
Surfoo 90cfc67c59 Translated using Weblate (French)
Currently translated at 100.0% (487 of 487 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/fr/
2022-06-19 17:34:42 +02:00
Dennis Guse cecc586868 Bugfix: export was not checking properly if file exists.
Fixes #1283.
2022-06-19 17:24:18 +02:00
Dennis Guse f956e7fab3 Duplicate variable name. 2022-06-19 17:21:56 +02:00
William Pettersson 128b95cd83 Fix voice announcement rounding problem
We extracted integral parts before rounding, but String.format does
rounding which caused occasional issues. To alleviate, we need to round
before passing to String.format. All this rounding logic has now been
moved into appendDecimalUnit, and tests have been added to check that
rounding works correctly.

Fixes: #1285
2022-06-19 16:39:46 +02:00
Román Ginés 895a5c2b2a Bugfixed: DistanceFormatter was using the wrong unitSystem. Fixes #1288. 2022-06-18 23:18:04 +02:00
Dennis Guse 91ce5cf8d7 Add nautical miles (with feet for altitude).
Tests were broken.

Fixes #1280.
2022-06-18 10:01:09 +02:00
Dennis Guse a283464331 Add nautical miles (with feet for altitude).
Fixes #1280.
2022-06-17 19:57:40 +02:00
Dennis Guse 1c3a2bd2e4 Removed altitude smoothing completely: now it will just be the plain GPS-reported value.
Was only used for statistics in exports.

Follow-up to #1241.
2022-06-11 14:41:56 +02:00
Dennis Guse 0b326a77c2 Bugfix: Elevation in charts smoothing removed as it was broken.
Fixes #1241.
2022-06-11 14:24:41 +02:00
Dennis Guse d68008941e Release: v4.0.1 2022-06-09 22:55:36 +02:00
Dennis Guse b822d05a78 Fix voice announcement tests. 2022-06-09 22:33:43 +02:00
Dennis Guse 74bfed768c Translations: removed obsolete ones. 2022-06-09 21:01:08 +02:00
arjan-s ebfcce6d8d Translated using Weblate (Dutch)
Currently translated at 100.0% (487 of 487 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/nl/
2022-06-09 16:04:29 +02:00
Hiajen d97d8f85e4 Translated using Weblate (German)
Currently translated at 96.9% (472 of 487 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/de/
2022-06-09 16:04:28 +02:00
FTno 37c2c5029e Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (482 of 482 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/nb_NO/
2022-06-07 09:55:18 +02:00
mondstern 105ff89fd8 Translated using Weblate (Romanian)
Currently translated at 84.4% (407 of 482 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/ro/
2022-06-07 09:55:18 +02:00
mondstern 0ac033106c Translated using Weblate (Hungarian)
Currently translated at 91.7% (442 of 482 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/hu/
2022-06-07 09:55:18 +02:00
mondstern 21ec9fc568 Translated using Weblate (Finnish)
Currently translated at 96.6% (466 of 482 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/fi/
2022-06-07 09:55:18 +02:00
mondstern 2e3a7a31d4 Translated using Weblate (Hungarian)
Currently translated at 82.5% (398 of 482 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/hu/
2022-06-07 09:55:17 +02:00
Román Ginés ecd7405a8c TrackStoppedActivity: added discard button option. Fixes #1267. 2022-06-07 09:55:09 +02:00
Dennis Guse 28abe360cb Merge pull request #1276 from OpenTracksApp/voiceConfig#1275
Added settings for what to announce
2022-06-06 17:04:15 +02:00
Dennis Guse 9cf88c00b0 Voice announcements: time is moving time (settings). 2022-06-06 17:02:31 +02:00
Dennis Guse 8bbde4aacd Voice announcements: fix translations 2022-06-06 17:02:31 +02:00
Jonas Kalderstam 00351136f2 Voice announcements: added settings for what to announce 2022-06-06 17:02:31 +02:00
dependabot[bot] 30b85bd215 Bump mockito-android from 4.6.0 to 4.6.1
Bumps [mockito-android](https://github.com/mockito/mockito) from 4.6.0 to 4.6.1.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v4.6.0...v4.6.1)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-android
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-04 09:40:55 +02:00
Dennis Guse 14b705b43e README: add large translation status; removed Amazon App Store link. 2022-06-04 09:22:51 +02:00
Dennis Guse 31fc0adabc Fullscreen: now more fullscreen. 2022-06-03 17:26:33 +02:00
Dennis Guse 1714da5d3b Translations: fix quantities (fr). 2022-06-02 22:12:00 +02:00
mondstern 6ae47b5e01 Translated using Weblate (Slovenian)
Currently translated at 93.3% (450 of 482 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/sl/
2022-06-02 22:06:14 +02:00
metezd df8e4c2e2e Translated using Weblate (Turkish)
Currently translated at 99.1% (478 of 482 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/tr/
2022-06-02 20:00:22 +02:00
Surfoo 6f9fa3719a Translated using Weblate (French)
Currently translated at 100.0% (482 of 482 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/fr/
2022-06-02 20:00:21 +02:00
vsevjednom-cz b9014c7219 Translated using Weblate (Czech)
Currently translated at 98.7% (483 of 489 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/cs/
2022-06-02 20:00:21 +02:00
FTno d44ab44620 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (502 of 502 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/nb_NO/
2022-06-02 20:00:21 +02:00
dependabot[bot] bcffe05744 Bump appcompat from 1.4.1 to 1.4.2
Bumps appcompat from 1.4.1 to 1.4.2.

---
updated-dependencies:
- dependency-name: androidx.appcompat:appcompat
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-02 20:00:06 +02:00
Dennis Guse ed60734868 Donations: we do not accept Bitcoin anymore (too much effort). 2022-06-01 20:17:02 +02:00
dependabot[bot] e314ff8f43 Bump material from 1.6.0 to 1.6.1
Bumps [material](https://github.com/material-components/material-components-android) from 1.6.0 to 1.6.1.
- [Release notes](https://github.com/material-components/material-components-android/releases)
- [Commits](https://github.com/material-components/material-components-android/compare/1.6.0...1.6.1)

---
updated-dependencies:
- dependency-name: com.google.android.material:material
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-31 22:48:36 +02:00
Peter Storch 1108684d06 Add "show on map" format selection dialog 2022-05-30 07:51:00 +02:00
dependabot[bot] 6c0033c107 Bump mockito-android from 4.5.1 to 4.6.0
Bumps [mockito-android](https://github.com/mockito/mockito) from 4.5.1 to 4.6.0.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v4.5.1...v4.6.0)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-android
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-30 07:35:00 +02:00
Dennis Guse 8a3e50ef9b BLE sensors: faster connection. 2022-05-29 22:50:55 +02:00
Román Ginés 05b572253e Bugfixes: buttons colors were handcoded. Fixes #1260. 2022-05-29 20:25:18 +02:00
Dennis Guse f755a086cd Cleanup: removed unused translations. 2022-05-29 20:22:50 +02:00
Dennis Guse b0605323da Track list: RTL fix. 2022-05-29 18:15:41 +02:00
Dennis Guse 585c2234eb Bugfix: request permissions in TrackListActivity.
Introduced in 9d64989ef1
2022-05-29 18:06:00 +02:00
Román Ginés c5d7ea4245 Bugfixes: TrackStoppedActivity on smaller devices. Fixes #1257. 2022-05-29 18:02:57 +02:00
Dennis Guse 281cd8a861 Merge pull request #1248 from OpenTracksApp/reset#1247
Allow settings reset and import/export while recording
2022-05-28 13:48:56 +02:00
Dennis Guse 36400c8ad0 Fix EspressoUITest for API24. 2022-05-26 23:39:35 +02:00
Dennis Guse fb00f697c5 Fix EspressoDeleteTrackTest.
Fixes #817.
2022-05-26 23:39:35 +02:00
Dennis Guse f529726218 KML: icons (start, stop, markers) use default icon.
Until now we used external URLs from Google to fetch proper icons.

Fixes #126.
2022-05-26 10:23:27 +02:00
Dennis Guse 059f3d8f43 Cleanup: align test classes name to tested class. 2022-05-26 10:23:27 +02:00
dependabot[bot] cae716925f Bump gradle from 7.2.0 to 7.2.1
Bumps gradle from 7.2.0 to 7.2.1.

---
updated-dependencies:
- dependency-name: com.android.tools.build:gradle
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-26 08:22:07 +02:00
Dennis Guse d29ee6da3d Settings: allow import/export settings while recording.
Fixes #1247.
2022-05-25 22:03:16 +02:00
Dennis Guse 033b5211ea Settings: allow reset settings while recording.
Fixes #1247.
2022-05-25 22:01:51 +02:00
Dennis Guse 5f4f5baba4 Remove unused settings (formerly used in settings). 2022-05-25 07:38:12 +02:00
Dennis Guse 4421046556 Remove unused settings (incl. deprecated ones). 2022-05-25 07:38:12 +02:00
Dennis Guse 30482d5a34 Record/stop buttons: add contentDescription. 2022-05-25 07:38:12 +02:00
Dennis Guse f12094c953 Remove unused translations (were used for voice announcements). 2022-05-25 07:38:12 +02:00
Dennis Guse c5147e7664 UI tests: use longClick() instead of veryLongClick(). 2022-05-25 07:38:12 +02:00
Dennis Guse 03b66bc6a2 Remove unused xml configuration. 2022-05-25 07:38:12 +02:00
Dennis Guse 7b40f2c949 Remove unused buttons.
Replaced in da62d26409
2022-05-25 07:38:12 +02:00
Dennis Guse 785a922d03 Remove empty translations. 2022-05-25 07:38:12 +02:00
Dennis Guse 5d2414977b TrackRecordingService: remove pause functionality.
Fixes #1233.
2022-05-23 07:41:50 +02:00
Román Ginés f9b8e46b32 Release: v4.0.0 2022-05-22 21:53:13 +02:00
Dennis Guse c79fc2f353 Change track title capitalization to sentences 2022-05-22 21:12:38 +02:00
gerberfg e283e993bb Change track title capitalization to sentences 2022-05-22 21:09:57 +02:00
Egor Ermakov 3f1e74e71e Translated using Weblate (Russian)
Currently translated at 100.0% (502 of 502 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/ru/
2022-05-21 21:04:10 +02:00
mondstern 300453e3e6 Translated using Weblate (Dutch)
Currently translated at 100.0% (502 of 502 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/nl/
2022-05-21 21:04:10 +02:00
Dennis Guse ac8eea377e Bugfix: resume functionality was not starting recording.
Introduced in 9d64989ef1
2022-05-21 09:16:43 +02:00
dependabot[bot] cc44e8e9bf Bump constraintlayout from 2.1.3 to 2.1.4
Bumps [constraintlayout](https://github.com/androidx/constraintlayout) from 2.1.3 to 2.1.4.
- [Release notes](https://github.com/androidx/constraintlayout/releases)
- [Commits](https://github.com/androidx/constraintlayout/commits)

---
updated-dependencies:
- dependency-name: androidx.constraintlayout:constraintlayout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-20 07:05:15 +02:00
mm4c 3860472e6b Translated using Weblate (Dutch)
Currently translated at 87.2% (75 of 86 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/app-store-metadata-files/nl/
2022-05-19 19:30:48 +02:00
metezd 69029486b6 Translated using Weblate (Turkish)
Currently translated at 98.9% (494 of 499 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/tr/
2022-05-19 19:30:48 +02:00
Adriel Sand b407f86d5e Translated using Weblate (Russian)
Currently translated at 100.0% (499 of 499 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/ru/
2022-05-19 19:30:48 +02:00
Adriel Sand 731adc7e31 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (499 of 499 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/pt_BR/
2022-05-19 19:30:48 +02:00
mondstern 858149e718 Translated using Weblate (Dutch)
Currently translated at 100.0% (499 of 499 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/nl/
2022-05-19 19:30:48 +02:00
mondstern 34e796f73c Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (499 of 499 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/nb_NO/
2022-05-19 19:30:47 +02:00
Dennis Guse 3ac71361b8 Launcher icon: provide a themed icon (Android 13). 2022-05-19 19:30:43 +02:00
Dennis Guse e409b315b4 Refactor permission requests.
Fixes #1224.
2022-05-19 19:30:20 +02:00
Dennis Guse 24eb768707 Export: filename template now distinguishes between {date} and {time}. 2022-05-19 19:28:15 +02:00
Peter Storch 17ad13d559 Add ExceptionHandler and ShowErrorActivity 2022-05-19 06:12:58 +02:00
Dennis Guse 3b71212905 Public API: respect post-workout export.
Fixes #1225.
2022-05-15 20:19:59 +02:00
Dennis Guse 16a29c5e31 Bugfix: for a recording Track TrackListActivity needs to open TrackRecordingActivity.
Introduced in 9d64989ef1

Fixes #1235.
2022-05-15 08:00:35 +02:00
Dennis Guse e421164282 Bugfix: fileformat template was not loaded (NPE).
Fixes #1232.
2022-05-13 19:28:43 +02:00
Pixtxa de01157964 Changed second t.getLatitude() to t.getLongitude()
I thought it was excel until I realized, the data is also broken in newly generated csv files
2022-05-12 22:30:01 +02:00
Dennis Guse 8ef51ec318 Bugfix: unitSystem not loaded correctly.
Introduced in a5b44175a3

Fixes #1228.
2022-05-12 07:44:34 +02:00
Dennis Guse d2139d70ad Bugfix: sharing track added file suffix twice.
Introduced in 0d3cf4ce0d.
2022-05-11 22:22:45 +02:00
metezd d1a37366be Translated using Weblate (Turkish)
Currently translated at 79.0% (68 of 86 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/app-store-metadata-files/tr/
2022-05-11 20:56:56 +02:00
metezd 52d7c8a6cb Translated using Weblate (Turkish)
Currently translated at 79.5% (397 of 499 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/tr/
2022-05-11 20:56:56 +02:00
metezd c62e56e5a3 Translated using Weblate (Turkish)
Currently translated at 78.1% (390 of 499 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/tr/
2022-05-11 20:56:56 +02:00
Xosé M 6b89092adf Translated using Weblate (Galician)
Currently translated at 100.0% (499 of 499 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/gl/
2022-05-11 20:56:56 +02:00
Mario 118a56b8d6 Translated using Weblate (Spanish)
Currently translated at 100.0% (499 of 499 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/es/
2022-05-11 20:56:56 +02:00
Mario 7fb993014c Translated using Weblate (Spanish (Latin America))
Currently translated at 100.0% (499 of 499 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/es_419/
2022-05-11 20:56:56 +02:00
Dennis Guse 09f72af66c Deleted translation using Weblate (Sinhala) 2022-05-11 20:56:56 +02:00
Dennis Guse 815f111ca6 TrackRecordingService: use main looper explicitly. 2022-05-11 20:56:52 +02:00
Dennis Guse 40d2bf0c22 Bluetooth: use TrackRecording handler for callback.
Otherwise the thread is not defined and a SecurityException may be triggered when inserting data into the ContentProvider.
Sadly, below API26 this needs to be done manually.

Fixes #1165.
2022-05-11 20:56:52 +02:00
Dennis Guse a77b7a0626 Update Android build gradle plugin to 7.2.0 2022-05-11 19:59:02 +02:00
Dennis Guse 1a00b4b779 UI tests slightly more robust. 2022-05-11 19:59:02 +02:00
Dennis Guse 6c5c47240c Move to androidx.constraintlayout. 2022-05-11 19:59:02 +02:00
Dennis Guse 3c32da970b Upgrade to gradlew 7.4.2 2022-05-09 22:35:50 +02:00
Román Ginés 9cff62c5ba New screenshots after UI refactoring. 2022-05-09 20:38:25 +02:00
dependabot[bot] 424f93b4da Bump material from 1.5.0 to 1.6.0
Bumps [material](https://github.com/material-components/material-components-android) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/material-components/material-components-android/releases)
- [Commits](https://github.com/material-components/material-components-android/compare/1.5.0...1.6.0)

---
updated-dependencies:
- dependency-name: com.google.android.material:material
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-08 20:53:53 +02:00
Dennis Guse 3a23a412c8 Cleanup: use explicit preference identifiers for TrackFileFormat. 2022-05-08 20:49:24 +02:00
Dennis Guse 9d64989ef1 Public API: implemented STOP properly.
Fixes #1103.
2022-05-08 20:29:13 +02:00
Román Ginés da62d26409 UI refactoring with a Bottom App Bar.
Only one button to play/stop.
Stop Activity to edit track just recorded, resuming or finishing.
Logo color to FF6D00.
Fixes #1078 #437 #575 #1094.
2022-05-08 20:06:24 +02:00
Dennis Guse a350077e5c Merge remote-tracking branch 'weblate/main' 2022-05-08 11:22:26 +02:00
Dennis Guse 36b6cd3c98 Release: v3.28.0 2022-05-08 11:16:19 +02:00
metezd 203b457ad0 Translated using Weblate (Turkish)
Currently translated at 60.5% (302 of 499 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/tr/
2022-05-08 10:59:46 +02:00
metezd 0873b3bd12 Translated using Weblate (Turkish)
Currently translated at 57.1% (285 of 499 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/tr/
2022-05-08 10:59:46 +02:00
Dennis Guse a7f7e3fca1 Release: v3.28.0 2022-05-08 10:59:27 +02:00
Dennis Guse a52beffef5 Translations: fix quantities. 2022-05-06 06:36:49 +02:00
Yaxi Ye e6f6d61b84 Translated using Weblate (Chinese (Simplified))
Currently translated at 64.7% (323 of 499 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/zh_Hans/
2022-05-04 20:18:51 +02:00
Vinicius 4a167c9335 Translated using Weblate (Portuguese (Brazil))
Currently translated at 82.3% (70 of 85 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/app-store-metadata-files/pt_BR/
2022-05-04 20:18:51 +02:00
Vinicius 16857a9968 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (498 of 498 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/pt_BR/
2022-05-04 20:18:51 +02:00
Dennis Guse 094a20c379 Vertical accuracy: process and import/export.
Fixes #78.
2022-05-04 20:18:45 +02:00
Dennis Guse 0d3cf4ce0d Configurable filename for export: used for all automatic exports after recording, exporting all, and sharing tracks.
Fixes #512.
2022-05-03 20:06:29 +02:00
dependabot[bot] 8a2d1c42a8 Bump reactivecircus/android-emulator-runner from 2.23.0 to 2.24.0
Bumps [reactivecircus/android-emulator-runner](https://github.com/reactivecircus/android-emulator-runner) from 2.23.0 to 2.24.0.
- [Release notes](https://github.com/reactivecircus/android-emulator-runner/releases)
- [Changelog](https://github.com/ReactiveCircus/android-emulator-runner/blob/main/CHANGELOG.md)
- [Commits](https://github.com/reactivecircus/android-emulator-runner/compare/v2.23.0...v2.24.0)

---
updated-dependencies:
- dependency-name: reactivecircus/android-emulator-runner
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-02 07:16:58 +02:00
Dennis Guse a5b44175a3 Convert metricUnits boolean into an enum.
Fixes #1199.
2022-04-28 22:35:38 +02:00
Dennis Guse db5c9479df Implement SpeedFormatter. 2022-04-28 22:35:38 +02:00
Dennis Guse 16f238f34b Refactoring: DistanceFormatter handles metricUnits. 2022-04-28 22:35:38 +02:00
Dennis Guse a57b5cd6ee From notification, open TrackRecordingActivity.
Fixes #1205.
2022-04-26 18:31:14 +02:00
Román Ginés 5e54cb8fda Bugfixed: intervals dropdown shows incorrect unit when metric units is selected.
Fixes #1201.
2022-04-24 19:23:44 +02:00
347 changed files with 27528 additions and 6798 deletions
@@ -0,0 +1,24 @@
---
name: Data not recorded
about: Somehow OpenTracks did not record GPS data
title: ''
labels: potential bug
assignees: ''
---
**Please describe the problem and how to reproduce it:**
...
**Checklist**
- [ ] Consulted [https://dontkillmyapp.com/]
- [ ] Power management: battery optimization is disabled for OpenTracks
**Technical information**
- Device: [e.g. Nexus 7 (2013)]
- OS: [e.g. LineageOS 15.1]
- OpenTracks version: [e.g. v3.2.4]
- OpenTracks commit id (only for nightly builds): [e.g. 4b333bb]
@@ -1,9 +1,10 @@
#https://github.com/marketplace/actions/android-emulator-runner
name: Build nightly
on: [ push ]
name: Build APK
on:
push
jobs:
nightly-build:
apk-build:
runs-on: ubuntu-latest
steps:
- name: checkout
@@ -14,7 +15,7 @@ jobs:
distribution: 'adopt'
java-version: '11'
- name: Assemble nighlty APK
- name: Assemble APK
shell: bash
run: |
echo ${NIGHTLY_STORE_FILE} | base64 -d > KEY_NIGHTLY.jks
@@ -28,7 +29,7 @@ jobs:
if: always()
uses: actions/upload-artifact@v3
with:
name: OpenTracks-nightly.apk
name: OpenTracks-APK.apk
path: ./build/outputs/apk/nightly/*.apk
retention-days: 7
+1 -1
View File
@@ -2,7 +2,7 @@ name: Linting
on:
push:
pull_request_target:
pull_request:
jobs:
analyse:
+2 -2
View File
@@ -2,7 +2,7 @@
name: Test
on:
push:
pull_request_target:
pull_request:
jobs:
test:
@@ -23,7 +23,7 @@ jobs:
java-version: '11'
- name: run tests
uses: reactivecircus/android-emulator-runner@v2.23.0
uses: reactivecircus/android-emulator-runner@v2.26.0
with:
api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }}
-1
View File
@@ -60,7 +60,6 @@ captures/
.idea/encodings.xml
.idea/jsLibraryMappings.xml
.idea/libraries/
.idea/misc.xml
.idea/modules.xml
.idea/scopes/scope_settings.xml
.idea/shelf/
+47 -46
View File
@@ -9,7 +9,6 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
<th>Free (F-Droid)</th>
<th>Free (Nightly for F-Droid)</th>
<th>Donations</th>
<th>Translations</th>
</tr>
<tr>
<td align="center">
@@ -27,47 +26,26 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
<img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg" height="45" align="middle">
</a>
</td>
<td align="center">
<a href="https://weblate.bubu1.eu/engage/OpenTracks/">
<img src="https://weblate.bubu1.eu/widgets/OpenTracks/-/opentracks/svg-badge.svg" alt="Translation status" />
</a>
</td>
</tr>
<tr>
<td align="center">
<img alt="OpenTracks version published on F-Droid" src="https://img.shields.io/f-droid/v/de.dennisguse.opentracks.svg" align="middle" >
</td>
<td></td>
<td align="center">
<a href="bitcoin:bc1qckpls3lpjlcpyxzm66hcy65hnesas9zgacy6yc">
<img alt="bitcoin:bc1qckpls3lpjlcpyxzm66hcy65hnesas9zgacy6yc" src="promotion/donations-bitcoin.png" height="90" align="middle">
</a>
</td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td align="center">
<a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">
<img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" height="60" align="middle">
</a>
</td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td align="center">
<a href="https://www.amazon.de/dp/B09K2KNQ7X/">
<img alt="Get it on Amazon App Store" src="https://images-na.ssl-images-amazon.com/images/G/01/mobile-apps/devportal2/res/images/amazon-appstore-badge-english-white.png" height="40" align="middle">
</a>
</td>
<td></td>
</tr>
</table>
<a href="https://weblate.bubu1.eu/engage/OpenTracks/">
<img src="https://weblate.bubu1.eu/widgets/OpenTracks/-/opentracks/horizontal-auto.svg" alt="Translation status" />
</a>
## Screenshots
<div>
<img width="23%" src="fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot1.png">
<img width="23%" src="fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot2.png">
@@ -86,19 +64,19 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
* __Voice announcements__
* __Photos and Markers:__ mark interesting locations while tracking
* __Export:__
* export tracks either as KMZ (incl. photos), KML, or GPX
* export automatically after each recording (e.g., to sync via [Nextcloud](https://nextcloud.com/))
* avoid duplication: each exported file contain a random unique identifier (i.e., `opentracks:trackid`)
* export tracks either as [KMZ 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html) (incl. photos), [KML 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html), or [GPX 1.1](https://www.topografix.com/GPX/1/1/)
* export automatically after each recording (e.g., to sync via [Nextcloud](https://nextcloud.com/))
* avoid duplication: each exported file contain a random unique identifier (i.e., `opentracks:trackid`)
* __Altitude:__
* gain/loss via barometric sensor (if present)
* shown in EGM2008 (above mean sea level); exported as WGS84
* gain/loss via barometric sensor (if present)
* shown in EGM2008 (above mean sea level); exported as WGS84
* __Bluetooth LE sensors:__
* heart rate
* cycling: speed and distance
* cycling: cadence
* cycling: power meter
* running: speed and cadence
* support for BLE sensor training only (i.e., without GPS) for indoor training
* heart rate
* cycling: speed and distance
* cycling: cadence
* cycling: power meter
* running: speed and cadence
* support for BLE sensor training only (i.e., without GPS) for indoor training
An overview of tested sensors: [README_TESTED_SENSORS.md](README_TESTED_SENSORS.md)
@@ -117,7 +95,7 @@ OpenTracks can be used with [Gadgetbridge](https://www.gadgetbridge.org/):
__Only required permission:__
* _ACCESS_FINE_LOCATION_: required to use the GPS.
### API
### Public API
OpenTracks includes an API for starting/stopping recording by another installed application (e.g., [Automate](https://llamalab.com/automate/), [Tasker](https://tasker.joaoapps.com), or [Easer](https://github.com/renyuneyun/Easer)).
The API can be invoked by sending an explicit Intent to start an activity.
@@ -129,19 +107,42 @@ The API can be invoked by sending an explicit Intent to start an activity.
* Nightly: `de.dennisguse.opentracks.nightly`
`Class`:
* **Start a recording:** `de.dennisguse.opentracks.publicapi.StartRecording`
* **Stop a recording:** `de.dennisguse.opentracks.publicapi.StopRecording`
For testing via adb: `adb shell am start -n "package/class"`
For testing via adb: `adb shell am start -e someParameter someValue -n "package/class"`
The Start/Stop API is disabled by default to protect the user's privacy, but it can easily be enabled in the settings.
`StartRecording` supports the following parameters:
## Custom Dashboards (incl. map)
As of v3.3.1, OpenTracks supports custom dashboards for displaying previously recorded and live tracks.
* Set track data: `TRACK_NAME`, `TRACK_DESCRIPTION`, `TRACK_CATEGORY`, and `TRACK_ICON` (non-localized identifier see [/src/main/java/de/dennisguse/opentracks/util/TrackIconUtils.java#L38](/src/main/java/de/dennisguse/opentracks/util/TrackIconUtils.java#L38)).
NOTE: if `TRACK_ICON` is not present, `TRACK_CATEGORY` will be used to determine the icon (localized).
* Send recorded data to another application via _Dashboard API_: `STATS_TARGET_PACKAGE` and `STATS_TARGET_CLASS`
The reference implementation is [OSMDashboard](https://github.com/OpenTracksApp/OSMDashboard), which presents an OpenStreetMap map (showing the current track, incl. updates). The Dashboard API is also used by [Gadgetbridge](https://codeberg.org/Freeyourgadget/Gadgetbridge/) for displaying live track statistics on supported wearables.
The Public API is disabled by default to protect the user's privacy, but it can easily be enabled in the settings.
Alternatively, recorded tracks can be shared as KMZ/GPX with installed applications (e.g., [OsmAnd](https://play.google.com/store/apps/details?id=net.osmand)).
However, this is rather slow and does not provide updates while recording.
## File formats compatibility with open-source software
| | [GPX 1.1](https://www.topografix.com/GPX/1/1/) | [KML 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html) | [KMZ 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html) |
|------------------------------------------------------|------------------------------------------------|----------------------------------------------------------------------|----------------------------------------------------------------------|
| [OpenLayers 7.1.0](https://openlayers.org/) | ? | [no](https://github.com/openlayers/openlayers/issues/14104) | [no](https://github.com/openlayers/openlayers/issues/14104) |
| [Golden Cheetah 3.5](https://www.goldencheetah.org/) | ? | [no](https://github.com/GoldenCheetah/GoldenCheetah/issues/4271) | [no](https://github.com/GoldenCheetah/GoldenCheetah/issues/4271) |
| [GpxPod](https://apps.nextcloud.com/apps/gpxpod) | ? | ? | ? |
| [OsmAnd](https://github.com/osmandapp/OsmAnd) | ? | [no](https://github.com/osmandapp/OsmAnd/issues/15271) | [no](https://github.com/osmandapp/OsmAnd/issues/15271) |
## Dashboard API (incl. map)
As of v3.3.1, OpenTracks supports custom dashboards for displaying previously recorded and live
tracks.
The reference implementation is [OSMDashboard](https://github.com/OpenTracksApp/OSMDashboard), which
presents an OpenStreetMap map (showing the current track, incl. updates). The Dashboard API is also
used by [Gadgetbridge](https://codeberg.org/Freeyourgadget/Gadgetbridge/) for displaying live track
statistics on supported wearables.
Alternatively, recorded tracks can be shared as KMZ/GPX with installed applications (
e.g., [OsmAnd](https://play.google.com/store/apps/details?id=net.osmand)). However, this is rather
slow and does not provide updates while recording.
The developer documentation is in [README_API.md](README_API.md).
+12 -16
View File
@@ -2,7 +2,7 @@
## 0x180D: Heartrate Service
* Decathlon|Geonaute|Kalenji Dual HRM Belt
* Decathlon/Geonaute/Kalenji Dual HRM Belt
* moofit heart rate monitor
* Polar H7
* Polar H10
@@ -38,17 +38,13 @@ However, often only one value is provided.
We only support _Instantaneous Power_.
_WARNING_: we only support physically movements with valid GPS coordinates.
Using a device indoors (no GPS available) will only show the current power values, but won't store anything.
Also the distance is not computed.
For cycling:
For cycling (incl. cadence):
* Assioma Duo Powermeter
* Elite drivo II
* QUARQ Red DZero Powermeter
* Tacx Satori Smart
* Wahoo Kickr v4.0
* Rotor 2INpower DM Road (only supports power measurement, cadence measurement is propietary @ firmware v1.061)
* Rotor 2INpower DM Road (only supports power measurement, cadence measurement is proprietary @ firmware v1.061)
For running:
* Stryd
@@ -67,12 +63,12 @@ There are smartphones that have a barometric sensor that OpenTracks uses to comp
In our tests there are smartphones whose barometric sensor works well and others don't. Here is a list of the smartphones tested:
|Brand|Model|Android Version|Tests|
|-----|-----|---------------|-----------|
|Google|Pixel|LineageOS 17.1|Ok|
|Google|Pixel 2 XL|LineageOS 17.1|Ok on the bike handlebars but it fails when I carry it in a pocket or backpack|
|Google|Pixel 3|Android 11|Failure|
|Google|Pixel 3a|Android 11|Ok|
|Google|Pixel 3a|Lineage 18.1|Ok|
|Google|Pixel 4a|Android 11|Ok|
|Sony|Xperia XZ1 Compact|LineageOS 16.1|Failure|
| Brand | Model | Android Version | Tests |
|--------|--------------------|-----------------|--------------------------------------------------------------------------------|
| Google | Pixel | LineageOS 17.1 | Ok |
| Google | Pixel 2 XL | LineageOS 17.1 | Ok on the bike handlebars but it fails when I carry it in a pocket or backpack |
| Google | Pixel 3 | Android 11 | Failure |
| Google | Pixel 3a | Android 11 | Ok |
| Google | Pixel 3a | Lineage 18.1 | Ok |
| Google | Pixel 4a | Android 11 | Ok |
| Sony | Xperia XZ1 Compact | LineageOS 16.1 | Failure |
+1 -1
View File
@@ -5,7 +5,7 @@ set -e
# Folder where svg screenshots are expected.
SRC_DIR="screenshots-svg/"
# Folder where png files will be saved.
DST_DIR="fastlane/metadata/android/en-US/phoneScreenshots/"
DST_DIR="fastlane/metadata/android/en-US/images/phoneScreenshots/"
echo "Exporting svg files to png..."
+17 -11
View File
@@ -6,7 +6,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.3'
classpath 'com.android.tools.build:gradle:7.3.0'
}
}
@@ -51,7 +51,7 @@ android {
targetCompatibility JavaVersion.VERSION_11
}
compileSdkVersion 31
compileSdk 33
buildFeatures {
viewBinding true
@@ -59,13 +59,13 @@ android {
defaultConfig {
applicationId "de.dennisguse.opentracks"
versionCode 4816
versionName "v3.27.0"
versionCode 5051
versionName "v4.1.1"
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
minSdkVersion 21
targetSdkVersion 31
minSdk 21
targetSdk 33
testInstrumentationRunner "de.dennisguse.opentracks.TestRunner"
testInstrumentationRunnerArguments clearPackageData: 'true'
@@ -81,6 +81,8 @@ android {
}
}
namespace 'de.dennisguse.opentracks'
buildTypes {
debug {
applicationIdSuffix ".debug"
@@ -100,6 +102,7 @@ android {
releasePlayStore {
applicationIdSuffix ".playstore"
versionNameSuffix "-PlayStore"
signingConfig signingConfigs.debug
}
}
@@ -117,20 +120,23 @@ android {
}
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.5.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
implementation 'androidx.preference:preference:1.2.0'
implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'com.android.support.constraint:constraint-layout:2.0.4'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.core:core-splashscreen:1.0.0'
androidTestImplementation 'androidx.test:core:1.4.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test:rules:1.4.0'
androidTestImplementation 'androidx.test:runner:1.4.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation 'org.mockito:mockito-android:4.5.1'
androidTestImplementation 'org.mockito:mockito-android:4.8.0'
androidTestUtil 'androidx.test:orchestrator:1.4.1'
}
+13 -1
View File
@@ -33,7 +33,19 @@
type="xsd:float">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Accuracy in meters of the current TrackPoint; 68% chance that the actual location is
Horizontal accuracy in meters of the current TrackPoint; 68% chance that the actual
location is
within this radius around the measured location.
Only used in GPX.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="accuracy_vertical"
type="xsd:float">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Vertical accuracy in meters of the current TrackPoint; 68% chance that the actual
location is
within this radius around the measured location.
Only used in GPX.
</xsd:documentation>
+114 -32
View File
@@ -1,37 +1,119 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
enable-background="new 0 0 95.334 100"
height="100"
id="Ebene_1"
version="1.1"
viewBox="0 0 100 100"
width="100"
x="0px"
xmlns="http://www.w3.org/2000/svg"
y="0px"
xml:space="preserve"><metadata
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 95.334 100"
height="100"
id="Ebene_1"
version="1.1"
viewBox="0 0 100 100"
width="100"
x="0px"
y="0px"
xml:space="preserve"
sodipodi:docname="LOGO.svg"
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1011"
id="namedview10"
showgrid="false"
inkscape:zoom="7.94"
inkscape:cx="50"
inkscape:cy="50"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="Ebene_1" /><metadata
id="metadata16"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata>
<defs id="defs14" />
<g id="g9"
transform="translate(-1.0593197,1.9067775)">
<linearGradient gradientUnits="userSpaceOnUse"
id="SVGID_1_"
x1="28.7554"
x2="82.789497"
y1="81.535202"
y2="9.2979002">
<stop style="stop-color:#FBB22F"
id="stop2"
offset="0" />
<stop style="stop-color:#F58820"
id="stop4"
offset="1" />
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata>
<defs
id="defs14"><linearGradient
inkscape:collect="always"
id="linearGradient862"><stop
style="stop-color:#ff6d00;stop-opacity:1;"
offset="0"
id="stop858" /><stop
style="stop-color:#ff6d00;stop-opacity:0;"
offset="1"
id="stop860" /></linearGradient><linearGradient
gradientUnits="userSpaceOnUse"
id="SVGID_1_"
x1="28.7554"
x2="82.789497"
y1="81.535202"
y2="9.2979002"
style="fill-opacity:1;fill:url(#linearGradient849)">
<stop
style="stop-color:#FBB22F;fill-opacity:1;fill:url(#linearGradient849)"
id="stop853"
offset="0" />
<stop
style="stop-color:#F58820;fill-opacity:1;fill:url(#linearGradient849)"
id="stop855"
offset="1" />
</linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient847"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop843" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop845" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient847"
id="linearGradient849"
x1="83.161415"
y1="8.9067831"
x2="10.194695"
y2="92.068085"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient862"
id="linearGradient866"
gradientUnits="userSpaceOnUse"
x1="62.43222"
y1="35.349697"
x2="9.7942638"
y2="93.02298" /></defs>
<g
id="g9"
transform="translate(-1.0593197,1.9067775)"
style="fill-opacity:1;fill:url(#linearGradient849)">
<linearGradient
gradientUnits="userSpaceOnUse"
id="SVGID_1_-3"
x1="28.7554"
x2="82.789497"
y1="81.535202"
y2="9.2979002"
style="fill-opacity:1;fill:url(#linearGradient849)">
<stop
style="stop-color:#FBB22F;fill-opacity:1;fill:url(#linearGradient849)"
id="stop2"
offset="0" />
<stop
style="stop-color:#F58820;fill-opacity:1;fill:url(#linearGradient849)"
id="stop4"
offset="1" />
</linearGradient>
<path style="fill:url(#SVGID_1_)"
d="M 83.548,10.927 C 84.255,8.511 83.102,7.649 80.985,9.01 L 11.146,53.921 c -2.117,1.361 -1.795,2.628 0.714,2.815 L 40.865,58.9 c 2.51,0.188 5.291,2.267 6.18,4.621 l 10.282,27.208 c 0.89,2.354 2.195,2.304 2.902,-0.111 z"
id="path7" />
<path
style="fill:url(#linearGradient866);fill-opacity:1"
d="M 83.548,10.927 C 84.255,8.511 83.102,7.649 80.985,9.01 L 11.146,53.921 c -2.117,1.361 -1.795,2.628 0.714,2.815 L 40.865,58.9 c 2.51,0.188 5.291,2.267 6.18,4.621 l 10.282,27.208 c 0.89,2.354 2.195,2.304 2.902,-0.111 z"
id="path7" />
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

+37
View File
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
enable-background="new 0 0 95.334 100"
height="100"
id="Ebene_1"
version="1.1"
viewBox="0 0 100 100"
width="100"
x="0px"
xmlns="http://www.w3.org/2000/svg"
y="0px"
xml:space="preserve"><metadata
id="metadata16"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata>
<defs id="defs14" />
<g id="g9"
transform="translate(-1.0593197,1.9067775)">
<linearGradient gradientUnits="userSpaceOnUse"
id="SVGID_1_"
x1="28.7554"
x2="82.789497"
y1="81.535202"
y2="9.2979002">
<stop style="stop-color:#FBB22F"
id="stop2"
offset="0" />
<stop style="stop-color:#F58820"
id="stop4"
offset="1" />
</linearGradient>
<path style="fill:url(#SVGID_1_)"
d="M 83.548,10.927 C 84.255,8.511 83.102,7.649 80.985,9.01 L 11.146,53.921 c -2.117,1.361 -1.795,2.628 0.714,2.815 L 40.865,58.9 c 2.51,0.188 5.291,2.267 6.18,4.621 l 10.282,27.208 c 0.89,2.354 2.195,2.304 2.902,-0.111 z"
id="path7" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+67
View File
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="125"
id="svg2"
version="1.1"
viewBox="0 0 125 125"
width="125"
sodipodi:docname="ic_button_done.svg"
inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1011"
id="namedview9"
showgrid="false"
inkscape:zoom="2.2457711"
inkscape:cx="104.26334"
inkscape:cy="71.321036"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<defs
id="defs6" />
<circle
style="fill:none;fill-opacity:1;stroke:#ff6d00;stroke-width:5.03185;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
cx="62.5"
cy="62.5"
id="path12"
r="59.984074" />
<metadata
id="metadata8">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:title></dc:title>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<circle
style="fill:#ff6d00;fill-opacity:1;stroke:none;stroke-width:6.91728;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
cx="62.5"
cy="62.5"
id="path835"
r="50" />
<path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:6.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 38.620483,63.865643 52.899055,78.144215 86.379516,44.663754"
id="path1000" />
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

+79
View File
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="125"
id="svg2"
version="1.1"
viewBox="0 0 125 125"
width="125"
sodipodi:docname="ic_button_resume.svg"
inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1011"
id="namedview9"
showgrid="false"
inkscape:zoom="3.176"
inkscape:cx="85.245821"
inkscape:cy="70.9649"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<defs
id="defs6" />
<circle
style="fill:none;fill-opacity:1;stroke:#ff6d00;stroke-width:5.03185;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
cx="62.5"
cy="62.5"
id="path12"
r="59.984074" />
<circle
style="fill:#ff6d00;fill-opacity:1;stroke:none;stroke-width:6.91728;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
cx="62.5"
cy="62.5"
id="path835"
r="50" />
<metadata
id="metadata8">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:title></dc:title>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:6.67087;stroke-linecap:round;stroke-opacity:1"
id="path837"
sodipodi:sides="3"
sodipodi:cx="61.453945"
sodipodi:cy="62.5"
sodipodi:r1="26.146271"
sodipodi:r2="13.073136"
sodipodi:arg1="0"
sodipodi:arg2="1.0471976"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="M 87.600216,62.5 67.990513,73.821668 48.38081,85.143335 l -10e-7,-22.643336 10e-7,-22.643334 19.609704,11.321667 z"
inkscape:transform-center-x="-6.5365678" />
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

@@ -7,4 +7,4 @@ Funktionen & Features:
* zeichnet deine Herzfrequenz auf (Bluetooth LE)
* nutze beim Radfahren deine Trittfrequenz- und Geschwindigkeitssensoren sowie Leistungsmesser (Bluetooth LE)
* exportiert deine Daten als KML oder GPX
* funktioniert ganz ohne Internet
* funktioniert ganz ohne Internet
@@ -1 +1 @@
OpenTracks – ein GPS-Tracker, der deine Daten für sich behält.
OpenTracks – ein GPS-Tracker, der deine Daten für sich behält.
@@ -0,0 +1,7 @@
v3.28.0: OpenTracks
Changes:
- Configurable filename for export/sharing (default: {starttime}_{name}.{suffix})
- Vertical GPS accuracy is now stored and can be exported/imported
(API level >= 26).
@@ -0,0 +1,24 @@
v4.0.0: OpenTracks
Changes:
- UI refactoring with a Bottom App Bar.
- Public API: implemented STOP properly.
- Cleanup: use explicit preference identifiers for TrackFileFormat.
- Improvements on BLE handler.
- Add exception handler and show error activity so users can send reports about bugs.
- Android 13: launcher icon, provide a themed icon.
- Change track title capitalization to sentences.
- Translations updated.
Bugfix:
- Sharing track added file suffix twice.
- Unit system not loaded correctly.
- File format template was not loaded (NPE).
- Public API: respect post-workout export.
Developer:
- Move to androix.constraintlayout.
- UI tests slightly more robust.
- Refactor permission request.
- Upgrade to gradlew 7.4.2
- Bump material from 1.5.0 to 1.6.0
@@ -0,0 +1,13 @@
v4.0.1: OpenTracks
Changes:
- Voice announcements are now configurable
- Fullscreen mode now also hides Android's navigation bar
- Connection to BLE sensors should now be faster
- Allow to reset settings while recording
- Allow to change import/export settings while recording
- KML/KMZ: do not use icons to be loaded from Google
Bugfix:
- TrackStoppedActivity now works on smaller screens
- Requests permissions on track list
@@ -0,0 +1,10 @@
v4.0.2: OpenTracks
Changes:
- Add nautical miles
- Settings can be reset while recording
Bugfix:
- Voice announcement rounding
- Chart: elevation was weirdly smoothed
- Export was not checking if file exists
@@ -0,0 +1,8 @@
v4.0.3: OpenTracks
Changes:
- Max. speed as reported by sensor (GPS or BLE)
Bugfix:
- Prevent crash if OSM Dashboard is not installed
- GPX track export: do not export empty track segments
@@ -0,0 +1,13 @@
v4.0.4: OpenTracks
Changes:
- Store BLE sensor data even without distance (every 10s)
- Public API: allows setting name, category, and description
Bugfix:
- Crash when taking a picture and rotating the phone
-
Developer:
- Target SDK 33
- Barometer uses interval of 5s
@@ -0,0 +1,8 @@
v4.0.5: OpenTracks
Changes:
- PlayStore relase get's another logo
Bugfix:
- Interval list shows "-" if gain/loss is not present
@@ -0,0 +1,6 @@
v4.0.6: OpenTracks
Bugfix:
- EGM2008 offset computed wrong for negative longitudes
- PublicAPI crashed if OpenTracks was in background (API26+)
@@ -0,0 +1,11 @@
v4.0.7: OpenTracks
Changes:
- GPS signal lost timeout: 30s
- Android13: request permission to show notifications
- Faster deletion of tracks (vacuum database only if needed)
Bugfix:
- PublicAPI stop command resulted in crash (introduced in v4.0.6)
- Permissions: request all permissions during on startup
@@ -0,0 +1,11 @@
v4.0.8: OpenTracks
Changes:
- Altitude always shown in m/ft
- Show track timezone if different than device timezone
- PublicAPI: support setting the track icon explicitly
Developer:
- Introduced DistanceFormatter
- Upgrade to AGP 7.3/desugar for Duration.class
- Android12+: do splashscreen via Androidx
@@ -0,0 +1,8 @@
v4.1.0: OpenTracks
Changes:
- Export KML/KMZ version 2.3 only (was 2.2)
- For crank-based powermeters: fetch cadence
Bugfix:
- Workaround for crash on "import all"
@@ -0,0 +1,7 @@
v4.1.1: OpenTracks
Changes:
- Add introduction screen (show once)
Bugfix:
- Do not crash if no GPS device is present
Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 KiB

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 574 KiB

After

Width:  |  Height:  |  Size: 560 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 812 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 456 KiB

After

Width:  |  Height:  |  Size: 462 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 630 KiB

After

Width:  |  Height:  |  Size: 695 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 416 KiB

After

Width:  |  Height:  |  Size: 467 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 508 KiB

After

Width:  |  Height:  |  Size: 509 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 798 KiB

After

Width:  |  Height:  |  Size: 872 KiB

@@ -0,0 +1,11 @@
v3.20.2 : OpenTracks
Changements :
- Le gain et la perte d'altitude sont des entiers au lieu de floats
- Demander la permission avant d'utiliser Bluetooth par un message Toast
Correction d'un bug :
- Définir FLAG_IMMUTABLE pour PendingIntents pour éviter un crash sur Android 12
- Le capteur de cadence renvoie 0 au lieu de None lorsque l'utilisateur ne pédale pas.
- Le capteur de vitesse renvoie 0 au lieu de la dernière vitesse connue quand c'est arrêté
- Avec les capteurs, le temps de déplacement s'arrête lorsqu'il n'est pas en mouvement
@@ -7,5 +7,11 @@ Fonctionnalités :
* Dénivelé positif et négatif: via capteur de pression barométrique
* Synthèse vocale
* Prise en charge des capteurs Bluetooth LE : rythme cardiaque, vitesse et distance (vélo), cadence de pédalage (vélo), et capteur de puissance (vélo)
* Export : export des traces au format KMZ (photos incluses), KML ou GPX
* Export : export des traces au format KMZ (photos incluses), KML ou GPX
* Pas d'accès internet requis : Internet n'est pas nécessaire pour le fonctionnement de l'application
* Thème sombre ou claire, avec le respect des paramètres du système
* Pas de publicités
Logiciel Open Source
Vous pouvez utiliser, étudier, changer et partager le code source.
Licence Apache 2.0
@@ -0,0 +1,16 @@
v3.18.6: OpenTracks
Wijzigingen:
- "Toon op kaart"-functionaliteit via KML in plaats van KMZ
- Spraakaankondigingen bevatten geen app naam
- Tijdens opname: hoogtewinst/-verlies wordt elke seconde bijgewerkt
- Winkel beschrijvingen vertaald
Bufixes:
- Ongeldige vertalingen van stemboodschappen opgelost
Ontwikkelaar:
- Android12-toestemmingen (BLUETOOTH_SCAN)
- Android12-toestemmingen (ACCESS_COARSE_LOCATION)
- Assertiviteit voor TrackPoints (testen)
- Ongebruikte vertalingen verwijderd
@@ -0,0 +1,16 @@
v3.19.0: OpenTracks
Wijzigingen:
- Ondersteuning voor Running Speed en Cadence (RCS) sensoren
- Bij het delen van een track wordt een afbeelding gemaakt
- Niet trillen bij starten van opnemen
- Bij opnemen: UI toont recentst ontvangen hoogte, snelheid en richting (ook al is deze niet opgeslagen)
Bugfix:
- Berekening van totale tijd mag geen pauze-tijd bevatten (vereist opnieuw importeren)
- (opnieuw) Gebruik aangepaste overzichten indien aanwezig
Ontwikkelaar:
- Upgrade naar SDK31 en AGP7.0.0 (vereist JDK11)
- TrackRecordingService: sensorfusie (TrackPointCreator) en TrackPoint-opslag (TrackRecordingManager) geëxtraheerd
- ExportImportTest is nu deterministisch/constant
@@ -0,0 +1,16 @@
v3.27.0: OpenTracks
Mudanças:
- Anúncios de voz: adicionado 100m, 200m e 500m
- Intervalo: adicionado 100m e 500m
- O relógio mostra agora também os segundos
- GPX, KML, KMZ: uma maior precisão horizontal de exportação e importação
- GPX: exporta a distância acumulada via extensão clustrust
- CSV: exportação básica (apenas TrackPoints; não para backup)
- Solução alternativa para o Tickr X, pois relata SPM em vez de RPM
Correções de bugs:
- Foi Aderido ao intervalo de distância de gravação; Os TrackPoints foram registrados embora estivessem muito próximos
- Prevenir travamento ao parar de gravar (condição de corrida)
- Evite acidentes ao usar vários sensores (condição de corrida)
- Evite travamentos ao abrir o OpenTracks via notificação
@@ -1 +1 @@
Um rastreador esportivo que respeita totalmente a sua privacidade.
Um rastreador esportivo que respeita a sua privacidade.
@@ -0,0 +1,22 @@
Gizliliğinize saygı duyan bir spor takipçisi.
Spor ve açık hava etkinlikleri mutluluk, beceri ve güven verir.
Antrenmanınızı takip ederek sağlığınıza değer verin.
Koşarken veya yürürken kaydeder ve size bisiklet sürmek için daha büyük ekranlı bir bisiklet bilgisayarı verir.
Yol boyunca ilginç yerleri resimlerle işaretleyin.
Analiz için kaydedilen istatistikleri çok ayrıntılı tutun.
Yalnızca başkalarının sahip olmasını istediğiniz verileri paylaşın.
* Sesli duyurular.
* Bluetooth LE sensörlerini destekler: kalp atış hızı, hız ve mesafe (bisiklet), tempo (bisiklet) ve güç ölçer (bisiklet).
* İrtifa kazancı ve kaybı: barometrik sensör aracılığıyla.
* EGM2008'de gösterilen rakım (ortalama deniz seviyesinin üzerinde); WGS84 olarak dışa aktarılır.
* Verileri KMZ (fotoğraflar dahil), KML veya GPX olarak parça olarak dışa aktarın.
* İnternet erişimi veya ekstra izin yok.
* Sistem ayarlarına uygun olarak koyu ve açık tema.
* Reklamsız.
Libre yazılımı / Özgür Yazılım / Açık Kaynak
yani kaynak kodunu kullanabilir, inceleyebilir, değiştirebilir ve paylaşabilirsiniz.
Lisanslı Apache 2.0
@@ -0,0 +1 @@
Gizliliğinize saygı duyan bir spor takipçisi.
@@ -0,0 +1,16 @@
v3.18.6: OpenTracks
Thay đổi:
- Tính năng "Hiện trên bản đồ" thông qua KML thay vì KMZ
- Thông báo bằng giọng nói không bao gồm tên ứng dụng
- Trong lúc ghi: Tăng/ giảm độ cao được cập nhật mỗi giây
- Thông tin cửa hàng được cập nhật
Sửa lỗi:
- Sửa lỗi các bản dịch Thông báo giọng nói không hợp lệ
Phát triển:
- Quyền Android 12 (BLUETOOTH_SCAN)
- Quyền Android 12 (ACCESS_COARSE_LOCATION)
- Khẳng định cho TrackPoints (thử nghiệm)
- Loại bỏ các bản dịch thừa
@@ -1,11 +1,22 @@
OpenTracks là một môn thể thao theo dõi ứng dụng hoàn toàn tôn trọng sự riêng tư.
OpenTracks là một ứng dụng theo dõi thể theo tôn trọng sự riêng tư của bạn.
Tính năng:
* Thể theo dõi theo dõi thể thao và hoạt động ngoài trời
* Ảnh và đánh dấu: đánh dấu địa điểm thú vị trong khi theo dõi
* Độ cao được và mất: thông qua khí quyển cảm biến
* Độ cao thể hiện trong EGM2008 (trên mực nước biển); xuất khẩu như WGS84
* Tiếng nói thông báo
* Hỗ trợ Bluetooth LE cảm biến: tim, tốc độ và khoảng cách (đi xe đạp), nhịp (đi xe đạp), và đồng hồ điện (đi xe đạp)
* Xuất dữ liệu: xuất bài hát hoặc là BIỂU (bao gồm. ảnh) DÕI hay C
* Không có Internet: Internet là không được sử dụng
Thể thao và các hoạt động ngoài trời cho bạn niềm vui, kĩ năng và sự tự tin.
Quý trọng sức khỏe của bạn bằng cách theo dõi tập luyện của bạn.
Ghi lại quá trình khi bạn chạy bộ hoặc đi bộ, và cho bạn một màn hình xe đạp khi đạp xe.
Đánh dấu những địa điểm thú vị trong lúc bạn di chuyển bằng hình ảnh.
Giữ các thống kê chi tiết để phân tích kĩ càng.
Chỉ chia sẻ những dữ liệu bạn muốn chia sẻ.
* Thông báo bằng giọng nói.
* Hỗ trợ Bluetooth LE cảm biến: nhịp tim, tốc độ và khoảng cách (đi xe đạp), nhịp (đi xe đạp), và đồng hồ sức mạnh (đi xe đạp)
* Tăng hoặc giảm độ cao: thông qua cảm biến khí áp.
* Độ cao được hiển thị dưới EGM2009 (trên nghĩa là trên mực nước biển), KML hoặc GPX.
* Xuất dữ liệu được lại dưới KMZ (gồm hình ảnh), KML hoặc GPX.
* Không cần Internet hoặc các quyền không cần thiết.
* Chế độ tối hoặc sáng, theo cài đặt hệ thống.
* Không quảng cáo.
Phần mềm Libre / Free Software / Mã nguồn mở
có nghĩa là bạn có thể dùng, học tập, thay đổi và chia sẻ mã nguồn.
Licensed Apache 2.0
@@ -1 +1 @@
Một môn thể thao theo dõi mà hoàn toàn tôn trọng sự riêng tư.
Một người bạn theo dõi mà tôn trọng sự riêng tư của bạn.
@@ -1,11 +1,22 @@
OpenTracks是一个完全尊重你的隐私的运动跟踪应用程序。
一款尊重您的隐私的运动追踪应用。
健康幸福的生活总少不了各种运动和户外活动。
使用本应用,您可以保持对您的训练数据的有效追踪,为您的健康助力。
本应用可以在您跑步或散步时记录数据,也可以在您骑车时作为一个大屏幕自行车电脑。
您可以为路上的有趣位置创建标记,还可以用图片作为标记。
您的数据会被详尽地记录下来,供您查阅分析。
您的数据只会分享给您想要分享给的人。
特点。
* 运动追踪:追踪你的运动和户外活动
* 照片和标记:在跟踪时标记有趣的地点
* 海拔增减:通过气压传感器
* 高度显示为EGM2008(平均海平面以上);导出为WGS84
* 语音播报
* 支持蓝牙LE传感器:心率、速度和距离(骑车)、节奏(骑车)和功率计(骑车)。
* 导出数据:以KMZ(包括照片)、KML或GPX形式导出轨迹
* 没有互联网接入。不使用互联网
* 支持蓝牙LE传感器:心率、速度和距离(骑车)、踏频(骑车)和功率计(骑车)。
* 海拔增减:通过气压传感器。
* EGM2008 海拔显示(平均海平面以上);导出为 WGS84。
* 导出路线数据,支持 KMZ(包括照片)、KML 和 GPX 格式。
* 不需要联网,不需要额外权限。
* 深色和浅色主题,跟随系统设置。
* 没有广告。
本应用是开源软件
意味着您可以使用、学习、修改和分发本应用的源代码。
本应用使用 Apache 2.0 协议发布。
@@ -1 +1 @@
一个完全尊重你隐私的运动追踪器。
一款尊重您的隐私的运动追踪应用。
-1
View File
@@ -1,2 +1 @@
android.enableJetifier=true
android.useAndroidX=true
Binary file not shown.
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 116 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 862 KiB

After

Width:  |  Height:  |  Size: 474 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 240 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 338 KiB

After

Width:  |  Height:  |  Size: 298 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 665 KiB

After

Width:  |  Height:  |  Size: 565 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 248 KiB

After

Width:  |  Height:  |  Size: 237 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 661 KiB

After

Width:  |  Height:  |  Size: 588 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1001 KiB

After

Width:  |  Height:  |  Size: 1012 KiB

@@ -3,48 +3,25 @@ package de.dennisguse.opentracks;
import static androidx.test.espresso.Espresso.onData;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.Espresso.pressBack;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.scrollTo;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.action.ViewActions.longClick;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anything;
import static de.dennisguse.opentracks.util.EspressoUtils.childAtPosition;
import static de.dennisguse.opentracks.util.EspressoUtils.veryLongTouch;
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
import static de.dennisguse.opentracks.util.EspressoUtils.withListSize;
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.content.Context;
import android.view.View;
import android.widget.ListView;
import androidx.test.espresso.IdlingRegistry;
import androidx.test.espresso.IdlingResource;
import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.assertion.ViewAssertions;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.GrantPermissionRule;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import de.dennisguse.opentracks.services.TrackDeleteService;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class EspressoDeleteTrackTest {
@@ -55,150 +32,33 @@ public class EspressoDeleteTrackTest {
@Rule
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
private MyIdlingResource idlingResource;
@Before
public void registerIntentServiceIdlingResource() {
Instrumentation instrumentation
= InstrumentationRegistry.getInstrumentation();
idlingResource = new MyIdlingResource(instrumentation.getTargetContext());
IdlingRegistry.getInstance().register(idlingResource);
}
@After
public void unregisterIntentServiceIdlingResource() {
IdlingRegistry.getInstance().unregister(idlingResource);
}
@Ignore("Test fails permanently")
@Deprecated
@Test
public void espressoDeleteTrackTest() {
int countBefore;
// TrackListActivity: start recording
onView(withId(R.id.track_list_fab_action)).perform(click());
{
// TrackListActivity: start recording
ViewInteraction trackControllerRecordButton = onView(withId(R.id.controller_record));
trackControllerRecordButton.perform(click());
}
// TrackRecordingActivity
onView(withId(R.id.track_recording_fab_action))
// wait; stay recording
.perform(waitFor(5000))
// stop;
.perform(longClick());
{
// TrackRecordingActivity
ViewInteraction trackControllerRecordButton = onView(withId(R.id.controller_record));
ViewInteraction trackControllerStopButton = onView(withId(R.id.controller_stop));
// wait; stay recording
trackControllerRecordButton.perform(waitFor(5000));
// stop;
trackControllerStopButton.perform(veryLongTouch(1600));
}
// back
pressBack();
// get number of items before deleting a track
countBefore = numberOfItemsListView();
// TrackStoppedActivity
onView(withId(R.id.finish_button)).perform(click());
// select track
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(veryLongTouch(5000));
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(longClick());
// open menu and delete selected track
ViewInteraction overflowMenuButton = onView(
allOf(withContentDescription("More options"),
childAtPosition(
childAtPosition(
withId(R.id.toolbar),
2),
2),
isDisplayed()));
overflowMenuButton.perform(click());
onView(allOf(withContentDescription("More options"), isDisplayed()))
.perform(click());
ViewInteraction appCompatTextView = onView(
allOf(withId(R.id.title), withText("Delete"),
childAtPosition(
childAtPosition(
withId(R.id.content),
0),
0),
isDisplayed()));
appCompatTextView.perform(click());
onView(withText("Delete")).perform(click());
ViewInteraction appCompatButton = onView(
allOf(withId(android.R.id.button1), withText("Yes"),
childAtPosition(
childAtPosition(
withId(R.id.buttonPanel),
0),
3)));
appCompatButton.perform(scrollTo(), click());
onView(withText("OK")).perform(click());
// check number of items after delete
onView(withId(R.id.track_list)).check(ViewAssertions.matches(withListSize(countBefore - 1)));
}
private int numberOfItemsListView() {
final int[] counts = new int[1];
onView(withId(R.id.track_list)).check(matches(new TypeSafeMatcher<View>() {
@Override
public boolean matchesSafely(View view) {
ListView listView = (ListView) view;
counts[0] = listView.getCount();
return true;
}
@Override
public void describeTo(Description description) {
}
}));
return counts[0];
}
private static class MyIdlingResource implements IdlingResource {
private final Context context;
private ResourceCallback resourceCallback;
public MyIdlingResource(Context context) {
this.context = context;
}
@Override
public String getName() {
return MyIdlingResource.class.getName();
}
@Override
public void registerIdleTransitionCallback(IdlingResource.ResourceCallback resourceCallback) {
this.resourceCallback = resourceCallback;
}
@Override
public boolean isIdleNow() {
boolean idle = !isDeleteServiceRunning();
if (idle && resourceCallback != null) {
resourceCallback.onTransitionToIdle();
}
return idle;
}
private boolean isDeleteServiceRunning() {
ActivityManager manager =
(ActivityManager) context.getSystemService(
Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo info :
manager.getRunningServices(Integer.MAX_VALUE)) {
if (TrackDeleteService.class.getName().equals(
info.service.getClassName())) {
return true;
}
}
return false;
}
// tracklist is empty now
onView(allOf(withText("Start recording your next adventure here"), isDisplayed()));
}
}
@@ -5,18 +5,14 @@ import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.Espresso.openContextualActionModeOverflowMenu;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
import static androidx.test.espresso.action.ViewActions.longClick;
import static androidx.test.espresso.action.ViewActions.replaceText;
import static androidx.test.espresso.action.ViewActions.scrollTo;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.is;
import static de.dennisguse.opentracks.util.EspressoUtils.childAtPosition;
import static de.dennisguse.opentracks.util.EspressoUtils.veryLongTouch;
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
import androidx.test.espresso.ViewInteraction;
@@ -25,7 +21,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.rule.GrantPermissionRule;
import org.hamcrest.core.IsInstanceOf;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -50,16 +45,15 @@ public class EspressoEditTrackRecordingTest {
public void espressoEditTrackRecordingTest() {
{
// TrackListActivity: start recording
ViewInteraction trackControllerRecordButton = onView(withId(R.id.controller_record));
trackControllerRecordButton.perform(click());
ViewInteraction fabRecordButton = onView(withId(R.id.track_list_fab_action));
fabRecordButton.perform(click());
}
{
// TrackRecordingActivity
ViewInteraction trackControllerRecordButton = onView(withId(R.id.controller_record));
ViewInteraction trackControllerStopButton = onView(withId(R.id.controller_stop));
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
// wait; stay recording
trackControllerRecordButton.perform(waitFor(15000));
trackControllerStopButton.perform(waitFor(4000));
// open menu
openContextualActionModeOverflowMenu();
@@ -68,58 +62,24 @@ public class EspressoEditTrackRecordingTest {
onView(withText(R.string.menu_edit)).perform(click());
// change name for "New Name"
ViewInteraction textInputEditText = onView(
allOf(withId(R.id.track_edit_name),
childAtPosition(
childAtPosition(
withClassName(is("com.google.android.material.textfield.TextInputLayout")),
0),
1)));
ViewInteraction textInputEditText = onView(withId(R.id.track_edit_name));
textInputEditText.perform(scrollTo(), replaceText("New Name"));
ViewInteraction textInputEditText2 = onView(
allOf(withId(R.id.track_edit_name), withText("New Name"),
childAtPosition(
childAtPosition(
withClassName(is("com.google.android.material.textfield.TextInputLayout")),
0),
1),
isDisplayed()));
ViewInteraction textInputEditText2 = onView(allOf(withId(R.id.track_edit_name), withText("New Name"), isDisplayed()));
textInputEditText2.perform(closeSoftKeyboard());
// save edition
ViewInteraction appCompatButton = onView(
allOf(withId(R.id.track_edit_save),
childAtPosition(
childAtPosition(
withClassName(is("android.widget.LinearLayout")),
3),
1),
isDisplayed()));
ViewInteraction appCompatButton = onView(withId(R.id.track_edit_save));
appCompatButton.perform(click());
//pause
trackControllerRecordButton.perform(veryLongTouch(1600));
// wait; stay paused
trackControllerRecordButton.perform(waitFor(1000));
// resume
trackControllerRecordButton.perform(click());
// wait; stay recording
trackControllerRecordButton.perform(waitFor(1000));
// stop;
trackControllerStopButton.perform(veryLongTouch(1600));
trackControllerStopButton.perform(longClick());
// check track's name is "New Name"
ViewInteraction textView = onView(
allOf(withId(R.id.stats_name_value),
withParent(withParent(IsInstanceOf.instanceOf(android.widget.ScrollView.class))),
isDisplayed()));
textView.check(matches(withText("New Name")));
// it's on track stopped activity and there are two buttons
ViewInteraction resumeButton = onView(withId(R.id.resume_button));
resumeButton.check(matches(isDisplayed()));
ViewInteraction finishButton = onView(withId(R.id.finish_button));
finishButton.check(matches(isDisplayed()));
}
}
}
@@ -3,26 +3,22 @@ package de.dennisguse.opentracks;
import static androidx.test.espresso.Espresso.onData;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.longClick;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anything;
import static de.dennisguse.opentracks.util.EspressoUtils.selectTabAtIndex;
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
import android.view.MotionEvent;
import android.view.View;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.action.MotionEvents;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.rule.GrantPermissionRule;
import org.hamcrest.Matcher;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,34 +35,30 @@ public class EspressoUITest {
@LargeTest
@Test
public void record_pause_resume_stop() {
public void record_stop_resume_stop_finish() {
{
// TrackListActivity: start recording
ViewInteraction trackControllerRecordButton = onView(withId(R.id.controller_record));
ViewInteraction trackControllerRecordButton = onView(withId(R.id.track_list_fab_action));
trackControllerRecordButton.perform(click());
}
{
// TrackRecordingActivity
ViewInteraction trackControllerRecordButton = onView(withId(R.id.controller_record));
ViewInteraction trackControllerStopButton = onView(withId(R.id.controller_stop));
// TrackRecordingActivity: wait to record some time and then stop
onView(withId(R.id.track_recording_fab_action))
.perform(waitFor(5000))
.perform(longClick());
// wait; stay recording
trackControllerRecordButton.perform(waitFor(5000));
// TrackStoppedActivity: resume
onView(allOf(withId(R.id.resume_button), isClickable()))
.perform(click());
//pause
trackControllerRecordButton.perform(veryLongTouch(1600));
// TrackRecordingActivity: wait and then stop
onView(withId(R.id.track_recording_fab_action))
.perform(waitFor(5000))
.perform(longClick());
// wait; stay paused
trackControllerRecordButton.perform(waitFor(1000));
// resume
trackControllerRecordButton.perform(click());
// wait; stay recording
trackControllerRecordButton.perform(waitFor(1000));
// stop;
trackControllerStopButton.perform(veryLongTouch(1600));
// TrackStoppedActivity
onView(withId(R.id.finish_button))
.perform(click());
}
}
@@ -75,13 +67,13 @@ public class EspressoUITest {
public void record_move_through_tabs() {
{
// TrackListActivity: start recording
ViewInteraction trackControllerRecordButton = onView(withId(R.id.controller_record));
ViewInteraction trackControllerRecordButton = onView(withId(R.id.track_list_fab_action));
trackControllerRecordButton.perform(click());
}
{
// TrackRecordingActivity
ViewInteraction tabLayout = onView(withId(R.id.track_detail_activity_tablayout));
ViewInteraction trackControllerStopButton = onView(withId(R.id.controller_stop));
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
tabLayout.perform(selectTabAtIndex(1));
tabLayout.perform(waitFor(1000));
@@ -96,7 +88,7 @@ public class EspressoUITest {
tabLayout.perform(waitFor(1000));
// stop
trackControllerStopButton.perform(veryLongTouch(1600));
trackControllerStopButton.perform(longClick());
}
}
@@ -104,36 +96,6 @@ public class EspressoUITest {
@Test
public void selectAndDeleteTrack() {
onView(withId(R.id.track_list)).check(matches(isDisplayed()));
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(veryLongTouch(2000));
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(longClick());
}
private static ViewAction veryLongTouch(final int duration_ms) {
return new ViewAction() {
@Override
public String getDescription() {
return "Perform long touch.";
}
@Override
public Matcher<View> getConstraints() {
return isDisplayed();
}
@Override
public void perform(UiController uiController, final View view) {
// Get view absolute position
int[] location = new int[2];
view.getLocationOnScreen(location);
// Offset coordinates by view position
float[] coordinates = new float[]{location[0] + 1, location[1] + 1};
// Send down event, pause, and send up
MotionEvent down = MotionEvents.sendDown(uiController, coordinates, new float[]{1f, 1f}).down;
uiController.loopMainThreadForAtLeast(duration_ms);
MotionEvents.sendUp(uiController, down, coordinates);
}
};
}
}
@@ -5,12 +5,13 @@ import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.settings.UnitSystem;
public class PreferenceMetricUnitRule implements TestRule {
private final boolean metricUnits;
public class PreferenceUnitSystemRule implements TestRule {
private final UnitSystem unit;
public PreferenceMetricUnitRule(boolean metricUnits) {
this.metricUnits = metricUnits;
public PreferenceUnitSystemRule(UnitSystem unit) {
this.unit = unit;
}
@Override
@@ -18,13 +19,13 @@ public class PreferenceMetricUnitRule implements TestRule {
return new Statement() {
@Override
public void evaluate() throws Throwable {
final boolean previousMetricUnits = PreferencesUtils.isMetricUnits();
final UnitSystem previousUnitSystem = PreferencesUtils.getUnitSystem();
try {
PreferencesUtils.setMetricUnits(metricUnits);
PreferencesUtils.setUnit(unit);
base.evaluate();
} finally {
PreferencesUtils.setMetricUnits(previousMetricUnits);
PreferencesUtils.setUnit(previousUnitSystem);
}
}
};
@@ -14,6 +14,7 @@ import de.dennisguse.opentracks.data.models.Altitude;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.TrackStatistics;
@RunWith(AndroidJUnit4.class)
@@ -26,7 +27,7 @@ public class ChartPointTest {
statistics.setTotalTime(Duration.ofSeconds(1000));
// when
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), Altitude.EGM2008.of(0), false, false);
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), false, UnitSystem.IMPERIAL);
// then
assertEquals(1000000, (long) point.getTimeOrDistance());
@@ -39,7 +40,7 @@ public class ChartPointTest {
statistics.setTotalDistance(Distance.of(1000));
// when
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), Altitude.EGM2008.of(0), true, true);
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), true, UnitSystem.METRIC);
// then
assertEquals(1, (long) point.getTimeOrDistance());
@@ -49,9 +50,11 @@ public class ChartPointTest {
public void create_get_altitude_speed_and_pace() {
// given
TrackStatistics statistics = new TrackStatistics();
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
.setAltitude(Altitude.EGM2008.of(50));
// when
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), Altitude.EGM2008.of(50), false, true);
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
// then
assertEquals(50, point.getAltitude(), 0.01);
@@ -63,9 +66,10 @@ public class ChartPointTest {
public void create_sensorNotAvailable() {
// given
TrackStatistics statistics = new TrackStatistics();
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
.setAltitude(Altitude.EGM2008.of(50));
// when
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), Altitude.EGM2008.of(50), false, true);
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
// then
assertNull(point.getHeartRate());
@@ -76,15 +80,16 @@ public class ChartPointTest {
@Test
public void create_sensorAvailable() {
// given
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
trackPoint.setHeartRate(100f);
trackPoint.setCadence(101f);
trackPoint.setPower(102f);
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
.setAltitude(Altitude.EGM2008.of(50))
.setHeartRate(100f)
.setCadence(101f)
.setPower(102f);
TrackStatistics statistics = new TrackStatistics();
// when
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), Altitude.EGM2008.of(50), false, true);
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
// then
assertEquals(100.0, point.getHeartRate(), 0.01);
@@ -15,6 +15,10 @@
*/
package de.dennisguse.opentracks.chart;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -25,10 +29,6 @@ import org.junit.runner.RunWith;
import de.dennisguse.opentracks.R;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Tests {@link ChartValueSeries}.
*
@@ -47,6 +47,7 @@ public class ChartValueSeriesTest {
new int[]{1, 100, 1000},
R.string.description_altitude_metric,
R.string.description_altitude_imperial,
R.string.description_altitude_imperial,
R.color.chart_altitude_fill,
R.color.chart_altitude_border,
15,
@@ -7,7 +7,6 @@ import android.util.Pair;
import org.junit.Test;
import java.util.HashSet;
import java.util.Set;
import de.dennisguse.opentracks.data.models.Track;
@@ -24,22 +23,17 @@ public class ShareContentProviderTest {
@Test
public void testCreateAndParseURI_valid() {
Set<Track.Id> trackIds = new HashSet<>();
trackIds.add(new Track.Id(1));
trackIds.add(new Track.Id(3));
trackIds.add(new Track.Id(5));
Set<Track.Id> trackIds = Set.of(new Track.Id(1), new Track.Id(3), new Track.Id(5));
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(trackIds, "TrackName", TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA);
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(trackIds, "filename.suffix", TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA);
assertEquals(trackIds, ShareContentProvider.parseURI(shareURIandMIME.first));
}
@Test
public void testCreateURIescapeFilename() {
Set<Track.Id> trackIds = new HashSet<>();
trackIds.add(new Track.Id(1));
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(trackIds, "../../&1=1", TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA);
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(Set.of(new Track.Id(1)), "../../&1=1", TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA);
assertEquals(Uri.parse("content://de.dennisguse.opentracks.debug.content/tracks/kml_with_trackdetail_and_sensordata/1/..%2F..%2F%261%3D1.kml"), shareURIandMIME.first);
assertEquals(Uri.parse("content://de.dennisguse.opentracks.debug.content/tracks/KML_WITH_TRACKDETAIL_AND_SENSORDATA/1/..%2F..%2F%261%3D1"), shareURIandMIME.first);
}
}
@@ -7,6 +7,7 @@ import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.Duration;
import java.time.Instant;
import de.dennisguse.opentracks.data.models.Track;
@@ -105,7 +106,7 @@ public class TrackSelectionTest extends TestCase {
long oneDay = 24 * 60 * 60 * 1000;
TrackSelection filterWrong1 = new TrackSelection().addDateRange(instant, null);
TrackSelection filterWrong2 = new TrackSelection().addDateRange(null, instant);
TrackSelection filterOk = new TrackSelection().addDateRange(instant, instant.plusMillis(oneDay));
TrackSelection filterOk = new TrackSelection().addDateRange(instant, instant.plus(Duration.ofDays(1)));
// when
SelectionData selectionWrong1 = filterWrong1.buildSelection();
@@ -10,24 +10,50 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import de.dennisguse.opentracks.settings.UnitSystem;
//TODO Parametrized tests
@RunWith(AndroidJUnit4.class)
public class DistanceFormatterTest {
private final Context context = ApplicationProvider.getApplicationContext();
@Test
public void testFormatDistance() {
public void testFormatDistance_metric() {
DistanceFormatter formatter = DistanceFormatter.Builder()
.setDecimalCount(2)
.setUnit(UnitSystem.METRIC)
.build(context);
// A large number in metric
assertEquals("5.00 km", formatter.formatDistance(Distance.of(5000), true));
// A large number in imperial
assertEquals("3.11 mi", formatter.formatDistance(Distance.of(5000), false));
assertEquals("5.00 km", formatter.formatDistance(Distance.of(5000)));
// A small number in metric
assertEquals("100.00 m", formatter.formatDistance(Distance.of(100), true));
assertEquals("100.00 m", formatter.formatDistance(Distance.of(100)));
}
@Test
public void testFormatDistance_imperial() {
DistanceFormatter formatter = DistanceFormatter.Builder()
.setDecimalCount(2)
.setUnit(UnitSystem.IMPERIAL)
.build(context);
// A large number in imperial
assertEquals("3.11 mi", formatter.formatDistance(Distance.of(5000)));
// A small number in imperial
assertEquals("328.08 ft", formatter.formatDistance(Distance.of(100), false));
assertEquals("328.08 ft", formatter.formatDistance(Distance.of(100)));
}
@Test
public void testFormatDistance_nautical() {
DistanceFormatter formatter = DistanceFormatter.Builder()
.setDecimalCount(2)
.setUnit(UnitSystem.NAUTICAL_IMPERIAL)
.build(context);
// A large number in nautical
assertEquals("2.70 NM", formatter.formatDistance(Distance.of(5000)));
// A small number in nautical
assertEquals("328.08 ft", formatter.formatDistance(Distance.of(100)));
}
}
@@ -0,0 +1,105 @@
package de.dennisguse.opentracks.data.models;
import static org.junit.Assert.assertEquals;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import de.dennisguse.opentracks.settings.UnitSystem;
//TODO Use make parametrized tests?
@RunWith(AndroidJUnit4.class)
public class SpeedFormatterTest {
private final Context context = ApplicationProvider.getApplicationContext();
@Test
public void testGetSpeedParts_pace_metric() {
SpeedFormatter formatter = SpeedFormatter.Builder()
.setDecimalCount(2)
.setUnit(UnitSystem.METRIC)
.setReportSpeedOrPace(false)
.build(context);
assertEquals("4:59", formatter.getSpeedParts(Speed.of(3.34)).first);
assertEquals("5:00", formatter.getSpeedParts(Speed.of(3.33)).first);
assertEquals("min/km", formatter.getSpeedParts(Speed.zero()).second);
}
@Test
public void testGetSpeedParts_pace_imperial() {
SpeedFormatter formatter = SpeedFormatter.Builder()
.setDecimalCount(2)
.setUnit(UnitSystem.IMPERIAL)
.setReportSpeedOrPace(false)
.build(context);
assertEquals("8:02", formatter.getSpeedParts(Speed.of(3.34)).first);
assertEquals("min/mi", formatter.getSpeedParts(Speed.zero()).second);
}
@Test
public void testGetSpeedParts_pace_nautical() {
SpeedFormatter formatter = SpeedFormatter.Builder()
.setDecimalCount(2)
.setUnit(UnitSystem.NAUTICAL_IMPERIAL)
.setReportSpeedOrPace(false)
.build(context);
assertEquals("9:14", formatter.getSpeedParts(Speed.of(3.34)).first);
assertEquals("min/NM", formatter.getSpeedParts(Speed.zero()).second);
}
@Test
public void testGetSpeedParts_speed_metric() {
SpeedFormatter formatter = SpeedFormatter.Builder()
.setDecimalCount(2)
.setUnit(UnitSystem.METRIC)
.setReportSpeedOrPace(true)
.build(context);
assertEquals("11.9", formatter.getSpeedParts(Speed.of(3.31)).first);
assertEquals("km/h", formatter.getSpeedParts(Speed.zero()).second);
}
@Test
public void testGetSpeedParts_speed_imperial() {
SpeedFormatter formatter = SpeedFormatter.Builder()
.setDecimalCount(2)
.setUnit(UnitSystem.IMPERIAL)
.setReportSpeedOrPace(true)
.build(context);
assertEquals("7.5", formatter.getSpeedParts(Speed.of(3.34)).first);
assertEquals("mph", formatter.getSpeedParts(Speed.zero()).second);
}
@Test
public void testGetSpeedParts_speed_nautical() {
SpeedFormatter formatter = SpeedFormatter.Builder()
.setDecimalCount(2)
.setUnit(UnitSystem.NAUTICAL_IMPERIAL)
.setReportSpeedOrPace(true)
.build(context);
assertEquals("6.5", formatter.getSpeedParts(Speed.of(3.34)).first);
assertEquals("kn", formatter.getSpeedParts(Speed.zero()).second);
}
@Test
public void testFormatSpeed() {
SpeedFormatter formatter = SpeedFormatter.Builder()
.setDecimalCount(2)
.setUnit(UnitSystem.METRIC)
.setReportSpeedOrPace(false)
.build(context);
assertEquals("4:59 min/km", formatter.formatSpeed(Speed.of(3.34)));
}
}
@@ -0,0 +1,61 @@
package de.dennisguse.opentracks.io.file;
import static org.junit.Assert.assertEquals;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
import java.util.TimeZone;
import java.util.UUID;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.TimezoneRule;
import de.dennisguse.opentracks.data.models.Track;
@RunWith(Parameterized.class)
public class TrackFilenameGeneratorTest {
@Rule
public TimezoneRule timezoneRule = new TimezoneRule(TimeZone.getTimeZone("Europe/Berlin"));
@Parameterized.Parameters
public static Collection<String[]> data() {
return Arrays.asList(new String[][]{
{"{uuid}_{name}", "0000fee0_Best Track.gpx"},
{"{name}_{uuid}", "Best Track_0000fee0.gpx"},
{"{time}_{name}", "02_02_02_Best Track.gpx"},
{"{date}_{name}", "2020-02-02_Best Track.gpx"},
{ApplicationProvider.getApplicationContext().getString(R.string.export_filename_format_default), "2020-02-02_02_02_02_Best Track.gpx"},
});
}
private final TrackFilenameGenerator subject;
private final String expected;
public TrackFilenameGeneratorTest(String template, String expected) {
this.subject = new TrackFilenameGenerator(template);
this.expected = expected;
}
@Test
public void testFilenameTemplate() {
// given
Track track = new Track();
track.setName("Best Track");
track.setUuid(UUID.fromString("0000fee0-0000-1000-8000-00805f9b34fb"));
track.getTrackStatistics().setStartTime(Instant.parse("2020-02-02T02:02:02Z"));
// when
String filename = subject.format(track, TrackFileFormat.GPX);
// then
assertEquals(expected, filename);
}
}
@@ -0,0 +1,44 @@
package de.dennisguse.opentracks.io.file;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
import java.util.UUID;
import de.dennisguse.opentracks.data.models.Track;
//TODO Merge with TrackFilenameGeneratorTest whenever Junit5 gets available.
//https://github.com/android/android-test/issues/224
@RunWith(Parameterized.class)
public class TrackFilenameGeneratorTest2 {
@Parameterized.Parameters
public static Collection<String> data() {
return Arrays.asList(
"{name}_{starime}",
"{name",
"name}");
}
private final TrackFilenameGenerator subject;
public TrackFilenameGeneratorTest2(String template) {
this.subject = new TrackFilenameGenerator(template);
}
@Test(expected = TrackFilenameGenerator.TemplateInvalidException.class)
public void testFilenameTemplate() {
// given
Track track = new Track();
track.setName("Best Track");
track.setUuid(UUID.fromString("0000fee0-0000-1000-8000-00805f9b34fb"));
track.getTrackStatistics().setStartTime(Instant.parse("2020-02-02T02:02:02Z"));
// when
String filename = subject.format(track, TrackFileFormat.GPX);
}
}
@@ -28,14 +28,14 @@ public class KmlTrackExporterTest {
@Test
public void writeCloseSegment_only_write_sensordata_if_present() {
String expected = "<when>1970-01-01T00:00:00Z</when>\n" +
"<gx:coord/>\n" +
"<coord/>\n" +
"<when>1970-01-01T01:00:00+01:00</when>\n" +
"<gx:coord/>\n" +
"<coord/>\n" +
"<ExtendedData>\n" +
"<SchemaData schemaUrl=\"#schema\">\n" +
"</SchemaData>\n" +
"</ExtendedData>\n" +
"</gx:Track>\n";
"</Track>\n";
// given
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0));
@@ -9,6 +9,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.location.Location;
import android.net.Uri;
import android.os.Build;
import android.os.Looper;
import androidx.annotation.NonNull;
@@ -23,6 +24,7 @@ import androidx.test.rule.ServiceTestRule;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -59,7 +61,8 @@ import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
import de.dennisguse.opentracks.sensors.AltitudeSumManager;
import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
@@ -74,6 +77,8 @@ import de.dennisguse.opentracks.stats.TrackStatistics;
@RunWith(AndroidJUnit4.class)
public class ExportImportTest {
private static final String TAG = ExportImportTest.class.getSimpleName();
@Rule
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
@@ -143,7 +148,7 @@ public class ExportImportTest {
Distance sensorDistance = Distance.of(10); // recording distance interval
sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3, 14, 10, 15, 10, 1);
sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3, 14, 10, 13, 15, 10, 1);
service.insertMarker("Marker 1", "Marker 1 category", "Marker 1 desc", null);
// A sensor-only TrackPoint
@@ -151,27 +156,32 @@ public class ExportImportTest {
mockAltitudeChange(trackPointCreator, 1);
mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f);
trackPointCreator.setClock("2020-02-02T02:02:05Z");
trackPointCreator.setClock("2020-02-02T02:02:14Z");
mockBLESensorData(trackPointCreator, 15f, null, 66f, 3f, 50f);
trackPointCreator.setClock("2020-02-02T02:02:15Z");
mockBLESensorData(trackPointCreator, null, null, 66f, 3f, 50f);
trackPointCreator.setClock("2020-02-02T02:02:16Z");
mockBLESensorData(trackPointCreator, 5f, Distance.of(2), 66f, 3f, 50f); // Distance will be added to next TrackPoint
sendLocation(trackPointCreator, "2020-02-02T02:02:05Z", 3, 14.001, 10, 15, 10, 0);
sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3, 14.001, 10, 13, 15, 10, 0);
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
trackPointCreator.setClock("2020-02-02T02:02:06Z");
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, trackPointCreator));
service.pauseCurrentTrack();
trackPointCreator.setClock("2020-02-02T02:02:18Z");
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator));
service.endCurrentTrack();
trackPointCreator.setClock("2020-02-02T02:02:20Z");
service.resumeCurrentTrack();
trackPointCreator.setClock("2020-02-02T02:03:20Z");
service.resumeTrack(trackId);
sendLocation(trackPointCreator, "2020-02-02T02:02:21Z", 3, 14.002, 10, 15, 10, 0);
sendLocation(trackPointCreator, "2020-02-02T02:03:21Z", 3, 14.002, 10, 13, 15, 10, 0);
sendLocation(trackPointCreator, "2020-02-02T02:02:22Z", 3, 16, 10, 15, 10, 0);
sendLocation(trackPointCreator, "2020-02-02T02:03:22Z", 3, 16, 10, 13, 15, 10, 0);
sendLocation(trackPointCreator, "2020-02-02T02:02:23Z", 3, 16.001, 10, 15, 10, 0);
sendLocation(trackPointCreator, "2020-02-02T02:03:23Z", 3, 16.001, 10, 27, 15, 10, 0);
trackPointCreator.setClock("2020-02-02T02:02:24Z");
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, trackPointCreator));
trackPointCreator.setClock("2020-02-02T02:03:24Z");
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator));
service.endCurrentTrack();
Track track = contentProviderUtils.getTrack(trackId);
@@ -183,7 +193,7 @@ public class ExportImportTest {
track = contentProviderUtils.getTrack(trackId);
trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
markers = contentProviderUtils.getMarkers(trackId);
assertEquals(10, trackPoints.size());
assertEquals(12, trackPoints.size());
assertEquals(2, markers.size());
}
@@ -227,29 +237,29 @@ public class ExportImportTest {
// Time
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
assertEquals(Instant.parse("2020-02-02T02:02:02Z"), importedTrackStatistics.getStartTime());
assertEquals(Instant.parse("2020-02-02T02:02:24Z"), importedTrackStatistics.getStopTime());
assertEquals(Instant.parse("2020-02-02T02:03:24Z"), importedTrackStatistics.getStopTime());
TrackStatistics originalTrackStatistics = track.getTrackStatistics();
assertEquals(originalTrackStatistics.getTotalTime(), importedTrackStatistics.getTotalTime());
assertEquals(Duration.ofSeconds(7), importedTrackStatistics.getTotalTime());
assertEquals(Duration.ofSeconds(19), importedTrackStatistics.getTotalTime());
assertEquals(originalTrackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime());
assertEquals(Duration.ofSeconds(3), importedTrackStatistics.getMovingTime());
// Distance
assertEquals(originalTrackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance());
assertEquals(123.16, importedTrackStatistics.getTotalDistance().toM(), 0.01);
assertEquals(125.16, importedTrackStatistics.getTotalDistance().toM(), 0.01);
// Speed
assertEquals(originalTrackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed());
assertEquals(41.05, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(41.72, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(originalTrackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed());
assertEquals(17.59, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
assertEquals(6.58, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
assertEquals(originalTrackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed());
assertEquals(41.05, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(41.72, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
// Altitude
assertEquals(originalTrackStatistics.getMinAltitude(), importedTrackStatistics.getMinAltitude(), 0.01);
@@ -341,19 +351,19 @@ public class ExportImportTest {
.setAltitudeLoss(1f)
.setAltitudeGain(1f)
.setHorizontalAccuracy(Distance.of(10)),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:05Z"))
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:17Z"))
.setLatitude(3)
.setLongitude(14.001)
.setAltitude(10)
.setSpeed(Speed.of(5))
.setAltitudeLoss(1f)
.setAltitudeGain(1f)
.setSensorDistance(Distance.of(12))
.setSensorDistance(Distance.of(14))
.setHeartRate(66f)
.setPower(50f)
.setCadence(3f)
.setHorizontalAccuracy(Distance.of(10)),
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:21Z"))
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:03:21Z"))
.setLatitude(3)
.setLongitude(14.002)
.setAltitude(10)
@@ -361,7 +371,7 @@ public class ExportImportTest {
.setAltitudeGain(0f)
.setSpeed(Speed.of(15))
.setHorizontalAccuracy(Distance.of(10)),
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:22Z"))
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:03:22Z"))
.setLatitude(3)
.setLongitude(16)
.setAltitude(10)
@@ -369,7 +379,7 @@ public class ExportImportTest {
.setAltitudeGain(0f)
.setSpeed(Speed.of(15))
.setHorizontalAccuracy(Distance.of(10)),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:23Z"))
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:03:23Z"))
.setLatitude(3)
.setLongitude(16.001)
.setAltitude(10)
@@ -386,18 +396,18 @@ public class ExportImportTest {
// Time
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
assertEquals(Instant.parse("2020-02-02T02:02:03Z"), importedTrackStatistics.getStartTime());
assertEquals(Instant.parse("2020-02-02T02:02:23Z"), importedTrackStatistics.getStopTime());
assertEquals(Instant.parse("2020-02-02T02:03:23Z"), importedTrackStatistics.getStopTime());
assertEquals(Duration.ofSeconds(3), importedTrackStatistics.getTotalTime());
assertEquals(Duration.ofSeconds(3), importedTrackStatistics.getMovingTime());
assertEquals(Duration.ofSeconds(15), importedTrackStatistics.getTotalTime());
assertEquals(Duration.ofSeconds(15), importedTrackStatistics.getMovingTime());
// Distance
assertEquals(123.16, importedTrackStatistics.getTotalDistance().toM(), 0.01);
assertEquals(125.16, importedTrackStatistics.getTotalDistance().toM(), 0.01);
// Speed
assertEquals(41.05, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(41.05, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
assertEquals(41.05, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
assertEquals(8.34, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(8.34, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
assertEquals(8.34, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
// Altitude
assertEquals(10, importedTrackStatistics.getMinAltitude(), 0.01);
@@ -437,6 +447,7 @@ public class ExportImportTest {
assertNull(trackImported);
}
@Ignore(value = "TODO Fails on CI; works on API24 and API30 locally")
@LargeTest
@Test
public void csv_export_only() throws TimeoutException, IOException {
@@ -453,7 +464,7 @@ public class ExportImportTest {
contentProviderUtils.deleteTrack(context, trackId);
// then
InputStream expected = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.csv_export);
InputStream expected = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.csv_export);
String expectedText = new BufferedReader(new InputStreamReader(expected, StandardCharsets.UTF_8))
.lines()
.collect(Collectors.joining("\n"));
@@ -487,23 +498,23 @@ public class ExportImportTest {
}
private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) {
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, trackPointCreator) {
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator) {
@Override
public SensorDataSet fill(@NonNull TrackPoint trackPoint) {
SensorDataSet sensorDataSet = new SensorDataSet();
sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power)));
sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", HeartRate.of(heartRate)));
SensorDataCycling.CyclingCadence cyclingCadence = Mockito.mock(SensorDataCycling.CyclingCadence.class);
SensorDataCyclingCadence cyclingCadence = Mockito.mock(SensorDataCyclingCadence.class);
Mockito.when(cyclingCadence.hasValue()).thenReturn(true);
Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence));
sensorDataSet.set(cyclingCadence);
if (distance != null && speed != null) {
SensorDataCycling.DistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCycling.DistanceSpeed.Data.class);
SensorDataCyclingDistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCyclingDistanceSpeed.Data.class);
Mockito.when(distanceSpeedData.getDistanceOverall()).thenReturn(distance);
Mockito.when(distanceSpeedData.getSpeed()).thenReturn(Speed.of(speed));
SensorDataCycling.DistanceSpeed distanceSpeed = Mockito.mock(SensorDataCycling.DistanceSpeed.class);
SensorDataCyclingDistanceSpeed distanceSpeed = Mockito.mock(SensorDataCyclingDistanceSpeed.class);
Mockito.when(distanceSpeed.hasValue()).thenReturn(true);
Mockito.when(distanceSpeed.getValue()).thenReturn(distanceSpeedData);
sensorDataSet.set(distanceSpeed);
@@ -522,11 +533,14 @@ public class ExportImportTest {
altitudeSumManager.setAltitudeLoss_m(altitudeGain);
}
private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float speed, float altitude, float altitudeGain) {
private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, float altitude, float altitudeGain) {
Location location = new Location("mock");
location.setLatitude(latitude);
location.setLongitude(longitude);
location.setAccuracy(accuracy);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
location.setVerticalAccuracyMeters(verticalAccuracy);
}
location.setSpeed(speed);
location.setAltitude(altitude);
@@ -15,11 +15,16 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.stream.Collectors;
import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.data.ContentProviderUtils;
@@ -27,13 +32,15 @@ import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
import de.dennisguse.opentracks.stats.TrackStatistics;
/**
* Test that legacy KML/GPX formats can still be imported.
*/
@RunWith(JUnit4.class)
public class GPXImportTest {
public class GPXTrackImporterTest {
private final Context context = ApplicationProvider.getApplicationContext();
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
@@ -62,7 +69,7 @@ public class GPXImportTest {
public void gpx_with_pause_resume() throws IOException {
// given
XMLImporter importer = new XMLImporter(new GpxTrackImporter(context, trackImporter));
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.legacy_gpx_pause_resume);
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.legacy_gpx_pause_resume);
// when
// 1. import
@@ -116,7 +123,7 @@ public class GPXImportTest {
public void gpx_without_speed() throws IOException {
// given
XMLImporter importer = new XMLImporter(new GpxTrackImporter(context, trackImporter));
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.gpx11_without_speed);
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx11_without_speed);
// when
// 1. import
@@ -164,7 +171,7 @@ public class GPXImportTest {
public void gpx_speed_no_namespace() throws IOException {
// given
XMLImporter importer = new XMLImporter(new GpxTrackImporter(context, trackImporter));
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.gpx11_with_speed_no_namespace);
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx11_with_speed_no_namespace);
// when
// 1. import
@@ -181,7 +188,7 @@ public class GPXImportTest {
// 3. trackstatistics
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
assertEquals(4, trackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(4.0, trackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(Duration.ofSeconds(101), trackStatistics.getMovingTime());
// 4. trackpoints
@@ -208,4 +215,31 @@ public class GPXImportTest {
.setSpeed(Speed.of(3))
), importedTrackPoints);
}
@LargeTest
@Test
public void importExportTest_timezone() throws IOException {
// given
XMLImporter importer = new XMLImporter(new GpxTrackImporter(context, trackImporter));
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx_timezone);
InputStream inputStreamExpected = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx_timezone);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// when
// 1. import
importTrackId = importer.importFile(inputStream).get(0);
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
trackExporter.writeTrack(importedTrack, outputStream);
// then
String expected = new BufferedReader(
new InputStreamReader(inputStreamExpected, StandardCharsets.UTF_8))
.lines()
.collect(Collectors.joining("\n")) + "\n";
assertEquals(expected, outputStream.toString()); //TODO inputStream.readAllBytes() ?
}
}
@@ -31,7 +31,7 @@ import de.dennisguse.opentracks.data.models.TrackPoint;
* Test that legacy KML/GPX formats can still be imported.
*/
@RunWith(JUnit4.class)
public class KMLImportTest {
public class KMLTrackImporterTest {
private final Context context = ApplicationProvider.getApplicationContext();
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
@@ -60,7 +60,7 @@ public class KMLImportTest {
public void kml22_order_location_and_when() throws IOException {
// given
XMLImporter importer = new XMLImporter(new KmlTrackImporter(context, trackImporter));
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.kml22_order_location_and_when);
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.kml22_order_location_and_when);
// when
// 1. import
@@ -109,7 +109,7 @@ public class KMLImportTest {
public void kml22_time_decreases() throws IOException {
// given
XMLImporter importer = new XMLImporter(new KmlTrackImporter(context, trackImporter));
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.kml22_time_decreases);
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.kml22_time_decreases);
// when
// 1. import
@@ -149,7 +149,7 @@ public class KMLImportTest {
public void kml22_with_statistics_marker() throws IOException {
// given
XMLImporter importer = new XMLImporter(new KmlTrackImporter(context, trackImporter));
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.legacy_kml22_statistics_marker);
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.legacy_kml22_statistics_marker);
// when
// 1. import
@@ -223,7 +223,7 @@ public class KMLImportTest {
public void kml_without_locations() throws IOException {
// given
XMLImporter importer = new XMLImporter(new KmlTrackImporter(context, trackImporter));
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.kml22_without_locations);
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.kml22_without_locations);
// when
importTrackId = importer.importFile(inputStream).get(0);
@@ -234,7 +234,7 @@ public class KMLImportTest {
public void kml_when_locations_different() throws IOException {
// given
XMLImporter importer = new XMLImporter(new KmlTrackImporter(context, trackImporter));
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.kml22_when_locations_different);
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.kml22_when_locations_different);
// when
importTrackId = importer.importFile(inputStream).get(0);
@@ -47,6 +47,10 @@ public class TrackPointAssert {
if (expected.hasHorizontalAccuracy()) {
Assert.assertEquals(expected.getHorizontalAccuracy().toM(), actual.getHorizontalAccuracy().toM(), delta);
}
Assert.assertEquals(expected.hasVerticalAccuracy(), actual.hasVerticalAccuracy());
if (expected.hasVerticalAccuracy()) {
Assert.assertEquals(expected.getVerticalAccuracy().toM(), actual.getVerticalAccuracy().toM(), delta);
}
Assert.assertEquals(expected.hasSensorDistance(), actual.hasSensorDistance());
if (expected.hasSensorDistance()) {
@@ -10,7 +10,8 @@ import org.junit.Test;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning;
public class BluetoothUtilsTest {
@@ -18,7 +19,7 @@ public class BluetoothUtilsTest {
@Test
public void parseHeartRate_uint8() {
// given
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEART_RATE_SERVICE_UUID, 0, 0);
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.getServiceUUID(), 0, 0);
characteristic.setValue(new byte[]{0x02, 0x3C});
// when
@@ -31,7 +32,7 @@ public class BluetoothUtilsTest {
@Test
public void parseHeartRate_uint16() {
// given
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEART_RATE_SERVICE_UUID, 0, 0);
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.getServiceUUID(), 0, 0);
characteristic.setValue(new byte[]{0x01, 0x01, 0x01});
// when
@@ -43,11 +44,11 @@ public class BluetoothUtilsTest {
@Test
public void parseCyclingSpeedCadence_crankOnly() {
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID, 0, 0);
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0);
characteristic.setValue(new byte[]{0x02, (byte) 0xC8, 0x00, 0x00, 0x00, 0x06, (byte) 0x99});
// when
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
// then
assertNull(sensor.getDistanceSpeed());
@@ -56,11 +57,11 @@ public class BluetoothUtilsTest {
@Test
public void parseCyclingSpeedCadence_wheelOnly() {
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID, 0, 0);
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0);
characteristic.setValue(new byte[]{0x01, (byte) 0xFF, (byte) 0xFF, 0, 1, 0x45, (byte) 0x99});
// when
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
// then
assertEquals(65535 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount());
@@ -69,11 +70,11 @@ public class BluetoothUtilsTest {
@Test
public void parseCyclingSpeedCadence_crankWheel() {
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID, 0, 0);
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0);
characteristic.setValue(new byte[]{0x03, (byte) 0xC8, 0x00, 0x00, 0x01, 0x06, (byte) 0x99, (byte) 0xE1, 0x00, 0x45, (byte) 0x99});
// when
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
// then
assertEquals(200 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount());
@@ -82,19 +83,34 @@ public class BluetoothUtilsTest {
@Test
public void parseCyclingPower_power() {
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER_MEASUREMENT_CHAR_UUID, 0, 0);
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.getServiceUUID(), 0, 0);
characteristic.setValue(new byte[]{0, 0, 40, 0});
// when
int power_w = BluetoothUtils.parseCyclingPower(characteristic);
SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic);
// then
assertEquals(40, power_w);
assertEquals(40, powerCadence.getPower().getValue().getW(), 0.01);
}
@Test
public void parseCyclingPower_power_with_cadence() {
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.getServiceUUID(), 0, 0);
characteristic.setValue(new byte[]{0x2C, 0x00, 0x00, 0x00, (byte) 0x9F, 0x00, 0x0C, 0x00, (byte) 0xE5, 0x42});
// when
SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic);
// then
assertEquals(0, powerCadence.getPower().getValue().getW(), 0.01);
assertEquals(12, powerCadence.getCadence().getCrankRevolutionsCount());
assertEquals(17125, powerCadence.getCadence().getCrankRevolutionsTime());
}
@Test
public void parseRunningSpeedAndCadence_with_distance() {
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.RUNNING_RUNNING_SPEED_CADENCE_CHAR_UUID, 0, 0);
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.RUNNING_SPEED_CADENCE.getServiceUUID(), 0, 0);
characteristic.setValue(new byte[]{2, 0, 5, 80, (byte) 0xFF, (byte) 0xFF, 0, 1});
// when
@@ -21,8 +21,8 @@ public class SensorDataCyclingTest {
@Test
public void compute_cadence_1() {
// given
SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 1024); // 1s
SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 2, 2048); // 2s
SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 1024); // 1s
SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 2048); // 2s
// when
current.compute(previous);
@@ -34,8 +34,8 @@ public class SensorDataCyclingTest {
@Test
public void compute_cadence_2() {
// given
SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 6184);
SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 2, 8016);
SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 6184);
SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 8016);
// when
current.compute(previous);
@@ -47,8 +47,8 @@ public class SensorDataCyclingTest {
@Test
public void compute_cadence_sameCount() {
// given
SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 1024);
SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 2048);
SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 1024);
SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 2048);
// when
current.compute(previous);
@@ -61,8 +61,8 @@ public class SensorDataCyclingTest {
@Test
public void compute_cadence_sameTime() {
// given
SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 1024);
SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 2, 1024);
SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 1024);
SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 1024);
// when
current.compute(previous);
@@ -74,8 +74,8 @@ public class SensorDataCyclingTest {
@Test
public void compute_cadence_rollOverTime() {
// given
SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, UintUtils.UINT16_MAX - 1024);
SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 2, 0);
SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, UintUtils.UINT16_MAX - 1024);
SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 0);
// when
current.compute(previous);
@@ -89,8 +89,8 @@ public class SensorDataCyclingTest {
@Deprecated
public void compute_cadence_rollOverCount() {
// given
SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 0, 2048);
SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 0, 2048);
// when
current.compute(previous);
@@ -102,8 +102,8 @@ public class SensorDataCyclingTest {
@Test
public void compute_cadence_overflow() {
// given
SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 0, 2048);
SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 0, 2048);
// when
current.compute(previous);
@@ -115,8 +115,8 @@ public class SensorDataCyclingTest {
@Test
public void compute_speed() {
// given
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 1, 6184);
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 2, 8016);
SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 1, 6184);
SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 2, 8016);
// when
current.compute(previous, Distance.ofMM(2150));
@@ -131,8 +131,8 @@ public class SensorDataCyclingTest {
@Deprecated
public void compute_speed_rollOverCount() {
// given
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048);
SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 0, 2048);
// when
current.compute(previous, Distance.ofMM(2000));
@@ -145,8 +145,8 @@ public class SensorDataCyclingTest {
@Test
public void compute_speed_overflow() {
// given
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048);
SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 0, 2048);
// when
current.compute(previous, Distance.ofMM(2000));
@@ -158,8 +158,8 @@ public class SensorDataCyclingTest {
@Test
public void equals_speed_with_no_data() {
// given
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress");
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048);
SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress");
SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 0, 2048);
// when
previous.toString();
@@ -172,8 +172,8 @@ public class SensorDataCyclingTest {
@Test
public void equals_cadence_with_no_data() {
// given
SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress");
SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 0, 2048);
SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress");
SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 0, 2048);
// when
previous.toString();
@@ -19,6 +19,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.TrackStatistics;
@RunWith(MockitoJUnitRunner.class)
@@ -48,7 +49,7 @@ public class TrackRecordingServiceNotificationManagerTest {
.thenReturn(notificationCompatBuilder);
TrackRecordingServiceNotificationManager subject = new TrackRecordingServiceNotificationManager(notificationManager, notificationCompatBuilder);
subject.setMetricUnits(true);
subject.setUnitSystem(UnitSystem.METRIC);
// when
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, Distance.of(100));
@@ -31,12 +31,14 @@ import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.io.file.importer.TrackPointAssert;
import de.dennisguse.opentracks.sensors.AltitudeSumManager;
import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning;
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
import de.dennisguse.opentracks.settings.PreferencesUtils;
@@ -159,7 +161,7 @@ public class TrackRecordingServiceTestRecording {
// when
String pauseTime = "2020-02-02T02:02:03Z";
trackPointCreator.setClock(pauseTime);
service.pauseCurrentTrack();
service.endCurrentTrack();
// then
assertEquals(new TrackStatistics(startTime, pauseTime, 0, 1, 0, 0, 0f, 0f)
@@ -205,7 +207,7 @@ public class TrackRecordingServiceTestRecording {
String pauseTime = "2020-02-02T02:02:03Z";
trackPointCreator.setClock(pauseTime);
service.pauseCurrentTrack();
service.endCurrentTrack();
// when
trackPointCreator.setClock("2020-02-02T02:02:04Z");
@@ -257,6 +259,47 @@ public class TrackRecordingServiceTestRecording {
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
}
@MediumTest
@Test
public void testRecording_blesensor_only_no_distance() {
// given
String startTime = "2020-02-02T02:02:02Z";
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
trackPointCreator.setClock(startTime);
Track.Id trackId = service.startNewTrack();
trackPointCreator.stopGPS();
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
BluetoothRemoteSensorManager remoteSensorManager = trackPointCreator.getRemoteSensorManager();
// when
String sensor1 = "2020-02-02T02:02:03Z";
trackPointCreator.setClock(sensor1);
remoteSensorManager.onChanged(new SensorDataHeartRate("", "", HeartRate.of(5))); //Should be ignored
String sensor3 = "2020-02-02T02:02:13Z";
trackPointCreator.setClock(sensor3);
remoteSensorManager.onChanged(new SensorDataHeartRate("", "", HeartRate.of(7)));
String stopTime = "2020-02-02T02:02:15Z";
trackPointCreator.setClock(stopTime);
service.endCurrentTrack();
// then
new TrackPointAssert().assertEquals(List.of(
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
new TrackPoint(TrackPoint.Type.SENSORPOINT, Instant.parse(sensor3))
.setAltitudeGain(0f)
.setAltitudeLoss(0f)
.setHeartRate(HeartRate.of(7)),
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
.setAltitudeGain(0f)
.setAltitudeLoss(0f)
.setHeartRate(HeartRate.of(7))
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
}
@MediumTest
@Test
public void testRecording_gpsOnly_recordingDistance_above() {
@@ -143,7 +143,7 @@ public class TrackRecordingServiceTestStateMachine {
@MediumTest
@Test
public void recording_startPauseStopResume_no_data() throws InterruptedException {
public void recording_startStopResume_no_data() throws InterruptedException {
// given
assertFalse(service.isRecording());
@@ -153,31 +153,19 @@ public class TrackRecordingServiceTestStateMachine {
// then
assertTrue(service.isRecording());
assertEquals(new RecordingStatus(trackId, false), service.getRecordingStatusObservable().getValue());
assertEquals(new RecordingStatus(trackId), service.getRecordingStatusObservable().getValue());
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
service.endCurrentTrack();
// when
service.pauseCurrentTrack();
service.resumeTrack(trackId);
Thread.sleep(1000);
// then
assertTrue(service.isRecording());
assertTrue(service.isPaused());
assertEquals(new RecordingStatus(trackId, true), service.getRecordingStatusObservable().getValue());
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue()); //TODO The GPS is kept in pause, right? Might be a bug.
// when
service.resumeCurrentTrack();
Thread.sleep(1000);
// then
assertTrue(service.isRecording());
assertFalse(service.isPaused());
assertEquals(new RecordingStatus(trackId, false), service.getRecordingStatusObservable().getValue());
assertEquals(new RecordingStatus(trackId), service.getRecordingStatusObservable().getValue());
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
@@ -188,7 +176,6 @@ public class TrackRecordingServiceTestStateMachine {
// then
assertFalse(service.isRecording());
assertFalse(service.isPaused());
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
@@ -200,8 +187,7 @@ public class TrackRecordingServiceTestStateMachine {
// then
assertTrue(service.isRecording());
assertFalse(service.isPaused());
assertEquals(new RecordingStatus(trackId, false), service.getRecordingStatusObservable().getValue());
assertEquals(new RecordingStatus(trackId), service.getRecordingStatusObservable().getValue());
Thread.sleep(1000); //TODO Figure out why we need to wait here until the update is happening
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
@@ -209,29 +195,31 @@ public class TrackRecordingServiceTestStateMachine {
@MediumTest
@Test
public void invalidStateChanges() throws InterruptedException {
public void cannotResume_non_existing_track() throws InterruptedException {
// given
assertFalse(service.isRecording());
// when
service.pauseCurrentTrack();
Thread.sleep(2000);
// then
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
// when
service.resumeCurrentTrack();
Thread.sleep(1000);
service.resumeTrack(new Track.Id(-1));
// then
assertFalse(service.isRecording());
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
}
@MediumTest
@Test
public void cannotEnd_without_starting() throws InterruptedException {
// given
assertFalse(service.isRecording());
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
// when
service.endCurrentTrack();
@@ -257,8 +245,7 @@ public class TrackRecordingServiceTestStateMachine {
// then
assertTrue(service.isRecording());
assertFalse(service.isPaused());
assertEquals(new RecordingStatus(trackId, false), service.getRecordingStatusObservable().getValue());
assertEquals(new RecordingStatus(trackId), service.getRecordingStatusObservable().getValue());
Thread.sleep(1000); //TODO Figure out why we need to wait here until the update is happening
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
@@ -27,7 +27,7 @@ public class TrackRecordingServiceTestUtils {
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)))
.getService();
service.getTrackPointCreator().setRemoteSensorManager(new BluetoothRemoteSensorManager(context, service.getTrackPointCreator()));
service.getTrackPointCreator().setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, service.getTrackPointCreator()));
service.getTrackPointCreator().setClock(Clock.systemUTC());
service.endCurrentTrack();
}
@@ -25,6 +25,7 @@ import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.SensorStatistics;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.ui.intervals.IntervalStatistics;
@@ -41,7 +42,13 @@ public class VoiceAnnouncementUtilsTest {
@Before
public void setUp() {
contentProviderUtils = new ContentProviderUtils(context);
PreferencesUtils.setVoiceAnnounceHeartRate(false);
PreferencesUtils.setVoiceAnnounceLapHeartRate(false);
PreferencesUtils.setVoiceAnnounceAverageHeartRate(false);
PreferencesUtils.setVoiceAnnounceTotalDistance(true);
PreferencesUtils.setVoiceAnnounceMovingTime(true);
PreferencesUtils.setVoiceAnnounceAverageSpeedPace(true);
PreferencesUtils.setVoiceAnnounceLapSpeedPace(true);
}
@Test
@@ -54,10 +61,58 @@ public class VoiceAnnouncementUtilsTest {
stats.setTotalAltitudeGain(6000f);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, true, null, null).toString();
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
// then
assertEquals("Total distance 20.00 kilometers. 1 hour 5 minutes 10 seconds. Speed 18.4 kilometers per hour.", announcement);
assertEquals("Total distance 20.00 kilometers. 1 hour 5 minutes 10 seconds. Average moving speed 18.4 kilometers per hour.", announcement);
}
@Test
public void getAnnouncement_metric_speed_rounding_check() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(20000));
stats.setTotalTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
stats.setMovingTime(Duration.ofHours(1).plusSeconds(1));
stats.setMaxSpeed(Speed.of(100));
stats.setTotalAltitudeGain(6000f);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
// then
assertEquals("Total distance 20.00 kilometers. 1 hour 1 second. Average moving speed 20.0 kilometers per hour.", announcement);
}
@Test
public void getAnnouncement_metric_distance_rounding_check() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(19999));
stats.setTotalTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
stats.setMovingTime(Duration.ofHours(1));
stats.setMaxSpeed(Speed.of(100));
stats.setTotalAltitudeGain(6000f);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
// then
assertEquals("Total distance 20.00 kilometers. 1 hour. Average moving speed 20.0 kilometers per hour.", announcement);
}
@Test
public void getAnnouncement_metric_distance_rounding_check_two() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(19990));
stats.setTotalTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
stats.setMovingTime(Duration.ofHours(1));
stats.setMaxSpeed(Speed.of(100));
stats.setTotalAltitudeGain(6000f);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
// then
assertEquals("Total distance 19.99 kilometers. 1 hour. Average moving speed 20.0 kilometers per hour.", announcement);
}
@Test
@@ -76,10 +131,10 @@ public class VoiceAnnouncementUtilsTest {
}
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, true, lastInterval, null).toString();
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, null).toString();
// then
assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour.", announcement);
assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Average moving speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour.", announcement);
}
@Test
@@ -92,7 +147,7 @@ public class VoiceAnnouncementUtilsTest {
stats.setTotalAltitudeGain(6000f);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, false, null, null).toString();
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, false, null, null).toString();
// then
assertEquals("Total distance 20.00 kilometers. 1 hour 5 minutes 10 seconds. Pace 3 minutes 15 seconds per kilometer.", announcement);
@@ -114,7 +169,7 @@ public class VoiceAnnouncementUtilsTest {
}
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, false, lastInterval, null).toString();
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, false, lastInterval, null).toString();
// then
assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Pace 1 minute 10 seconds per kilometer. Lap time 1 minute 10 seconds per kilometer.", announcement);
@@ -130,10 +185,10 @@ public class VoiceAnnouncementUtilsTest {
stats.setTotalAltitudeGain(6000f);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, true, null, null).toString();
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, true, null, null).toString();
// then
assertEquals("Total distance 12.43 miles. 1 hour 5 minutes 10 seconds. Speed 11.4 miles per hour.", announcement);
assertEquals("Total distance 12.43 miles. 1 hour 5 minutes 10 seconds. Average moving speed 11.4 miles per hour.", announcement);
}
@Test
@@ -152,10 +207,10 @@ public class VoiceAnnouncementUtilsTest {
}
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, true, lastInterval, null).toString();
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, true, lastInterval, null).toString();
// then
assertEquals("Total distance 8.83 miles. 16 minutes 39 seconds. Speed 31.8 miles per hour. Lap speed 31.8 miles per hour.", announcement);
assertEquals("Total distance 8.83 miles. 16 minutes 39 seconds. Average moving speed 31.8 miles per hour. Lap speed 31.8 miles per hour.", announcement);
}
@Test
@@ -168,7 +223,7 @@ public class VoiceAnnouncementUtilsTest {
stats.setTotalAltitudeGain(6000f);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, false, null, null).toString();
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, false, null, null).toString();
// then
assertEquals("Total distance 12.43 miles. 1 hour 5 minutes 10 seconds. Pace 5 minutes 15 seconds per mile.", announcement);
@@ -190,7 +245,7 @@ public class VoiceAnnouncementUtilsTest {
}
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, false, lastInterval, null).toString();
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, false, lastInterval, null).toString();
// then
assertEquals("Total distance 8.83 miles. 16 minutes 39 seconds. Pace 1 minute 53 seconds per mile. Lap time 1 minute 53 seconds per mile.", announcement);
@@ -198,7 +253,8 @@ public class VoiceAnnouncementUtilsTest {
@Test
public void getAnnouncement_heart_rate_and_sensor_statistics() {
PreferencesUtils.setVoiceAnnounceHeartRate(true);
PreferencesUtils.setVoiceAnnounceAverageHeartRate(true);
PreferencesUtils.setVoiceAnnounceLapHeartRate(true);
int numberOfPoints = 1000;
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
@@ -215,9 +271,39 @@ public class VoiceAnnouncementUtilsTest {
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, true, lastInterval, sensorStatistics).toString();
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
// then
assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour. Average heart rate 180 bpm. Current heart rate 133 bpm.", announcement);
assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Average moving speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour. Average heart rate 180 bpm. Current heart rate 133 bpm.", announcement);
}
@Test
public void getAnnouncement_only_lap_heart_rate() {
PreferencesUtils.setVoiceAnnounceLapHeartRate(true);
PreferencesUtils.setVoiceAnnounceAverageHeartRate(false);
PreferencesUtils.setVoiceAnnounceTotalDistance(false);
PreferencesUtils.setVoiceAnnounceMovingTime(false);
PreferencesUtils.setVoiceAnnounceAverageSpeedPace(false);
PreferencesUtils.setVoiceAnnounceLapSpeedPace(false);
int numberOfPoints = 1000;
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
Track.Id trackId = trackWithStats.first;
TrackStatistics stats = trackWithStats.second;
IntervalStatistics.Interval lastInterval;
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
assertEquals(trackPointIterator.getCount(), numberOfPoints);
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
intervalStatistics.addTrackPoints(trackPointIterator);
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
}
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
// then
assertEquals(" Current heart rate 133 bpm.", announcement);
}
}
@@ -32,7 +32,7 @@ public class TrackPointCreatorTest {
@Before
public void setUp() {
subject = new TrackPointCreator(locationHandler, server);
subject.start(context);
subject.start(context, null);
}
@After
@@ -58,7 +58,7 @@ public class PreferencesUtilsTest {
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat();
// then
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES, trackFileFormat);
}
@Test
@@ -73,7 +73,7 @@ public class PreferencesUtilsTest {
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat();
// then
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES, trackFileFormat);
}
@Test
@@ -99,7 +99,7 @@ public class DescriptionGeneratorTest {
+ "Min elevation: -500 m (-1640 ft)<br>"
+ "Elevation gain: 6000 m (19685 ft)<br>"
+ "Elevation loss: 6000 m (19685 ft)<br>"
+ "Recorded: " + StringUtils.formatDateTime(OffsetDateTime.ofInstant(START_TIME, ZoneId.systemDefault())) + "<br>";
+ "Recorded: " + StringUtils.formatDateTimeWithOffset(OffsetDateTime.ofInstant(START_TIME, ZoneId.systemDefault())) + "<br>";
assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
}
@@ -135,7 +135,7 @@ public class DescriptionGeneratorTest {
+ "Fastest pace: 0:10 min/km (0:16 min/mi)<br>"
+ "Elevation gain: 6000 m (19685 ft)<br>"
+ "Elevation loss: 6000 m (19685 ft)<br>"
+ "Recorded: " + StringUtils.formatDateTime(OffsetDateTime.ofInstant(START_TIME, ZoneId.systemDefault())) + "<br>";
+ "Recorded: " + StringUtils.formatDateTimeWithOffset(OffsetDateTime.ofInstant(START_TIME, ZoneId.systemDefault())) + "<br>";
assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
}
@@ -11,6 +11,7 @@ import org.junit.runner.RunWith;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.data.models.Altitude;
@@ -86,7 +87,7 @@ public class TrackStatisticsUpdaterTest {
assertEquals(Duration.ofSeconds(9), statistics.getMovingTime());
assertEquals(2.5, statistics.getMinAltitude(), 0.01);
assertEquals(28.75, statistics.getMaxAltitude(), 0.01);
assertEquals(32.5, statistics.getMaxAltitude(), 0.01);
assertEquals(36, statistics.getTotalAltitudeGain(), 0.01);
assertEquals(36, statistics.getTotalAltitudeLoss(), 0.01);
@@ -245,6 +246,70 @@ public class TrackStatisticsUpdaterTest {
public void addTrackPoint_speed_from_GPS_moving_and_sensor_speed() {
}
@Test
public void addTrackPoint_maxSpeed_ignore_above_acceleration() {
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
assertEquals(Speed.of(0f), subject.getTrackStatistics().getMaxSpeed());
subject.addTrackPoint(new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0)));
assertEquals(Speed.of(0f), subject.getTrackStatistics().getMaxSpeed());
// Ignore as we set max speed if two consecutive trackpoints were considered moving
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(1))
.setSpeed(Speed.of(1f)));
assertEquals(Speed.of(0f), subject.getTrackStatistics().getMaxSpeed());
// Update max speed
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(2))
.setSpeed(Speed.of(1f)));
assertEquals(Speed.of(1f), subject.getTrackStatistics().getMaxSpeed());
// Update max speed
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(12))
.setSpeed(Speed.of(50f)));
assertEquals(Speed.of(50f), subject.getTrackStatistics().getMaxSpeed());
// Ignore; we were getting slower
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(13))
.setSpeed(Speed.of(5f)));
assertEquals(Speed.of(50f), subject.getTrackStatistics().getMaxSpeed());
// Ignore acceleration above 2g
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(14))
.setSpeed(Speed.of(500f)));
assertEquals(Speed.of(50f), subject.getTrackStatistics().getMaxSpeed());
}
@Test
public void addTrackPoint_maxSpeed_multiple_segments() {
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
assertEquals(Speed.of(0f), subject.getTrackStatistics().getMaxSpeed());
subject.addTrackPoints(List.of(
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0)),
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(1))
.setSpeed(Speed.of(2f)),
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(2))
.setSpeed(Speed.of(2f)),
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochSecond(4))
));
assertEquals(Speed.of(2f), subject.getTrackStatistics().getMaxSpeed());
// when
subject.addTrackPoints(List.of(
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(5)),
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(6))
.setSpeed(Speed.of(1f)),
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(7))
.setSpeed(Speed.of(1f)),
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochSecond(8))
));
// then
assertEquals(Speed.of(2f), subject.getTrackStatistics().getMaxSpeed());
}
@Test
public void copy_constructor() {
// given
@@ -59,13 +59,13 @@ public class EspressoAggregatedFilterTest {
@Test
public void espressoAggregatedFilterTest() {
// open AggregatedStatisticsActivity through toolbar's menu item
onView(ViewMatchers.withId(R.id.toolbar)).check(matches(isDisplayed()));
onView(withId(R.id.toolbar)).check(matches(hasDescendant(withId(R.id.track_list_aggregated_stats))));
onView(ViewMatchers.withId(R.id.bottom_app_bar)).check(matches(isDisplayed()));
onView(withId(R.id.bottom_app_bar)).check(matches(hasDescendant(withId(R.id.track_list_aggregated_stats))));
onView(withId(R.id.track_list_aggregated_stats)).perform(click());
// open FilterDialogFragment through toolbar's menu item
onView(withId(R.id.toolbar)).check(matches(isDisplayed()));
onView(withId(R.id.toolbar)).check(matches(hasDescendant(withId(R.id.aggregated_statistics_filter))));
onView(withId(R.id.bottom_app_bar)).check(matches(isDisplayed()));
onView(withId(R.id.bottom_app_bar)).check(matches(hasDescendant(withId(R.id.aggregated_statistics_filter))));
onView(withId(R.id.aggregated_statistics_filter)).perform(click());
// check there's a checkbox with CATEGORY text
@@ -3,10 +3,10 @@ package de.dennisguse.opentracks.ui.customRecordingLayout;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.longClick;
import static androidx.test.espresso.matcher.ViewMatchers.assertThat;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.CoreMatchers.is;
import static de.dennisguse.opentracks.util.EspressoUtils.veryLongTouch;
import android.view.View;
@@ -41,7 +41,7 @@ public class EspressoCustomLayoutTest {
@Test
public void customLayoutTest() {
// TrackListActivity: start recording
ViewInteraction trackControllerRecordButton = onView(ViewMatchers.withId(R.id.controller_record));
ViewInteraction trackControllerRecordButton = onView(ViewMatchers.withId(R.id.track_list_fab_action));
trackControllerRecordButton.perform(click());
// Get custom layout preferences and check all data fields are showed.
@@ -50,8 +50,8 @@ public class EspressoCustomLayoutTest {
onView(withId(R.id.stats_recycler_view)).check(new RecyclerViewItemCountAssertion((int) layout.getFields().stream().filter(DataField::isVisible).count()));
// stop recording
ViewInteraction trackControllerStopButton = onView(withId(R.id.controller_stop));
trackControllerStopButton.perform(veryLongTouch(1600));
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
trackControllerStopButton.perform(longClick());
}
private static class RecyclerViewItemCountAssertion implements ViewAssertion {
@@ -21,7 +21,6 @@ import de.dennisguse.opentracks.data.TrackPointIterator;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.data.models.UnitConversions;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
@@ -180,7 +179,7 @@ public class IntervalStatisticsTest {
// then
assertEquals(trackStatistics.getTotalDistance().toM(), totalDistance.toM(), 0.01);
assertEquals(trackStatistics.getTotalTime().toMillis(), totalTime * UnitConversions.S_TO_MS, 1);
assertEquals(trackStatistics.getTotalTime().toSeconds(), totalTime, 0.01);
assertEquals(intervalList.size(), (int) Math.ceil(trackStatistics.getTotalDistance().toM() / distanceInterval));
if (totalGain != null) {
assertEquals(totalGain, numberOfPoints * TestDataUtil.ALTITUDE_GAIN, 0.1);
@@ -142,7 +142,7 @@ public class EGM2008UtilsTest {
}
@Test
public void data_Berlin() throws IOException {
public void data_Berlin_Germany_() throws IOException {
// given
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochMilli(0));
trackPoint.setLatitude(52.530644);
@@ -157,7 +157,7 @@ public class EGM2008UtilsTest {
}
@Test
public void data_Berlin_Caching() throws IOException {
public void data_Berlin_Germany_Caching() throws IOException {
// given
TrackPoint trackPoint1 = new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochMilli(0));
trackPoint1.setLatitude(52.530644);
@@ -176,6 +176,22 @@ public class EGM2008UtilsTest {
assertNotEquals(altitude_egm2008.correctAltitude(trackPoint1.getLocation()), altitude_egm2008.correctAltitude(trackPoint2.getLocation()), 0.0001);
}
@Test
public void data_Seattle_USA() throws IOException {
// given
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochMilli(0));
trackPoint.setLatitude(47.63153);
trackPoint.setLongitude(-122.30938);
trackPoint.setAltitude(0);
// when
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
// then
assertEquals(22.99, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
}
@Test
public void data_MaxUndulation() throws IOException {
// given
@@ -191,6 +207,7 @@ public class EGM2008UtilsTest {
assertEquals(-85.824, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
}
@Test
public void getIndices() {
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochMilli(0));
@@ -208,7 +225,7 @@ public class EGM2008UtilsTest {
trackPoint.setLatitude(-90);
trackPoint.setLongitude(-180);
assertEquals(new EGM2008Utils.Indices(2160, 0), EGM2008Utils.getIndices(trackPoint.getLocation()));
assertEquals(new EGM2008Utils.Indices(2160, 2160), EGM2008Utils.getIndices(trackPoint.getLocation()));
}
@Test
@@ -1,21 +1,14 @@
package de.dennisguse.opentracks.util;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.util.TreeIterables.breadthFirstViewTraversal;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.ListView;
import androidx.test.espresso.PerformException;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.espresso.action.MotionEvents;
import androidx.test.espresso.util.HumanReadables;
import com.google.android.material.tabs.TabLayout;
@@ -23,8 +16,6 @@ import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import java.util.concurrent.TimeoutException;
public class EspressoUtils {
public static Matcher<View> childAtPosition(
@@ -80,76 +71,6 @@ public class EspressoUtils {
};
}
public static ViewAction veryLongTouch(final int duration_ms) {
return new ViewAction() {
@Override
public String getDescription() {
return "Perform long touch.";
}
@Override
public Matcher<View> getConstraints() {
return isDisplayed();
}
@Override
public void perform(UiController uiController, final View view) {
// Get view absolute position
int[] location = new int[2];
view.getLocationOnScreen(location);
// Offset coordinates by view position
float[] coordinates = new float[]{location[0] + 1, location[1] + 1};
// Send down event, pause, and send up
MotionEvent down = MotionEvents.sendDown(uiController, coordinates, new float[]{1f, 1f}).down;
uiController.loopMainThreadForAtLeast(duration_ms);
MotionEvents.sendUp(uiController, down, coordinates);
}
};
}
public static ViewAction waitId(final int viewId, final long millis) {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return isRoot();
}
@Override
public String getDescription() {
return "wait for a specific view with id <" + viewId + "> during " + millis + " millis.";
}
@Override
public void perform(final UiController uiController, final View view) {
uiController.loopMainThreadUntilIdle();
final long startTime = System.currentTimeMillis();
final long endTime = startTime + millis;
final Matcher<View> viewMatcher = withId(viewId);
do {
for (View child : breadthFirstViewTraversal(view)) {
// found view with required ID
if (viewMatcher.matches(child)) {
return;
}
}
uiController.loopMainThreadForAtLeast(50);
}
while (System.currentTimeMillis() < endTime);
// timeout happens
throw new PerformException.Builder()
.withActionDescription(this.getDescription())
.withViewDescription(HumanReadables.describe(view))
.withCause(new TimeoutException())
.build();
}
};
}
public static ViewAction selectTabAtIndex(final int index) {
return new ViewAction() {
@Override
@@ -1,63 +0,0 @@
package de.dennisguse.opentracks.util;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@RunWith(JUnit4.class)
public class ExportUtilsTest extends TestCase {
@Test
public void testIsExportFileExists_exists() {
// given
UUID uuid = UUID.randomUUID();
String format = "kmz";
List<String> fileNames = new ArrayList<>();
fileNames.add(uuid.toString().substring(0, 8) + "_name.gpx");
fileNames.add(uuid.toString().substring(0, 8) + "_other name name.kmz");
// when
boolean exists = ExportUtils.isExportFileExists(uuid, format, fileNames);
// then
assertTrue(exists);
}
@Test
public void testIsExportFileExists_not_exists1() {
// given
UUID uuid = UUID.randomUUID();
String format = "gpx";
List<String> fileNames = new ArrayList<>();
fileNames.add(UUID.randomUUID().toString().substring(0, 8) + "_name.gpx");
fileNames.add(UUID.randomUUID().toString().substring(0, 8) + "_other name name.gpx");
// when
boolean exists = ExportUtils.isExportFileExists(uuid, format, fileNames);
// then
assertFalse(exists);
}
@Test
public void testIsExportFileExists_not_exists2() {
// given
UUID uuid = UUID.randomUUID();
String format = "kmz";
List<String> fileNames = new ArrayList<>();
fileNames.add(uuid.toString().substring(0, 8) + "_name.gpx");
fileNames.add(UUID.randomUUID().toString().substring(0, 8) + "_other name name.gpx");
// when
boolean exists = ExportUtils.isExportFileExists(uuid, format, fileNames);
// then
assertFalse(exists);
}
}
@@ -41,13 +41,12 @@ import java.time.format.TextStyle;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
import java.util.stream.Collectors;
import de.dennisguse.opentracks.LocaleRule;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.TimezoneRule;
/**
* Tests for {@link StringUtils}.
@@ -60,6 +59,9 @@ public class StringUtilsTest {
@Rule
public final LocaleRule mLocaleRule = new LocaleRule(Locale.ENGLISH);
@Rule
public TimezoneRule timezoneRule = new TimezoneRule(TimeZone.getTimeZone("Europe/Berlin"));
private final Context context = ApplicationProvider.getApplicationContext();
/**
@@ -100,51 +102,13 @@ public class StringUtilsTest {
}
@Test
public void testGetTime_fractional() {
assertGetTime("2010-05-04T03:02:01.352Z", 2010, 5, 4, 3, 2, 1, 352);
assertGetTime("2010-05-04T03:02:01.3529Z", 2010, 5, 4, 3, 2, 1, 352);
}
public void testParseTime() {
assertEquals(Instant.ofEpochMilli(352), StringUtils.parseTime("1970-01-01T00:00:00.352").toInstant());
assertEquals(Instant.ofEpochMilli(352), StringUtils.parseTime("1970-01-01T00:00:00.352Z").toInstant());
assertEquals(Instant.ofEpochMilli(352), StringUtils.parseTime("1970-01-01T00:00:00.352+00:00").toInstant());
@Test
public void testGetTime_timezone() {
assertGetTime("2010-05-04T03:02:01", 2010, 5, 4, 3, 2, 1, 0);
assertGetTime("2010-05-04T03:02:01Z", 2010, 5, 4, 3, 2, 1, 0);
assertGetTime("2010-05-04T03:02:01+00:00", 2010, 5, 4, 3, 2, 1, 0);
assertGetTime("2010-05-04T03:02:01-00:00", 2010, 5, 4, 3, 2, 1, 0);
assertGetTime("2010-05-04T03:02:01+01:00", 2010, 5, 4, 2, 2, 1, 0);
assertGetTime("2010-05-04T03:02:01+10:30", 2010, 5, 3, 16, 32, 1, 0);
assertGetTime("2010-05-04T03:02:01-09:30", 2010, 5, 4, 12, 32, 1, 0);
assertGetTime("2010-05-04T03:02:01-05:00", 2010, 5, 4, 8, 2, 1, 0);
}
@Test
public void testGetTime_fractionalAndTimezone() {
assertGetTime("2010-05-04T03:02:01.352Z", 2010, 5, 4, 3, 2, 1, 352);
assertGetTime("2010-05-04T03:02:01.47+00:00", 2010, 5, 4, 3, 2, 1, 470);
assertGetTime("2010-05-04T03:02:01.5791+03:00", 2010, 5, 4, 0, 2, 1, 579);
assertGetTime("2010-05-04T03:02:01.8-05:30", 2010, 5, 4, 8, 32, 1, 800);
}
/**
* Asserts the {@link StringUtils#parseTime(String)} returns the expected values.
*
* @param xmlDateTime the xml date time string
* @param year the expected year
* @param month the expected month
* @param day the expected day
* @param hour the expected hour
* @param minute the expected minute
* @param second the expected second
* @param millisecond the expected milliseconds
*/
private void assertGetTime(String xmlDateTime, int year, int month, int day, int hour, int minute, int second, int millisecond) {
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
calendar.set(year, month - 1, day, hour, minute, second);
calendar.set(GregorianCalendar.MILLISECOND, millisecond);
// This comparision tends to be flaky (difference of 1ms)
// Assert.assertEquals(calendar.getTimeInMillis(), StringUtils.parseTime(xmlDateTime));
assertTrue(calendar.getTimeInMillis() + " vs. " + StringUtils.parseTime(xmlDateTime), Math.abs(calendar.getTimeInMillis() - StringUtils.parseTime(xmlDateTime).toInstant().toEpochMilli()) <= 1);
assertEquals(Instant.ofEpochMilli(352), StringUtils.parseTime("1970-01-01T01:00:00.352+01:00").toInstant());
assertEquals(Instant.ofEpochMilli(352).plus(Duration.ofHours(1)), StringUtils.parseTime("1970-01-01T00:00:00.352-01:00").toInstant());
}
@Test
@@ -167,27 +131,6 @@ public class StringUtilsTest {
assertEquals("1.0", StringUtils.formatDecimal(0.99, 1));
}
@Test
public void testGetSpeedParts() {
assertEquals("4:59", StringUtils.getSpeedParts(context, Speed.of(3.34), true, false).first);
assertEquals("5:00", StringUtils.getSpeedParts(context, Speed.of(3.33), true, false).first);
assertEquals("11.9", StringUtils.getSpeedParts(context, Speed.of(3.31), true, true).first);
assertEquals("7.5", StringUtils.getSpeedParts(context, Speed.of(3.34), false, true).first);
assertEquals("min/km", StringUtils.getSpeedParts(context, Speed.zero(), true, false).second);
assertEquals("min/mi", StringUtils.getSpeedParts(context, Speed.zero(), false, false).second);
}
@Test
public void testFormatSpeed() {
assertEquals("4:59 min/km", StringUtils.formatSpeed(context, Speed.of(3.34), true, false));
assertEquals("8:02 min/mi", StringUtils.formatSpeed(context, Speed.of(3.34), false, false));
assertEquals("12.0 km/h", StringUtils.formatSpeed(context, Speed.of(3.34), true, true));
assertEquals("7.5 mph", StringUtils.formatSpeed(context, Speed.of(3.34), false, true));
}
@Test
public void testFormatDateTodayRelative() {
// given
@@ -23,6 +23,7 @@ import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.services.RecordingData;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.stats.SensorStatistics;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
@@ -61,7 +62,7 @@ public class StatisticDataBuilderTest extends TestCase {
layout.addField(context.getString(R.string.stats_custom_layout_max_speed_key), context.getString(R.string.stats_max_speed), false, true, false);
// when
List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(context, recordingDataMock, layout, true);
List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(context, recordingDataMock, layout, UnitSystem.METRIC);
// then
assertEquals(statisticDataList.size(), 3);
@@ -100,7 +101,7 @@ public class StatisticDataBuilderTest extends TestCase {
layout.addField(context.getString(R.string.stats_custom_layout_power_key), context.getString(R.string.stats_sensors_power), true, true, false);
// when
List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(context, recordingDataMock, layout, true);
List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(context, recordingDataMock, layout, UnitSystem.METRIC);
// then
assertEquals(statisticDataList.size(), 18);
+11 -11
View File
@@ -1,11 +1,11 @@
#time,trackpoint_type,latitude,longitude,altitude,accuracy_horizontal,speed,altitude_gain,altitude_loss,sensor_distance,heartrate,cadence,power
"2020-02-02T03:02:02+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,
"2020-02-02T03:02:03+01:00","TRACKPOINT",3,3,10,10,54,1,1,,,,
"2020-02-02T03:02:04+01:00","SENSORPOINT",,,,,54,1,1,10,66,3,50
"2020-02-02T03:02:05+01:00","TRACKPOINT",3,3,10,10,18,0,0,2,66,3,50
"2020-02-02T03:02:06+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,
"2020-02-02T03:02:20+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,
"2020-02-02T03:02:21+01:00","TRACKPOINT",3,3,10,10,54,0,0,,,,
"2020-02-02T03:02:22+01:00","SEGMENT_START_AUTOMATIC",3,3,10,10,54,0,0,,,,
"2020-02-02T03:02:23+01:00","TRACKPOINT",3,3,10,10,54,0,0,,,,
"2020-02-02T03:02:24+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,
#time,trackpoint_type,latitude,longitude,altitude,accuracy_horizontal,accuracy_vertical,speed,altitude_gain,altitude_loss,sensor_distance,heartrate,cadence,power
"2020-02-02T03:02:02+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,,
"2020-02-02T03:02:03+01:00","TRACKPOINT",3,3,10,10,,54,1,1,,,,
"2020-02-02T03:02:04+01:00","SENSORPOINT",,,,,,54,1,1,10,66,3,50
"2020-02-02T03:02:05+01:00","TRACKPOINT",3,3,10,10,,18,0,0,2,66,3,50
"2020-02-02T03:02:06+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
"2020-02-02T03:02:20+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,,
"2020-02-02T03:02:21+01:00","TRACKPOINT",3,3,10,10,,54,0,0,,,,
"2020-02-02T03:02:22+01:00","SEGMENT_START_AUTOMATIC",3,3,10,10,,54,0,0,,,,
"2020-02-02T03:02:23+01:00","TRACKPOINT",3,3,10,10,,54,0,0,,,,
"2020-02-02T03:02:24+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
1 #time trackpoint_type latitude longitude altitude accuracy_horizontal accuracy_vertical speed altitude_gain altitude_loss sensor_distance heartrate cadence power
2 2020-02-02T03:02:02+01:00 SEGMENT_START_MANUAL
3 2020-02-02T03:02:03+01:00 TRACKPOINT 3 3 10 10 54 1 1
4 2020-02-02T03:02:04+01:00 SENSORPOINT 54 1 1 10 66 3 50
5 2020-02-02T03:02:05+01:00 TRACKPOINT 3 3 10 10 18 0 0 2 66 3 50
6 2020-02-02T03:02:06+01:00 SEGMENT_END_MANUAL
7 2020-02-02T03:02:20+01:00 SEGMENT_START_MANUAL
8 2020-02-02T03:02:21+01:00 TRACKPOINT 3 3 10 10 54 0 0
9 2020-02-02T03:02:22+01:00 SEGMENT_START_AUTOMATIC 3 3 10 10 54 0 0
10 2020-02-02T03:02:23+01:00 TRACKPOINT 3 3 10 10 54 0 0
11 2020-02-02T03:02:24+01:00 SEGMENT_END_MANUAL
+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<gpx
version="1.1"
creator="OpenTracks (Debug)"
xmlns="http://www.topografix.com/GPX/1/1"
xmlns:topografix="http://www.topografix.com/GPX/Private/TopoGrafix/0/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:opentracks="http://opentracksapp.com/xmlschemas/v1"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v2"
xmlns:gpxtrkx="http://www.garmin.com/xmlschemas/TrackStatsExtension/v1"
xmlns:cluetrust="http://www.cluetrust.com/Schemas/"
xmlns:pwr="http://www.garmin.com/xmlschemas/PowerExtension/v1"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.topografix.com/GPX/Private/TopoGrafix/0/1 http://www.topografix.com/GPX/Private/TopoGrafix/0/1/topografix.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v2 https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd http://www.garmin.com/xmlschemas/PowerExtension/v1 https://www8.garmin.com/xmlschemas/PowerExtensionv1.xsd http://www.garmin.com/xmlschemas/TrackStatsExtension/v1 http://www.cluetrust.com/Schemas http://www.cluetrust.com/Schemas/gpxdata10.xsd http://opentracksapp.com/xmlschemas/v1 http://opentracksapp.com/xmlschemas/OpenTracks_v1.xsd">
<wpt lat="3" lon="14">
<ele>10</ele>
<time>2021-01-07T21:51:59.179+01:15</time>
<name><![CDATA[Marker 1]]></name>
<desc><![CDATA[Marker 1 desc]]></desc>
<type><![CDATA[Marker 1 category]]></type>
</wpt>
<trk>
<name><![CDATA[2021-01-07 22:51]]></name>
<desc><![CDATA[the description]]></desc>
<type><![CDATA[the category]]></type>
<extensions>
<topografix:color>c0c0c0</topografix:color>
<opentracks:trackid>7002101e-4198-4613-8c24-544e01ca3981</opentracks:trackid>
<gpxtrkx:TrackStatsExtension>
<gpxtrkx:Distance>0.0</gpxtrkx:Distance>
<gpxtrkx:TimerTime>0</gpxtrkx:TimerTime>
<gpxtrkx:MovingTime>0</gpxtrkx:MovingTime>
<gpxtrkx:StoppedTime>0</gpxtrkx:StoppedTime>
<gpxtrkx:MaxSpeed>0.0</gpxtrkx:MaxSpeed>
<gpxtrkx:Ascent>null</gpxtrkx:Ascent>
<gpxtrkx:Descent>null</gpxtrkx:Descent>
</gpxtrkx:TrackStatsExtension>
</extensions>
<trkseg>
<trkpt lat="3" lon="14">
<ele>10</ele>
<time>2021-01-07T21:51:59.179+01:15</time>
</trkpt>
</trkseg>
</trk>
</gpx>
+29 -30
View File
@@ -15,7 +15,6 @@ limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="de.dennisguse.opentracks"
android:installLocation="auto">
<supports-screens
@@ -59,6 +58,8 @@ limitations under the License.
tools:targetApi="s" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:name=".Startup"
android:allowBackup="false"
@@ -77,14 +78,25 @@ limitations under the License.
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<!-- <activity-->
<!-- android:name=".publicapi.StopRecording"-->
<!-- android:exported="true"-->
<!-- android:theme="@style/SplashTheme">-->
<!-- <intent-filter>-->
<!-- <category android:name="android.intent.category.DEFAULT" />-->
<!-- </intent-filter>-->
<!-- </activity>-->
<activity
android:name=".publicapi.StopRecording"
android:exported="true"
android:theme="@style/SplashTheme">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".introduction.IntroductionActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".AboutActivity" />
@@ -309,13 +321,8 @@ limitations under the License.
<activity
android:name=".TrackListActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/SplashTheme">
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
@@ -324,6 +331,7 @@ limitations under the License.
</activity>
<activity android:name=".TrackRecordedActivity" />
<activity android:name=".TrackStoppedActivity" />
<activity
android:name=".TrackRecordingActivity"
@@ -347,21 +355,12 @@ limitations under the License.
<activity android:name=".settings.SettingsCustomLayoutEditActivity" />
<activity
android:name=".share.ShowOnMapProxyActivity$KML"
android:exported="true">
<intent-filter android:label="@string/open_track_as_KML">
<action android:name="Intent.OpenTracks-Dashboard" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".share.ShowOnMapProxyActivity$GPX"
android:exported="true">
<intent-filter android:label="@string/open_track_as_GPX">
<action android:name="Intent.OpenTracks-Dashboard" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
android:name=".ShowErrorActivity"
android:excludeFromRecents="true"
android:exported="false"
android:finishOnTaskLaunch="true"
android:launchMode="singleInstance"
android:process=":crash" />
<provider
android:name=".data.ShareContentProvider"

Some files were not shown because too many files have changed in this diff Show More