Compare commits

...

312 Commits

Author SHA1 Message Date
Dennis Guse ae4d2da262 Heartrate: ignore "0" if sent from sensor as this is an invalid value.
On import "0" is ignored.
2026-02-10 18:42:14 +01:00
Dennis Guse 708cbf0b2a Refactored Sensor subsystem 2026-02-10 18:30:15 +01:00
Dennis Guse 60e2fe57f9 Bugfix: SensorHandler may receive a connectionLost from BluetoothDriver without being connected first. 2026-02-10 18:13:13 +01:00
Dennis Guse 14cebf3fec Aggregator: there are no default values anymore. 2026-02-10 11:13:57 +01:00
Dennis Guse b651a8ca6a GPS is now optional: data source (internal, none) be selected in settings.
Migrated to GPSHandler.

Fixes #1279.
2026-02-10 10:07:41 +01:00
Dennis Guse d75472ef11 BluetoothLeSensorPreference was not adhering to configured default value. 2026-02-09 18:15:37 +01:00
Dennis Guse 0b849c3d55 CyclingDistanceSpeedHandler: uses wheel size (again).
Skipped while to SensorHandler: a5dcbec3961957dfc651cf4ffd62ec075324c0ba
2026-02-09 18:15:37 +01:00
Dennis Guse 1c767c41a7 DriverObserver: renamed methods and all drivers properly use onConnected, onConnectionLost and onDisconnected. 2026-02-09 18:15:37 +01:00
Dennis Guse dfa80d61d7 Restored functionality: sensor name/address is shown AFTER connecting. 2026-02-09 18:15:37 +01:00
Dennis Guse fa558c9ab7 Cleanup: SensorManager avoids instanceof for testing. 2026-02-09 18:15:37 +01:00
Dennis Guse a1a363698f SensorHandler now interacts with Aggregator. 2026-02-09 18:15:37 +01:00
Dennis Guse b79acfb942 Cleanup: SensorManager provides context and handler. 2026-02-09 18:15:37 +01:00
Dennis Guse 24ea718ff2 Cleanup: renamed GainManager to AltitudeChangeHandler. 2026-02-09 18:15:37 +01:00
Dennis Guse 6d187fa66d Cleanup: removed DriverObserver0 and moved sensor drivers to separate package. 2026-02-09 18:15:37 +01:00
Dennis Guse ea27db13fa Created RunningSpeedCadenceDistanceHandler. 2026-02-09 18:15:37 +01:00
Dennis Guse 29d2e7c78c Created CyclingPowerHandler. 2026-02-09 18:15:37 +01:00
Dennis Guse 4b7badbecc Created CyclingDistanceSpeedHandler. 2026-02-09 18:15:37 +01:00
Dennis Guse 0dcb8010e2 Created CyclingCadenceHandler. 2026-02-09 18:15:37 +01:00
Dennis Guse 0a9d4b207c Created TemperatureHandler. 2026-02-09 18:15:37 +01:00
Dennis Guse c6cb388ce4 Created HeartRateHandler. 2026-02-09 18:15:37 +01:00
Dennis Guse e6e009ba14 GainManager should only re-connect if sensor should be changed. 2026-02-09 18:15:37 +01:00
Dennis Guse f8aa456549 Extract SensorHandler from GainManager for re-use. 2026-02-09 18:15:37 +01:00
Dennis Guse 167d90332e Extract Driver for internal GPS to GpsInternal. 2026-02-09 18:15:37 +01:00
Dennis Guse 719a052f34 Cleanup: merged BluetoothRemoteSensorManager into SensorManager.
Preparation for refactoring.
2026-02-09 18:15:37 +01:00
Dennis Guse 244ca26210 Cleanup: created new DriverObserver interface. 2026-02-09 18:15:37 +01:00
Dennis Guse 476f3a517b Cleanup: GainManager now processes AggregatorBarometer directly (instead of SensorManager). 2026-02-09 18:15:37 +01:00
Dennis Guse 289e93eb5d Cleanup: moved AggregatorGPS to GPSManager. 2026-02-09 18:15:37 +01:00
Dennis Guse 18ff96733e Cleanup: moved Raw into Aggregator.
Preparation for refactoring.
2026-02-09 18:15:37 +01:00
Dennis Guse 0f2c5adf26 Cleanup: SensorManager.SensorDataChangedObserver inlined into SensorManager.
Preparation for refactoring.
2026-02-09 18:15:37 +01:00
Dennis Guse 74954df600 Cleanup: SensorManager.SensorDataChangedObserver inlined into SensorManager.
Preparation for refactoring.
2026-02-09 18:15:37 +01:00
Dennis Guse 1a14fed287 Cleanup: SensorDataAggregator inlined into SensorManager.
Preparation for refactoring.
2026-02-09 18:15:37 +01:00
Dennis Guse 744853ee91 Cleanup: removed SensorConnector interface.
Preparation for refactoring.
2026-02-09 18:15:37 +01:00
Dennis Guse 597e1b2c7f Release: v4.25.2 2026-02-09 07:51:06 +01:00
jimkats 8156993921 Translated using Weblate (Greek)
Currently translated at 51.0% (278 of 545 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/el/
2026-02-08 22:49:35 +01:00
Dennis Guse 9890dc28bd Dashboard API: provide version 2 again (required by OSMDashboard until v6.4.1).
Part of #2265.
2026-02-08 22:48:43 +01:00
Dennis Guse b65d83010d Bugfix: ShareContentProvider should report contentType for all Uris (not only TrackFileFormat).
Part of #2265.
2026-02-08 18:11:52 +01:00
Dennis Guse 1a5d06f9c6 Cleanup: renamed IntentDashboardUtils.java to DataProvider. 2026-02-07 19:20:48 +01:00
Dennis Guse 723c21d7ec Dashboard API: add tests for column names.
Part of #2265.
2026-02-07 09:39:23 +01:00
Dennis Guse 8fe0c3be7d Bugfix: update data for UI immediately after starting recording. 2026-02-06 18:54:32 +01:00
Dennis Guse e64792bf3f Resetting preferences requires clearing and naming each preference file explicitly. 2026-02-06 18:54:18 +01:00
Dennis Guse 21bafff761 TrackRecordingService should start UI timer at the end of startRecording. 2026-02-06 18:54:06 +01:00
Dennis Guse c33c2de51d Cleanup: EspressoUI should always lookup UI elements. 2026-02-06 18:53:55 +01:00
Dennis Guse a6b11c1c37 Release: v4.25.1 2026-02-01 22:05:05 +01:00
Dennis Guse 315788308b Bumping DashboardAPI to version 3.
Fixes #2255.
2026-02-01 20:15:24 +01:00
Dennis Guse 547c1c5c65 Rename MarkerColumns.
Fixes #2255.
2026-02-01 20:15:24 +01:00
tace16 69efd6fb82 Translated using Weblate (Tamil)
Currently translated at 100.0% (545 of 545 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/ta/
2026-01-31 18:08:56 +01:00
ghose 3a0524f96d Translated using Weblate (Galician)
Currently translated at 100.0% (545 of 545 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/gl/
2026-01-31 18:08:56 +01:00
Priit Jõerüüt 05456b44ef Translated using Weblate (Estonian)
Currently translated at 100.0% (545 of 545 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/et/
2026-01-31 18:08:56 +01:00
tace16 b449dad619 Translated using Weblate (Tamil)
Currently translated at 100.0% (179 of 179 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://translate.codeberg.org/projects/opentracks/app-store-metadata-files/ta/
2026-01-31 18:08:56 +01:00
SomeTr ccefe36489 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (545 of 545 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/uk/
2026-01-31 18:08:56 +01:00
ovl-005 90e9ab6d2a Translated using Weblate (Norwegian Bokmål)
Currently translated at 98.1% (535 of 545 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/nb_NO/
2026-01-31 18:08:56 +01:00
Outbreak2096 d1061fa6cd Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 88.8% (159 of 179 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://translate.codeberg.org/projects/opentracks/app-store-metadata-files/zh_Hans/
2026-01-31 18:08:56 +01:00
Outbreak2096 c0c813001f Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (545 of 545 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/zh_Hans/
2026-01-31 18:08:56 +01:00
Outbreak2096 51498bbcf9 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (542 of 542 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/zh_Hans/
2026-01-31 18:08:56 +01:00
CI/CD Bot by pstorch 4ef1fcefbc Update dependency gradle to v9.3.1 2026-01-29 15:35:43 +00:00
CI/CD Bot by pstorch 9a572130a7 Update dependency androidx.work:work-runtime to v2.11.1 2026-01-29 00:34:18 +00:00
Dennis Guse e931f5ab7c Reproducible build: for naming the APK use the actual versionCode.
Fixes #2252.
2026-01-23 15:11:37 +01:00
Dennis Guse 9affe28fad Release: v4.25.0 2026-01-21 18:33:40 +01:00
Dennis Guse 4905a37951 Build scripts: add some error handling. 2026-01-21 18:33:20 +01:00
Dennis Guse 439671d0a4 Build scripts: add some error handling. 2026-01-18 15:49:41 +01:00
Dennis Guse 825e8dda6e Revert "Translated using Weblate (English (en_001))"
This reverts commit 99a9248b27.
2026-01-18 10:13:00 +01:00
Dennis Guse 661c839c78 Update README_TESTED_SENSORS.md
Assioma Pro MX-2

Fixes #2246.
2026-01-18 10:08:42 +01:00
Dennis Guse 8eb42a7ecb Upgrade to Android Gradle Plugin 9.0: removing applicationVariants 2026-01-17 22:58:17 +01:00
CI/CD Bot by pstorch 660a55ff5a Update dependency gradle to v9.3.0 2026-01-16 12:45:31 +00:00
CI/CD Bot by pstorch 958697bcb3 Update dependency com.android.tools.build:gradle to v9 2026-01-15 17:42:07 +00:00
ntd 6029eb0317 Translated using Weblate (Italian)
Currently translated at 100.0% (542 of 542 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/it/
2026-01-10 21:48:12 +01:00
ntd 8b56d53a6a Translated using Weblate (Italian)
Currently translated at 3.9% (7 of 178 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://translate.codeberg.org/projects/opentracks/app-store-metadata-files/it/
2026-01-10 21:48:12 +01:00
Dennis Guse e9deebea6c Refactor: RecordingLayout. 2026-01-10 21:43:54 +01:00
Dennis Guse 54844b9a9a Sensor: add temperature.
Fixes #2241.
2026-01-10 21:43:54 +01:00
Dennis Guse 738201c1c2 Cleanup. 2026-01-04 09:57:05 +01:00
discollizard 48b6be0bd1 record button moved to the middle of the screen
Part of #2199
2026-01-03 18:40:17 -03:00
Priit Jõerüüt 6b924cd936 Translated using Weblate (Estonian)
Currently translated at 100.0% (542 of 542 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/et/
2026-01-02 19:56:27 +01:00
discollizard 5f083de530 adjusted distance time and markers to be horizontally consistent
formatter

initial date and time are always visible in the list while recording
2026-01-02 15:31:42 -03:00
Matt Votava 278f6323f1 track import: get extension from file name, not URI
When importing tracks from nextcloud, the URI for a file may be something like

content://org.nextcloud.documents/tree/7keh3ot7zmiaxecoktv6bfn6gdygt2lg%2F707/document/7keh3ot7zmiaxecoktv6bfn6gdygt2lg%2F745

The URI contains no ".", so getExtension() would return the whole URI as the extension, resulting in an "Unsupported file format" error.

To fix this, also pass DocumentFile.getName() to the ImportWorker.
2025-12-28 01:30:10 -08:00
Dennis Guse 5baa8fa21e Cleanup: KMLTrackImporter doesn't need Position at first. 2025-12-28 08:59:19 +01:00
Dennis Guse 707f4add71 Cleanup: GpsStatusManager only needs Position. 2025-12-28 08:59:19 +01:00
Dennis Guse b583f8f23d Sticker: added printable PDF 2025-12-27 08:38:01 +01:00
Priit Jõerüüt c847f622e4 Translated using Weblate (Estonian)
Currently translated at 91.3% (495 of 542 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/et/
2025-12-27 07:35:12 +01:00
Priit Jõerüüt 70dd58f484 Translated using Weblate (Estonian)
Currently translated at 78.9% (428 of 542 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/et/
2025-12-27 07:35:12 +01:00
Dennis Guse 015352c989 TrackPoint is now a record. 2025-12-24 17:15:19 +01:00
Dennis Guse 42ae582cd8 Release: v4.24.2 2025-12-19 22:14:30 +01:00
frazou 6e0ad50397 Translated using Weblate (French)
Currently translated at 100.0% (544 of 544 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/fr/
2025-12-13 12:35:09 +01:00
Dennis Guse d43efad52a Bugfix: crash on select all tracks or all markers.
Fixes #2231.
2025-12-12 21:59:45 +01:00
Dennis Guse 693904529d Release: v4.24.1 2025-12-11 22:46:23 +01:00
Dennis Guse 08143a546a Bugfix: marker.icon column is still used by OSMDashboard v6.4.1
Fixes https://codeberg.org/OpenTracksApp/OSMDashboard/issues/552
2025-12-11 22:44:44 +01:00
Dennis Guse eb49156ee7 Bugfix: crash when using number as default track name.
Fixes #2229.
2025-12-11 18:24:47 +01:00
CI/CD Bot by pstorch 51c03d8d75 Update dependency com.android.tools.build:gradle to v8.13.2 2025-12-11 05:33:57 +00:00
CI/CD Bot by pstorch 666cbbad68 Update dependency org.mockito:mockito-android to v5.21.0 2025-12-09 19:34:11 +00:00
Dennis Guse 1cbc93c303 Release: v4.24.0 2025-12-09 07:41:29 +01:00
frazou afd1232940 Fix null race-condition for heart rate sensor data
The previous implementation was technically safe because the sensor
never had null values. Now that it can, the calls to getHeartRate
must be stored instead of repeated to avoid a race condition where the
sensor data become null between the null-check and the second call
2025-12-07 19:16:42 +01:00
frazou a3407ccbea Add an optional filter for invalid (0 bpm) heart rate readings 2025-12-07 19:16:42 +01:00
yosoyalguien c259f9b8bd Translated using Weblate (Spanish (Latin America))
Currently translated at 84.0% (457 of 544 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/es_419/
2025-12-07 12:15:12 +01:00
Stzyxh b1a556bf91 Translated using Weblate (German)
Currently translated at 6.2% (11 of 175 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://translate.codeberg.org/projects/opentracks/app-store-metadata-files/de/
2025-12-07 12:15:12 +01:00
tace16 ab70874d59 Translated using Weblate (Tamil)
Currently translated at 100.0% (175 of 175 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://translate.codeberg.org/projects/opentracks/app-store-metadata-files/ta/
2025-12-07 12:15:12 +01:00
Dennis Guse e6367b1dfa Cleanup: inline SelectionData. 2025-12-06 11:46:44 +01:00
Dennis Guse 8db381c448 Cleanup. 2025-12-06 10:25:11 +01:00
Dennis Guse ef111b7959 Cleanup: removed import functionality for legacy format (coordinates 100 and 200 where special).
Deadline was EOF 2021.
2025-12-06 10:23:21 +01:00
Dennis Guse 56c6cb1f19 Cleanup: removed CachedTrackPointsIndexes.
Caching already happens in SQLite.getColumnIndexOrThrow().
2025-12-06 10:05:24 +01:00
Dennis Guse 874c5eb8ae Cleanup. 2025-12-06 09:36:04 +01:00
Dennis Guse 1a087b1117 Bugfix: icon for activity motor bike was wrong. 2025-12-06 09:34:17 +01:00
Dennis Guse 3120cd8da0 README: description for KML/KMZ vs. GPX. 2025-12-06 09:31:36 +01:00
Dennis Guse 4263c000c0 Show coordinates with always with 5 decimals. 2025-12-06 09:18:06 +01:00
Dennis Guse f4073ead63 Removed KMZ export without photos. 2025-12-06 09:03:03 +01:00
Dennis Guse 2f90784442 Cleanup. 2025-12-06 08:47:06 +01:00
Dennis Guse 2cab394d03 Cleanup. 2025-12-06 08:41:57 +01:00
Dennis Guse 55f1bcb30b PublicAPI: StartRecording does not overwrite Track defaults if no data was provided.
Fixes #2214.
2025-12-06 08:34:17 +01:00
Dennis Guse 33a17784e6 Re-enable marker photo sharing.
Don't recall why it was disabled.
2025-12-06 08:27:48 +01:00
Dennis Guse 7cb97b0e6b Cleanup: use try-with-resources. 2025-12-06 08:27:01 +01:00
Dennis Guse 1cb3db496a Cleanup. 2025-12-05 08:05:25 +01:00
Dennis Guse d49d6c9857 Cleanup: AutoCloseableIterator now handles the cursor. 2025-12-05 08:05:25 +01:00
Dennis Guse 12f0c3313f ContentProviderUtils.searchMarkers uses MarkerIterator. 2025-12-05 08:05:25 +01:00
Dennis Guse a78e059612 Cleanup: ChartView doesn't show any Markers since a while.
Drawing Markers was removed in d8eed2ccc9
2025-12-05 08:05:25 +01:00
Dennis Guse 0774ee512f Cleanup: clarify options for Marker.TypeLocalized. 2025-12-05 08:05:25 +01:00
Dennis Guse 4c55468571 TrackList now has an AutoClosableIterator. 2025-12-05 08:05:25 +01:00
Dennis Guse 201f2d1b27 Cleanup: renaming Marker.category to typeLocalized. 2025-12-05 08:05:25 +01:00
Dennis Guse 595ad7f7b5 Marker list: split category and description into two fields. 2025-12-05 08:05:25 +01:00
Dennis Guse d518507193 Cleanup: use TextUtils.isEmpty(). 2025-12-05 08:05:25 +01:00
Dennis Guse 888f59133d Track list: split activity type localized and description into two fields. 2025-12-05 08:05:25 +01:00
Dennis Guse 5bb5b3b344 Track.ActivityType and Marker.Category are shown not wrapped in [] anymore. 2025-12-05 08:05:25 +01:00
Dennis Guse b584a95c9c Bugfix: crash when opening Marker Detail.
Introduced in 5b4b54694c
2025-12-05 08:05:25 +01:00
Dennis Guse 9ba152c469 Cleanup: use Position instead of Android's location. 2025-12-05 08:05:25 +01:00
Dennis Guse 1191f2e1e2 Cleanup: remove unused imports. 2025-12-05 08:05:25 +01:00
frazou e5aed794ba Fix idle discrepancy in UI 2025-11-30 22:49:50 +01:00
Dennis Guse 7956063598 Cleanup: Marker.icon was not used anymore. 2025-11-30 16:51:02 +01:00
Dennis Guse 0f356de2ca Gradle: add managed device. 2025-11-30 16:51:02 +01:00
Dennis Guse 5b4b54694c Marker is now a record. 2025-11-30 16:51:02 +01:00
Dennis Guse 9c271972d2 Cleanup. 2025-11-30 16:51:02 +01:00
CI/CD Bot by pstorch 744f11eaa0 Update actions/checkout action to v6 2025-11-30 14:34:10 +00:00
tace16 3c16f67882 Translated using Weblate (Tamil)
Currently translated at 100.0% (544 of 544 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/ta/
2025-11-27 20:48:11 +01:00
Dennis Guse 12d1a50995 Track is now a record.
Create Track directly.
2025-11-27 20:34:51 +01:00
Dennis Guse 21404fed50 Track is now a record.
Create Track directly.
2025-11-27 20:11:53 +01:00
Dennis Guse 990c91f978 Track is now a record. 2025-11-27 19:35:05 +01:00
Dennis Guse 6a262426a5 Delete all tracks (trackpoints and markers) works via cascade. 2025-11-27 17:14:11 +01:00
Dennis Guse 1b2998f423 CustomContentProvider: limit URIs for updates. 2025-11-27 17:14:11 +01:00
Dennis Guse d24dd2b005 Cleanup. 2025-11-27 17:14:11 +01:00
Dennis Guse 341fd02f9a Cleanup. 2025-11-26 22:48:00 +01:00
Dennis Guse 6833ea7282 Use MarkerIterator for TrackDataHub. 2025-11-26 22:46:39 +01:00
Dennis Guse a1da53864e Use MarkerIterator for MarkerDetailActivity. 2025-11-26 22:10:53 +01:00
Dennis Guse 28d49de047 Cleanup: removed ContentProviderUtils.getMarkers(). 2025-11-26 21:51:07 +01:00
Dennis Guse 4ae9d24960 Implemented MarkerIterator. 2025-11-26 21:42:11 +01:00
Dennis Guse 0b0b1a0d8d Cleanup. 2025-11-26 20:07:39 +01:00
Dennis Guse 090cdbf712 Cleanup: ContentProviderUtils.getMarkerCursor() has an optional parameter. 2025-11-26 20:05:27 +01:00
Dennis Guse 8258d607c3 Cleanup: ContentProviderUtils.getMarkerCursor() had an unused parameter. 2025-11-26 19:56:32 +01:00
Dennis Guse 686a2a9b8f Cleanup: removed method that was only used for testing in ContentProviderUtils. 2025-11-25 19:34:41 +01:00
Dennis Guse 7ddb6b32bb Cleanup: TrackDataHub doesn't need to query the id of the last TrackPoint. 2025-11-25 18:46:20 +01:00
Dennis Guse 5cd639c852 Cleanup: TrackDataHub only needs in-sample. 2025-11-25 18:42:02 +01:00
Dennis Guse ae9906fb18 Cleanup. 2025-11-25 07:40:01 +01:00
Dennis Guse 96bcac397b Cleanup. 2025-11-25 07:31:49 +01:00
Dennis Guse d7b207afac Cleanup: trackStatistics were already updated. 2025-11-25 07:31:49 +01:00
Dennis Guse 1672ba99ad Cleanup: in TrackImporter create Track object when all data got loaded. 2025-11-25 07:31:49 +01:00
Dennis Guse a6d4a05839 Statistics requires startTime and stopTime.
This is now enforced for import as wells.
2025-11-25 07:31:48 +01:00
Dennis Guse 860b90da37 Cleanup ExportImportTest. 2025-11-24 19:38:33 +01:00
Dennis Guse 46d7385d25 Cleanup RecordingData. 2025-11-22 18:22:00 +01:00
Dennis Guse 25fbd9490c Cleanup RecordingData. 2025-11-22 18:06:48 +01:00
Dennis Guse 6d33adffce Move temp functionality into TrackStatisticsUpdater. 2025-11-22 17:49:35 +01:00
Dennis Guse 92cecb6f3a For TrackStatisticsUpdater there may no pre-existing Statistics. 2025-11-22 13:14:50 +01:00
Dennis Guse e6bdc9a41d Altitude extremities is now null instead of (0,0) if no altitude was received (yet). 2025-11-22 13:03:52 +01:00
Dennis Guse e234e9c493 Only execute VoiceAnnouncementUtilsTest for SDK34+. 2025-11-22 12:56:17 +01:00
Dennis Guse 73d6ca6ca6 Cleanup: SegmentStatisticUpdater is always initialized. 2025-11-21 16:30:29 +01:00
Dennis Guse 1ef7a6ecc4 Move .merge() from SegmentStatisticUpdater to Statistics.
This is a functional change as maxSpeed is now determined by Segment (rather than the joined Segment).
2025-11-21 16:15:18 +01:00
Dennis Guse 94a0b57362 Simplify construction of TrackStatisticsUpdater. 2025-11-21 13:37:14 +01:00
Dennis Guse 4ebaf366d6 TrackStatisticsUpdater only needs one SegmentStatisticUpdater. 2025-11-21 13:37:14 +01:00
Dennis Guse 911f2c525f Move isIdle to TrackStatisticsUpdater. 2025-11-21 13:37:14 +01:00
CI/CD Bot by pstorch a310f773a1 Update dependency com.android.tools.build:gradle to v8.13.1 2025-11-20 05:33:43 +01:00
Dennis Guse 8a247e5b38 Cleanup: TrackStatisticsUpdater. 2025-11-17 22:22:44 +01:00
Dennis Guse 1045ade71a Cleanup: SegmentStatisticUpdater. 2025-11-17 22:22:44 +01:00
Dennis Guse 4307dceb70 Cleanup: rename to totalDuration and movingDuration. 2025-11-17 22:22:44 +01:00
Dennis Guse 59ea50b96f Cleanup: SegmentStatisticUpdater. 2025-11-17 22:22:44 +01:00
Dennis Guse 8d015fee6c Cleanup: SegmentStatisticUpdater. 2025-11-17 22:22:44 +01:00
Dennis Guse 653352255e Cleanup: SegmentStatisticUpdater. 2025-11-17 22:22:44 +01:00
Dennis Guse ebcdd87481 Cleanup: use Duration.ZERO. 2025-11-17 22:22:44 +01:00
Stzyxh 8d1af8f2cf Translated using Weblate (German)
Currently translated at 5.1% (9 of 175 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://translate.codeberg.org/projects/opentracks/app-store-metadata-files/de/
2025-11-17 22:22:12 +01:00
CI/CD Bot by pstorch 766739c222 Update dependency gradle to v9.2.1 2025-11-17 14:35:53 +00:00
Dennis Guse 54fa6aa3e2 Add GPX.Studio to README. 2025-11-16 15:17:15 +01:00
SomeTr 1dc737410b Translated using Weblate (Ukrainian)
Currently translated at 99.8% (543 of 544 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/uk/
2025-11-10 00:09:55 +00:00
ghose 4093899a82 Translated using Weblate (Galician)
Currently translated at 17.7% (31 of 175 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://translate.codeberg.org/projects/opentracks/app-store-metadata-files/gl/
2025-11-10 00:09:55 +00:00
Dennis Guse 9236eb6944 AndroidStudio: set line width (for autoformatting) to 200. 2025-11-09 22:45:50 +01:00
Dennis Guse 9f00b0606b Revert "AndroidStudio: set line width (for autoformatting) to 200."
This reverts commit 95b27f66fb.
2025-11-09 22:45:41 +01:00
SomeTr 20a18b86dc Translated using Weblate (Ukrainian)
Currently translated at 99.4% (541 of 544 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/uk/
2025-11-08 23:09:40 +00:00
alecsargent 34cfc235c3 Translated using Weblate (Spanish (Latin America))
Currently translated at 83.6% (455 of 544 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/es_419/
2025-11-08 23:09:40 +00:00
CI/CD Bot by pstorch f733d18a60 Add renovate.json 2025-11-08 20:33:45 +00:00
Dennis Guse 221b61c7f1 Aggregate statistics are now computed in the database. 2025-11-06 21:54:30 +01:00
Dennis Guse ca64f2c749 Tracks table: remove unused columns and rename existing columns. 2025-11-06 21:54:30 +01:00
Dennis Guse 3d083928eb Cleanup. 2025-11-06 21:54:30 +01:00
Dennis Guse f47ab142f2 Upgrade to Gradlew 9.2.0 2025-11-06 21:54:30 +01:00
Dennis Guse 6e4d46a14d Distance.ZERO as constant. 2025-11-06 21:54:30 +01:00
Dennis Guse fffeb40200 Speed.ZERO as constant. 2025-11-06 21:54:30 +01:00
Dennis Guse a5e5d0ca51 AggregatedStatistics: compute without SegmentStatisticUpdater. 2025-11-06 21:54:30 +01:00
Dennis Guse 7791ff8659 Cleanup: Track simplify constructors. 2025-11-06 21:54:30 +01:00
Dennis Guse 3db0a53377 Release: v4.23.2 2025-11-06 21:41:13 +01:00
Dennis Guse 58dac8b706 Translations 2025-11-06 18:12:42 +01:00
Codeberg Translate f29b6d4088 Merge remote-tracking branch 'origin/main' 2025-11-05 21:38:42 +00:00
ghose 80fb4f9998 Translated using Weblate (Galician)
Currently translated at 100.0% (544 of 544 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/gl/
2025-11-05 21:38:39 +00:00
Dennis Guse 6340e55fff Bump org.mockito:mockito-android from 5.19.0 to 5.20.0 2025-11-05 21:34:54 +01:00
Dennis Guse 625549d381 Bump androidx.work:work-runtime from 2.10.5 to 2.11.0 2025-11-05 21:34:23 +01:00
Dennis Guse 7d9878b32e Bump androidx.core:core-location-altitude from 1.0.0-alpha03 to 1.0.0-beta01 2025-11-05 21:33:47 +01:00
Dennis Guse a5553211af Bump androidx.core:core-splashscreen from 1.0.1 to 1.2.0 2025-11-05 21:33:15 +01:00
Dennis Guse 044e84154c Bugfix: crash in settings.
Introduced in 696939112a
2025-11-05 21:31:40 +01:00
Outbreak2096 f61d718b2d Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (544 of 544 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/zh_Hans/
2025-11-04 06:06:45 +00:00
Codeberg Translate c7a54dcee8 Merge remote-tracking branch 'origin/main' 2025-11-03 00:23:28 +00:00
Eryk Michalak 3dc2ee3462 Translated using Weblate (Polish)
Currently translated at 93.9% (511 of 544 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/pl/
2025-11-03 00:23:26 +00:00
Stzyxh 6817ff3234 Translated using Weblate (German)
Currently translated at 4.5% (8 of 174 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://translate.codeberg.org/projects/opentracks/app-store-metadata-files/de/
2025-11-03 00:23:22 +00:00
Codeberg Translate fb0400001a Merge remote-tracking branch 'origin/main' 2025-11-01 10:10:24 +00:00
ovl-005 16591b3e80 Translated using Weblate (Norwegian Bokmål)
Currently translated at 98.3% (534 of 543 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/nb_NO/
2025-11-01 10:09:54 +00:00
Dennis Guse 28da32f573 Fixed TrackRecordingServiceRecordingTest: disable the real GPS (finally). 2025-11-01 10:13:52 +01:00
Dennis Guse c687308d97 Move WakeLock to SensorManager (was TrackRecordingService). 2025-11-01 09:56:31 +01:00
Dennis Guse 65c8e8542e Cleanup: pass RecordingData to VoiceAnnouncementManager. 2025-11-01 09:30:47 +01:00
Dennis Guse ca4cb648e7 Predictive back: shown also for TrackRecordedActivity.
However: the shared element animation needed to be removed. ActivityOptions.makeSceneTransitionAnimation() seems to overwrite the predictive back animation.
2025-11-01 08:49:08 +01:00
Dennis Guse 53240c3d1a IntervalStatisticsUpdater exposes Statistics (instead of Interval). 2025-10-31 19:00:32 +01:00
Dennis Guse 95b27f66fb AndroidStudio: set line width (for autoformatting) to 200. 2025-10-31 18:34:07 +01:00
Dennis Guse f31e146cda Cleanup: TrackPoint uses AltitudeGainLoss. 2025-10-30 22:33:00 +01:00
Dennis Guse 696939112a Cleanup: Statistics doesn't need isIdle.
isIdle is only used by SegmentStatisticsUpdater.
2025-10-30 21:52:00 +01:00
Dennis Guse 73ce97a63e VoiceAnnouncementUtilsTest do not store data. 2025-10-30 21:45:03 +01:00
Dennis Guse 5988ae3d3f VoiceAnnouncementUtilsTest do not use SegmentStatisticsUpdater. 2025-10-30 21:30:01 +01:00
Dennis Guse 58655814b2 VoiceAnnouncementManager should close TrackPointIterator to avoid memory leaks. 2025-10-30 07:54:05 +01:00
Dennis Guse 11f1b6e72d Cleanup: TrackPointIterator is now package private. 2025-10-30 07:54:05 +01:00
Dennis Guse 8f537f33a7 Cleanup: removed Track.setTrackStatistics(SegmentStatisticsUpdater). 2025-10-30 07:54:05 +01:00
Dennis Guse 4db72230ff Refactored VoiceAnnouncementUtilsTest. 2025-10-30 07:54:05 +01:00
Dennis Guse fd92793c2a Test: IntervalStatisticsUpdaterTest doesn't store data anymore. 2025-10-30 07:54:05 +01:00
Dennis Guse 05e45b6870 TrackStatisticsUpdater now provides Statistics.
Used for non-testing code.
2025-10-30 07:54:04 +01:00
Dennis Guse 7521f906ed IntervalStatisticsUpdater now uses Statistics. 2025-10-30 07:54:04 +01:00
Dennis Guse 3222a0c497 Rename IntervalStatistics to IntervalStatisticsUpdater. 2025-10-30 07:54:04 +01:00
Dennis Guse caf3d957cd Cleanup: SegmentStatisticUpdaterTest uses Statistic. 2025-10-30 07:54:04 +01:00
Dennis Guse 005d00cfaa Cleanup: SegmentStatisticUpdaterTest. 2025-10-30 07:54:04 +01:00
Dennis Guse c227d54f26 Cleanup: SegmentStatisticUpdater and Track do not need equal(). 2025-10-30 07:54:04 +01:00
Dennis Guse bc62320b9f Track only has Statistics (instead of SegmentStatisticsUpdater). 2025-10-30 07:54:04 +01:00
Dennis Guse 0ad32f676c Create SegmentStatisticUpdater by passing Statistics (2/2). 2025-10-30 07:54:04 +01:00
Dennis Guse fff5f2d406 Create SegmentStatisticUpdater by passing Statistics (1/2). 2025-10-30 07:54:04 +01:00
Dennis Guse be653b73ee Added value object: AltitudeExtremities. 2025-10-30 07:54:04 +01:00
Dennis Guse 4580f2a7bb ContentProviderUtils use Statistics for saving. 2025-10-30 07:54:04 +01:00
Dennis Guse 5dd56892fb Cleanup: move Statistics to package models. 2025-10-30 07:54:04 +01:00
Dennis Guse e3460d94ed Statistics: used when only the data is needed (instead of passing SegmentStatisticUpdater). 2025-10-30 07:54:04 +01:00
Dennis Guse 920ad5129a Introduced Statistics as a value object. 2025-10-30 07:54:04 +01:00
Dennis Guse fba08fe8dd Cleanup: rename TrackStatistics to SegmentStatisticsUpdater. 2025-10-30 07:54:04 +01:00
Dennis Guse 5746a41905 Release: v4.23.1 2025-10-30 07:48:12 +01:00
Dennis Guse 361ae909e7 Bugfix: post-recording workout crashes.
Fixes #2180.

Introduced in 02a77e5ce7
2025-10-29 22:14:46 +01:00
Dennis Guse 4b59660ea1 Revert "Allow adding newly developed UI Components to existing custom layouts."
This reverts commit daa4689f44.
2025-10-29 22:14:12 +01:00
SomeTr cabf5d238e Translated using Weblate (Ukrainian)
Currently translated at 94.6% (514 of 543 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/uk/
2025-10-28 17:35:25 +01:00
Outbreak2096 9c902b89ed Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (543 of 543 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/zh_Hans/
2025-10-28 17:35:25 +01:00
Claudinux c31b8a5802 Translated using Weblate (Italian)
Currently translated at 100.0% (543 of 543 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/it/
2025-10-28 17:35:25 +01:00
SomeTr 901c548d13 Translated using Weblate (Ukrainian)
Currently translated at 94.4% (513 of 543 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/uk/
2025-10-28 17:35:25 +01:00
SomeTr b77de1e10d Translated using Weblate (Ukrainian)
Currently translated at 94.2% (512 of 543 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/uk/
2025-10-28 17:35:25 +01:00
SomeTr b8c7fba7d8 Translated using Weblate (Ukrainian)
Currently translated at 94.2% (512 of 543 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/uk/
2025-10-28 17:35:25 +01:00
ghose 466719fbc8 Translated using Weblate (Galician)
Currently translated at 100.0% (543 of 543 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/gl/
2025-10-28 17:35:25 +01:00
ghose 4f0ab71503 Translated using Weblate (Galician)
Currently translated at 100.0% (541 of 541 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/gl/
2025-10-28 17:35:25 +01:00
Jeff Shelley daa4689f44 Allow adding newly developed UI Components to existing custom layouts.
Fixes #2176.
2025-10-28 09:51:51 -05:00
Jeff Shelley 023a5aa221 added new UI component which shows elapsed segment time 2025-10-28 09:51:29 -05:00
SomeTr 291633c1d5 Translated using Weblate (Ukrainian)
Currently translated at 94.6% (514 of 543 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/uk/
2025-10-28 07:53:05 +00:00
Codeberg Translate 00990076fb Merge remote-tracking branch 'origin/main' 2025-10-28 05:27:33 +00:00
Outbreak2096 e672df7a60 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (543 of 543 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/zh_Hans/
2025-10-28 05:27:31 +00:00
Claudinux 82eb237117 Translated using Weblate (Italian)
Currently translated at 100.0% (543 of 543 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/it/
2025-10-28 05:27:31 +00:00
Peter Storch 832005565a run forgejo actions on pull requests from forks (#2183)
Reviewed-on: https://codeberg.org/OpenTracksApp/OpenTracks/pulls/2183
Co-authored-by: Peter Storch <peter.storch@tuta.io>
Co-committed-by: Peter Storch <peter.storch@tuta.io>
2025-10-27 20:58:18 +01:00
SomeTr 894699f009 Translated using Weblate (Ukrainian)
Currently translated at 94.4% (513 of 543 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/uk/
2025-10-26 21:02:55 +00:00
SomeTr f45dba258b Translated using Weblate (Ukrainian)
Currently translated at 94.2% (512 of 543 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/uk/
2025-10-26 20:23:21 +00:00
SomeTr 17be49af70 Translated using Weblate (Ukrainian)
Currently translated at 94.2% (512 of 543 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/uk/
2025-10-26 13:17:55 +00:00
ghose e91d36fc16 Translated using Weblate (Galician)
Currently translated at 100.0% (543 of 543 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/gl/
2025-10-26 13:17:55 +00:00
ghose 56f333180b Translated using Weblate (Galician)
Currently translated at 100.0% (541 of 541 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/gl/
2025-10-25 17:24:16 +00:00
Dennis Guse 9af8b0d353 Cleanup. 2025-10-24 22:26:08 +02:00
Dennis Guse 57c0750980 Release: v4.23.0 2025-10-24 10:23:14 +02:00
Dennis Guse 8e36ee2e7d Cleanup: TrackRecordingServiceNotificationManager uses RecordingData. 2025-10-24 10:19:43 +02:00
Dennis Guse ec2e527fd1 Cleanup. 2025-10-24 10:04:04 +02:00
Dennis Guse d968e5d0c1 Notification opens recording UI.
Features was removed as it broke occasionally and re-implemented after refactoring.

Fixes #1156.
2025-10-22 23:25:55 +02:00
Dennis Guse 4257f21cae Fixed tests.
Missed in1fa76301e23bf709402c19838efea597e7e38ca7
2025-10-22 23:24:13 +02:00
Dennis Guse 26e7059af2 Cleanup. 2025-10-22 22:49:20 +02:00
Jeff Shelley 94595f2a1f Added support for average pace of the current segment 2025-10-21 19:34:19 -05:00
Dennis Guse 1fa76301e2 Cleanup: TrackShareSummaryGenerator (was DescriptionGenerator) now only produces plaintext (HTML was not used anymore). 2025-10-20 09:15:39 +02:00
Dennis Guse ce2c608cc9 Cleanup: TrackStatistics has altitudeExtremities or not. 2025-10-19 22:27:11 +02:00
Dennis Guse 916f26dfda Merge pull request 'Cleanup' (#2173) from cleanup into main
Reviewed-on: https://codeberg.org/OpenTracksApp/OpenTracks/pulls/2173
2025-10-19 18:43:38 +02:00
Dennis Guse a108edc125 Bump androidx.work:work-runtime from 2.10.2 to 2.10.5 2025-10-19 18:28:13 +02:00
Dennis Guse 1a1a8a851b Cleanup: use Stream.toList()
Replaces Stream.collect(Collection.toList() | Collectors.toCollection()).
2025-10-19 18:26:49 +02:00
Dennis Guse ddd1efeb58 Cleanup: use .isEmpty() (except for CharSequence and StringBuilder as it requires SDK33). 2025-10-19 18:26:48 +02:00
Dennis Guse 6c30dd8509 Lists should show vertical scrolling indicator (was lost by switching to RecyclerView). 2025-10-19 17:59:47 +02:00
Dennis Guse 21fe59f32c Cleanup: SDK_INT is always >= Android O. 2025-10-19 17:48:23 +02:00
Dennis Guse f79b2b51c8 Cleanup: use correct order for assert in tests. 2025-10-18 21:48:45 +02:00
Dennis Guse b16e9ad964 Cleanup: UintUtilsTest was only partly executed. 2025-10-18 21:41:46 +02:00
Dennis Guse 65f8ba16b3 Cleanup: RecyclerViewSwipeDeleteCallback.onChildDraw() should only call super once. 2025-10-18 21:28:49 +02:00
Dennis Guse 9b48993cd0 Cleanup. 2025-10-18 21:27:31 +02:00
Dennis Guse 54cec3e821 Cleanup: limit method visibility. 2025-10-18 21:10:26 +02:00
Dennis Guse daeb972c8a Cleanup: simplify stream by using List operations (desugar). 2025-10-18 21:10:26 +02:00
Dennis Guse 73872e8926 Cleanup. 2025-10-18 21:09:21 +02:00
Dennis Guse c15fa21a56 Cleanup: use viewHolder.getAbsoluteAdapterPosition(). 2025-10-18 09:40:56 +02:00
Dennis Guse e9d1080e5d Cleanup: SpeedFormatter doesn't support decimal places. 2025-10-18 09:40:54 +02:00
Claudinux d0b3f45151 Translated using Weblate (Italian)
Currently translated at 4.0% (7 of 172 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://translate.codeberg.org/projects/opentracks/app-store-metadata-files/it/
2025-10-16 21:50:25 +02:00
happylocalisation 32b0b02d28 Added translation using Weblate (English (Middle)) 2025-10-16 21:50:25 +02:00
Dennis Guse d6617f61b7 Cleanup: use PreferenceUtils.isKey(List). 2025-10-15 18:10:35 +02:00
Dennis Guse b5180f235f Cleanup: StatisticsUtils were unused. 2025-10-15 18:10:15 +02:00
Dennis Guse e879eedbd6 Migrate ImportService to WorkManager.
Part of #1315.
2025-10-15 17:25:48 +02:00
Dennis Guse c7ca6800f7 Cleanup: ImportActivity. 2025-10-15 09:02:49 +02:00
Dennis Guse fed884a6ce ExportActivity inherits AbstractActivity. 2025-10-15 08:40:19 +02:00
Dennis Guse 02a77e5ce7 Migrate ExportService to WorkManager.
Part of #1315.
2025-10-14 22:45:00 +02:00
Dennis Guse 6f97399aeb Migrate TrackDeletionService to WorkManager.
Part of #1315.
2025-10-14 19:13:10 +02:00
pstorch 1c4f0e67ab Merge pull request 'use forgejo runner with simple unit tests' (#2159) from connected_check into main
Reviewed-on: https://codeberg.org/OpenTracksApp/OpenTracks/pulls/2159
2025-10-13 11:27:17 +02:00
Peter Storch ebed386d96 use forgejo runners with unit tests 2025-10-12 17:39:35 +02:00
Dennis Guse e44f63feb7 Merge remote-tracking branch 'weblate/main' 2025-09-25 19:08:49 +02:00
Dennis Guse 34c5a3ab97 Release: v4.22.2 2025-09-25 18:59:19 +02:00
Dennis Guse eac47db058 Bump material from 1.12.0 to 1.13.0 2025-09-25 18:58:13 +02:00
SomeTr e72d2a750c Translated using Weblate (Ukrainian)
Currently translated at 93.5% (507 of 542 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/uk/
2025-09-22 10:09:12 +00:00
Rob Herremans f54856c3f1 Fix for time and unit announcements (#2166)
Bugfix: time and unit announcement where not configurable correctly.

Fixes #2165.

Reviewed-on: https://codeberg.org/OpenTracksApp/OpenTracks/pulls/2166
Co-authored-by: Rob Herremans <dev@robherremans.nl>
Co-committed-by: Rob Herremans <dev@robherremans.nl>
2025-09-16 20:13:15 +02:00
Claudinux 81f6bbe955 Translated using Weblate (Italian)
Currently translated at 3.5% (6 of 171 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://translate.codeberg.org/projects/opentracks/app-store-metadata-files/it/
2025-09-09 15:09:10 +00:00
mikini c162c0a0ff Translated using Weblate (Danish)
Currently translated at 97.4% (528 of 542 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/da/
2025-09-07 08:51:08 +00:00
Outbreak2096 385d05dcdb Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (542 of 542 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/zh_Hans/
2025-09-07 08:51:08 +00:00
Claudinux 308ce4ac07 Translated using Weblate (Italian)
Currently translated at 100.0% (542 of 542 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/it/
2025-09-07 08:51:08 +00:00
Outbreak2096 f8a0241e1f Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 93.5% (159 of 170 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://translate.codeberg.org/projects/opentracks/app-store-metadata-files/zh_Hans/
2025-09-07 08:51:08 +00:00
Claudinux c74a5c8749 Translated using Weblate (Italian)
Currently translated at 2.9% (5 of 170 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://translate.codeberg.org/projects/opentracks/app-store-metadata-files/it/
2025-09-07 08:51:08 +00:00
Claudinux defdd49a65 Translated using Weblate (Italian)
Currently translated at 97.4% (528 of 542 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/it/
2025-09-07 08:51:08 +00:00
Dennis Guse 46cdae399d Release: v4.22.1 2025-08-31 09:31:26 +02:00
Dennis Guse a19f363b4a Translations are now on translate.codeberg.org
Part of #2154.
2025-08-28 22:14:45 +02:00
Aindriú Mac Giolla Eoin b55b25e00d Translated using Weblate (Irish)
Currently translated at 100.0% (170 of 170 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://hosted.weblate.org/projects/opentracks/app-store-metadata-files/ga/
2025-08-28 21:55:10 +02:00
Ghost of Sparta d527770a0a Translated using Weblate (Hungarian)
Currently translated at 100.0% (542 of 542 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://hosted.weblate.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/hu/
2025-08-28 21:55:10 +02:00
Fjuro fb75b0fa58 Translated using Weblate (Czech)
Currently translated at 100.0% (170 of 170 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://hosted.weblate.org/projects/opentracks/app-store-metadata-files/cs/
2025-08-28 21:55:10 +02:00
Dennis Guse 1c986e48f2 Reproducible builds: use OpenJDK-21 (build script only). 2025-08-28 21:55:10 +02:00
Peter Storch 7f666c571e codeberg migration 2025-08-24 20:48:53 +02:00
368 changed files with 10230 additions and 9434 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = false
max_line_length = 100
max_line_length = 200
tab_width = 4
ij_continuation_indent_size = 8
ij_formatter_off_tag = @formatter:off
+36
View File
@@ -0,0 +1,36 @@
name: Android Test
on:
push:
branches:
- main
pull_request:
jobs:
lint:
name: Android Lint
runs-on: docker
container:
image: 'ubuntu:android-24.04'
steps:
- name: Checkout sources
uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Lint
shell: bash
run: ./gradlew lint
android-check:
runs-on: docker
container:
image: 'ubuntu:android-24.04'
steps:
- name: Checkout sources
uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Test
shell: bash
run: ./gradlew testNightlyDebugUnitTest --stacktrace
-5
View File
@@ -1,5 +0,0 @@
# These are supported funding model platforms
liberapay: OpenTracks
github: [rgmf, pstorch, dennisguse]
custom: https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore
+10 -8
View File
@@ -40,10 +40,9 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
</tr>
</table>
Translations are hosted on <a href="https://hosted.weblate.org/engage/opentracks/">
hosted.weblate.org</a>.
<a href="https://hosted.weblate.org/engage/opentracks/">
<img src="https://hosted.weblate.org/widgets/opentracks/-/horizontal-auto.svg" alt="Translation status" />
Translations are hosted on <a href="https://translate.codeberg.org/engage/opentracks/">translate.codeberg.org/projects/opentracks/</a>.
<a href="https://translate.codeberg.org/engage/opentracks/">
<img src="https://translate.codeberg.org/widget/opentracks/opentracks-src-main-res-values-strings-xml-main/horizontal-auto.svg" alt="Translation status" />
</a>
## Screenshots
@@ -112,8 +111,7 @@ The API is disabled by default to protect the user's privacy, but it can easily
`Classes`:
* **Start a recording:** `de.dennisguse.opentracks.publicapi.StartRecording`
* 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)).
see `ActivityType.id` defined in [/src/main/java/de/dennisguse/opentracks/util/TrackIconUtils.java#L38](/src/main/java/de/dennisguse/opentracks/data/model/ActivityType.java)).
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
@@ -134,6 +132,10 @@ adb shell am start -n "de.dennisguse.opentracks.playstore/de.dennisguse.opentrac
```
## File formats compatibility with open-source software
KML/KMZ can be used to export indoor activities (no GPS present).
GPX exports only those points that have a GPS coordinate present (sensor data is aggregated though).
| | [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) |
@@ -143,8 +145,8 @@ adb shell am start -n "de.dennisguse.opentracks.playstore/de.dennisguse.opentrac
| [FitTrackee](https://github.com/SamR1/FitTrackee) | yes | n/a | n/a |
| [SportsTracker](https://github.com/ssaring/sportstracker) | yes, [single tracks only](https://github.com/ssaring/sportstracker/issues/260) | no | no |
| [ExifTool](https://exiftool.org) | [yes](https://exiftool.org/forum/index.php?topic=15972.0) | no | no |
| [Wanderer](https://wanderer.to/) | yes | yes | yes |
| [Wanderer](https://wanderer.to/) | yes | yes | yes |
| [GPX.Studio]https://gpx.studio/) | yes | no | no |
## Dashboard API (incl. map)
+3 -3
View File
@@ -3,10 +3,10 @@
## Craft new releases
1. Execute use `RELEASE.sh`.
2. Push to Github
3. Create release on Github (incl. changelog)
2. Push to Codeberg
3. Create release on Codeberg (incl. changelog)
This actually creates a tag that is later used by F-Droid
4. Pull from Github
4. Pull from Codeberg
5. Build reproducible release and attach to release
6. ... wait until reproducibility is verified by F-Droid
+4 -1
View File
@@ -44,7 +44,10 @@ However, often only one value is provided.
We only support _Instantaneous Power_.
For cycling (incl. cadence):
* Assioma Duo Powermeter
* Assioma
* Duo Powermeter
* Pro MX-2
* Elite drivo II
* QUARQ Red DZero Powermeter
* Tacx Satori Smart
+3 -6
View File
@@ -11,9 +11,6 @@ CHANGELOG_FILE="$CHANGELOG_DIR/$VERSIONID.txt"
git diff --exit-code || echo -e "\nWARNING WARNING WARNING WARNING WARNING: this branch contains uncommit changes."
#TODO Execute all tests: we are destroying data, running on a real device.
# ./gradlew connectedCheck
# Changelog
echo -e "\nPrevious changelog\n------------"
PREVIOUS_CHANGELOG=$(find "$CHANGELOG_DIR" -iname "[[:digit:]]**.txt" | sort -n | tail -1)
@@ -34,8 +31,8 @@ gedit -w "$CHANGELOG_FILE"
git add "$CHANGELOG_FILE"
echo "Updating build.gradle"
sed -i s/versionCode\ .*/versionCode\ "$VERSIONID"/g build.gradle
sed -i s/versionName\ .*/versionName\ "\"$VERSIONNAME\""/g build.gradle
sed -i -e "s/versionCode\ [0-9]\{1,\}/versionCode\ $VERSIONID/g" build.gradle
sed -i -e "s/versionName\ \"v[0-9\.]\{1,\}\"/versionName\ \"$VERSIONNAME\"/g" build.gradle
git diff
@@ -48,4 +45,4 @@ read
git add -u
git commit -m "$COMMIT_MESSAGE"
echo -e "Next steps:\n1. Push to Github\n2. Create release on Github: https://github.com/OpenTracksApp/OpenTracks/releases"
echo -e "Next steps:\n1. Push to Codeberg\n2. Create release on Codeberg: https://codeberg.org/OpenTracksApp/OpenTracks/releases"
+9 -4
View File
@@ -2,12 +2,17 @@
set -e
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64/
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64/
JAVA_VERSION=`java --version`
if [[ $JAVA_VERSION != "openjdk 17."* ]]; then
echo "OpenJDK version should be 17.X"
exit -1;
if [[ $JAVA_VERSION != "openjdk 21."* ]]; then
echo -e "OpenJDK version should be 21.X"
fi
git diff --exit-code || echo -e "\nWARNING WARNING WARNING WARNING WARNING: this branch contains uncommit changes."
if [[ $(git log "$(git describe --tags --abbrev=0)..HEAD" --no-merges --oneline | wc -l) -gt 0 ]]; then
echo -e "No version tag for this commit; please use RELEASE.sh";
fi
RELEASE_STORE_PASSWORD=
+33 -26
View File
@@ -6,7 +6,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.11.1'
classpath 'com.android.tools.build:gradle:9.0.0'
}
}
@@ -21,7 +21,7 @@ def getVersionName = { ->
return 'git describe --tags'.execute().text.trim()
}
def getVersionCode = { ->
def getVersionCodeNightly = { ->
return Integer.valueOf('git rev-list HEAD --count'.execute().text.trim())
}
@@ -38,6 +38,7 @@ android {
buildFeatures {
viewBinding = true
buildConfig = true
resValues = true
}
androidResources {
@@ -46,8 +47,8 @@ android {
defaultConfig {
applicationId 'de.dennisguse.opentracks'
versionCode 6284
versionName "v4.22.0"
versionCode 6564
versionName "v4.25.2"
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
@@ -57,6 +58,17 @@ android {
testInstrumentationRunner 'de.dennisguse.opentracks.TestRunner'
testInstrumentationRunnerArguments clearPackageData: 'true'
}
testOptions {
managedDevices {
localDevices {
mediumPhone {
device = "Medium Phone"
apiLevel = 35
systemImageSource = "aosp"
}
}
}
}
signingConfigs {
nightly {
if (System.getProperty('nightly_store_file') != null) {
@@ -95,41 +107,35 @@ android {
nightly {
dimension 'version'
applicationId 'de.dennisguse.opentracks.nightly'
resValue 'string', 'applicationId', 'de.dennisguse.opentracks.nightly'
versionCode getVersionCodeNightly()
versionName getVersionName()
base.archivesName = "de.dennisguse.opentracks.nightly_${getVersionCodeNightly()}"
signingConfig = signingConfigs.nightly
}
irreproducible {
// Non-reproducible: https://f-droid.org/de/packages/de.dennisguse.opentracks/
dimension 'version'
applicationId 'de.dennisguse.opentracks'
resValue 'string', 'applicationId', 'de.dennisguse.opentracks'
versionNameSuffix 'irreproducible'
}
reproducible {
// Developer Binaries: https://github.com/OpenTracksApp/OSMDashboard/releases/download/v%v/de.dennisguse.opentracks.playstore_%v.apk
// Developer Binaries: https://codeberg.org/OpenTracksApp/OSMDashboard/releases/download/v%v/de.dennisguse.opentracks.playstore_%v.apk
// FDroid: https://f-droid.org/de/packages/de.dennisguse.opentracks.playstore
// PlayStore: https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore
dimension 'version'
applicationId 'de.dennisguse.opentracks.playstore'
resValue 'string', 'applicationId', 'de.dennisguse.opentracks.playstore'
base.archivesName = "de.dennisguse.opentracks.playstore_${getVersionName()}_${android.defaultConfig.versionCode}"
signingConfig = signingConfigs.release
}
}
applicationVariants.configureEach { variant ->
variant.resValue 'string', 'applicationId', variant.applicationId
variant.outputs.configureEach {
if (variant.flavorName == 'nightly') {
setVersionCodeOverride(getVersionCode())
setVersionNameOverride(getVersionName())
}
if (variant.flavorName == 'reproducible') {
outputFileName = "${applicationId}_${variant.versionName}.apk"
} else {
outputFileName = "${applicationId}_${variant.versionCode}.apk"
}
}
}
dependenciesInfo {
// Disables dependency metadata when building APKs.
includeInApk = false
@@ -146,19 +152,20 @@ dependencies {
implementation 'androidx.preference:preference:1.2.1'
implementation 'androidx.documentfile:documentfile:1.1.0'
implementation 'androidx.gridlayout:gridlayout:1.1.0'
implementation 'com.google.android.material:material:1.12.0'
implementation 'com.google.android.material:material:1.13.0'
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
implementation 'androidx.core:core:1.17.0'
implementation 'androidx.core:core-splashscreen:1.0.1'
implementation 'androidx.core:core-splashscreen:1.2.0'
implementation 'androidx.mediarouter:mediarouter:1.8.1'
implementation 'androidx.core:core-location-altitude:1.0.0-alpha03'
implementation 'androidx.core:core-location-altitude:1.0.0-beta01'
implementation 'androidx.work:work-runtime:2.11.1'
androidTestImplementation 'androidx.test:core:1.7.0'
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
androidTestImplementation 'androidx.test:rules:1.7.0'
androidTestImplementation 'androidx.test:runner:1.7.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'
androidTestImplementation 'org.mockito:mockito-android:5.19.0'
androidTestImplementation 'org.mockito:mockito-android:5.21.0'
androidTestUtil 'androidx.test:orchestrator:1.6.1'
}
@@ -0,0 +1,4 @@
v4.22.0: OpenTracks
Změny:
- Přesun na https://codeberg.org/OpenTracksApp/OpenTracks
@@ -0,0 +1,17 @@
v3.0.1: OpenTracks
Änderungen:
- Markierungen teilen
- Umstrukturierte Einstellungen
- Zurück-Schaltfläche in der Aktionsleiste entfernt
- Export: Option zur Einstellung der Fotogröße entfernt
- Export: KML als Format ist jetzt auswählbar
Fehlerbehebungen:
- Exportverzeichnis ist jetzt /OpenTracks/
- Querformatmodus stürzt nicht mehr ab
- KML/KMZ-Export behoben
Entwickler:
- Tests sind ausführbar (aber viele schlagen fehl)
- Kontinuierliche Integration mit TravisCI aktiviert (keine Tests)
@@ -0,0 +1,14 @@
v3.1.0: OpenTracks
Änderungen:
- Anzeige der Strecke in Karten-Apps hinzugefügt (getestet mit OsmAnd und Maps.ME)
- Während der Verfolgung werden Streckenstatistiken angezeigt, ohne das Gerät entsperren zu müssen
- Querformat für die Detailansicht der Strecke entfernt (war fehlerhaft)
- Verlauf der Suchvorschläge entfernt
Fehlerbehebung:
- Speicherberechtigung für „Alle importieren/Alle exportieren” anfordern
Entwickler:
- Upgrade auf Gradle 5.6
- Release-Builds nicht minimieren
@@ -0,0 +1,10 @@
v4.23.0: OpenTracks
Änderungen:
- Benutzeroberfläche: Durchschnittsgeschwindigkeit des aktuellen Segments hinzugefügt
(derzeit nur in neuen Layouts verfügbar)
- Durch Klicken auf die Benachrichtigung wird die eigentliche Aufzeichnungs-Benutzeroberfläche geöffnet
Entwickler:
- Veralteter JobIntentService durch WorkManager ersetzt
- Verwende .isEmpty() außer für CharSequence und Stream.toList()
@@ -0,0 +1,8 @@
v4.23.1: OpenTracks
Änderungen:
- Neue UI-Komponente zur Anzeige der verstrichenen Zeit im aktuellen Segment
Fehlerbehebung:
- Sofortiger Export nach Workout abgestürzt (eingeführt in v4.23.0)
@@ -0,0 +1,10 @@
v4.23.2: OpenTracks
Änderungen:
- Vorausschauende Rückwärtsfunktion funktioniert vollständig
Fehlerbehebung:
- Absturz im Zusammenhang mit dem Dateinamen der Strecke in den Einstellungen behoben
Entwickler:
- Datenklasse für Statistiken eingeführt
@@ -0,0 +1,4 @@
v4.22.1: OpenTracks
Changes:
- First release via Codeberg
@@ -0,0 +1,4 @@
v4.22.2: OpenTracks
Bugfix:
- Voice annoucements setting for time and unit were linked
@@ -0,0 +1,10 @@
v4.23.0: OpenTracks
Changes:
- UI: added average pace of current segment
(currently only available in new layouts)
- Clicking on notification opens actual recording UI
Developer:
- Deprecated JobIntentService replaced with WorkManager
- Use .isEmpty() except for CharSequence and Stream.toList()
@@ -0,0 +1,7 @@
v4.23.1: OpenTracks
Changes:
- New UI component to show elapsed time in the current segment
Bugfix:
- Instant post-workout export crashed (introduced in v4.23.0)
@@ -0,0 +1,10 @@
v4.23.2: OpenTracks
Changes:
- Predictive back works completely
Bugfix:
- Fixed in crash related to the track filename in the settings
Developer:
- Introduced data class for Statistics
@@ -0,0 +1,15 @@
v4.24.0: OpenTracks
Changes:
- PublicAPI: start track only sets field if data is present
- Marker photos can be shared again
- Removed KMZ without photos
- Heart rate of 0.0 is treated as invalid (except for SensorStatistics)
- Removed import workaround for special coordinates (100/200)
(was used by MyTracks to export/import special trackpoints)
Developer:
- Track and Marker are records
- AggregatedStatistics are computed in database
- Split into TrackStatisticsUpdater and SegmentStatisticUpdater
- Introduced Iterator classes (instead of passing Cursor or List)
@@ -0,0 +1,6 @@
v4.24.1: OpenTracks
Bugfix:
- Restore compatibility with OSM Dashboard v6.4.1 and earlier (introduced in v4.24.0)
- Crash when using default track name by number (introduced in v4.24.0)
@@ -0,0 +1,4 @@
v4.24.2: OpenTracks
Bugfix:
- Crash when select all (introduced in v4.24.0)
@@ -0,0 +1,13 @@
v4.25.0: OpenTracks
Changes:
- Record button is centered
- Support for BLE thermometer (Environmental Sensing)
Bugfix:
- On import: get type from filename (not URI)
Developer:
- TrackPoint is a record
- Update to Android Gradle Plugin 9.0
@@ -0,0 +1,7 @@
v4.25.1: OpenTracks
Bugfix:
- Since v4.24.0, the API for transferring data to OSM Dashboard was broken.
Sadly, the changes in OpenTracks could not be reverted.
Thus, the protocol version was bumped to 3.
This requires updating OSM Dashboard to a version AFTER v6.5.7.
@@ -0,0 +1,8 @@
v4.25.2: OpenTracks
Changes:
- Restored compatibility with OSMDashboard (Dashboard API protocol version 2)
Bugfix:
- Update UI immediately after resuming a track
@@ -0,0 +1,4 @@
v4.22.0: OpenTracks
Athruithe:
- Aistriú go https://codeberg.org/OpenTracksApp/OpenTracks
@@ -0,0 +1,10 @@
v4.23.2: OpenTracks
Cambios:
- a volta predictiva funciona
Arranxo:
- do fallo que daba relacionado co nome do ficheiro nos axustes
Desenvolvemento:
- Engádese data class para as Estatísticas
@@ -4,7 +4,7 @@ Modifiche:
- Velocità degli annunci vocali configurabile
- API <26: L'icona del lanciatore omette lo sfondo
Bugfix:
Correzioni:
- UI di registrazione: mostra i valori corretti
- Registrazione UI: mostra (di nuovo) l'altitudine in EGM2008
@@ -0,0 +1,4 @@
v4.22.0: OpenTracks
Modifiche:
- Migrazione a https://codeberg.org/OpenTracksApp/OpenTracks
@@ -0,0 +1,4 @@
v4.22.1: OpenTracks
Modifiche:
- Primo rilascio da Codeberg
@@ -0,0 +1,4 @@
v4.22.2: OpenTracks
Correzioni:
- L'impostazione degli annunci vocali per l'ora e l'unità di misura è stata collegata
@@ -0,0 +1,10 @@
(versionName): OpenTracks
Modifiche:
-
Correzioni:
-
Sviluppatore:
-
@@ -1 +1 @@
Un tracker sportivo che rispetta completamente la tua privacy.
Un tracker sportivo che rispetta la tua privacy.
@@ -1,6 +1,6 @@
v3.0.0: Opentracks
v3.0.0: திறந்தபாதைகள்
ஓபன்ட்ராக்சின் முதல் வெளியீடு (அதாவது, மைட்ராக்சிலிருந்து ஆர்ட் ஃபோர்க்)
திறந்தபாதைகளின் முதல் வெளியீடு (அதாவது, மைட்ராக்சிலிருந்து ஆர்ட் ஃபோர்க்)
மாற்றங்கள்:
- மைட்ராக்சிலிருந்து ஓபன்ட்ராக்சுக்கு மறுபெயரிடப்பட்டது
@@ -1,4 +1,4 @@
v3.8.0: Opentracks
v3.8.0: திறந்தபாதைகள்
மாற்றத்தை உடைத்தல்:
- சிபிஎக்ச் இறக்குமதி/ஏற்றுமதி விரைவு, கேடென்ச் மற்றும் ஆர்ட்ரேட் டிராக் பாயிண்ட் எக்ச்டென்சன்/வி 2 வழியாக
@@ -6,7 +6,7 @@ v3.8.0: Opentracks
மாற்றங்கள்:
- டாச்போர்டு ஏபிஐ: குறிப்பான்களுக்கான அணுகலை வழங்குதல் (ஓபன்ட்ராக்சில் காண்பிக்க திரும்பப்பெறுதல்)
- புதிய மார்க்கர் படவுரு (ஆரஞ்சு புச் முள், எச்.வி.சி)
- இறக்குமதிக்கான கோப்புகளை கோப்பு மேலாளர் வழியாக தேர்ந்தெடுக்கலாம்
- இறக்குமதிக்கான கோப்புகளைக் கோப்பு மேலாளர் வழியாகத் தேர்ந்தெடுக்கலாம்
பிழைத்திருத்தங்கள்:
- ஒருங்கிணைந்த புள்ளிவிவரங்கள் இடைமுகம் விருப்பமான விகிதத்தை மதிக்கிறது
@@ -1,4 +1,4 @@
v3.9.0: Opentracks
v3.9.0: திறந்தபாதைகள்
மாற்றங்கள்:
- தடங்களை அவற்றின் UUID உடன் ஏற்றுமதி செய்யுங்கள்
@@ -1,4 +1,4 @@
v3.12.0: ஓபன்ட்ராக்ச்
v3.12.0: திறந்தபாதைகள்
மாற்றங்கள்:
- ஊடலை சென்சார் முகவரிகள்
@@ -1,4 +1,4 @@
v3.15.0: ஓபன்ட்ராக்ச்
v3.15.0: திறந்தபாதைகள்
எச்சரிக்கை:
இந்த வெளியீடு மாற்ற முடியாத தரவுத்தள மாற்றங்களைப் பயன்படுத்துகிறது.
@@ -14,7 +14,7 @@ v3.15.0: ஓபன்ட்ராக்ச்
(தயவுசெய்து OSMDashboard 2.10.0 ஐ மேம்படுத்தவும்)
மாற்றங்கள்:
- KML/KMZ சரியாக காப்புப் பிரதி எடுக்கவும் மீட்டமைக்கவும் பயன்படுத்தலாம் (புள்ளிவிவரங்கள் உட்பட)
- KML/KMZ சரியாகக் காப்புப் பிரதி எடுக்கவும் மீட்டமைக்கவும் பயன்படுத்தலாம் (புள்ளிவிவரங்கள் உட்பட)
டெவலப்பர்:
- மறுவேலை செய்யப்பட்ட தட பிரிவு மற்றும் அது சேமிப்பு (முன்னர் இடைநிறுத்தம்/மீண்டும் மார்க்கர்)
@@ -1,4 +1,4 @@
v3.16.0: ஓபன்ட்ராக்ச்
v3.16.0: திறந்தபாதைகள்
மாற்றங்கள்:
- சைக்கிள் ஓட்டுதல்: பி.எல்.இ வேக சென்சாரிலிருந்து தூரத்தைப் பெறுங்கள்
@@ -1,4 +1,4 @@
v3.17.0: Opentracks
v3.17.0: திறந்தபாதைகள்
மாற்றங்கள்:
- கயாக்கிங்கிற்கு ஐகானைச் சேர்க்கவும்
@@ -1,4 +1,4 @@
v3.18.0: ஓபன்ட்ராக்ச்
v3.18.0: திறந்தபாதைகள்
மாற்றங்கள்:
- உயரம் EGM2008 இல் காட்டப்பட்டுள்ளது (WGS84 எனச் சேமிக்கப்படுகிறது/ஏற்றுமதி செய்யப்படுகிறது)
@@ -1,4 +1,4 @@
v3.18.3: ஓபன்ட்ராக்ச்
v3.18.3: திறந்தபாதைகள்
மாற்றங்கள்:
- KML பாகுபடுத்தலுக்கு/GX: KOURS இன் ஆர்டர் தேவையில்லை
@@ -1,4 +1,4 @@
v3.18.6: Opentracks
v3.18.6: திறந்தபாதைகள்
மாற்றங்கள்:
- "வரைபடத்தில் காட்டு"- KMZ க்கு பதிலாக KML வழியாகச் செயல்பாடு
@@ -1,4 +1,4 @@
v3.19.0: Opentracks
v3.19.0: திறந்தபாதைகள்
மாற்றங்கள்:
- இயங்கும் விரைவு மற்றும் கேடென்ச் (ஆர்.சி.எச்) சென்சார்களுக்கான உதவி செயல்படுத்தப்பட்டது
@@ -1,4 +1,4 @@
v3.20.2: ஓபன்ட்ராக்ச்
v3.20.2: திறந்தபாதைகள்
மாற்றங்கள்:
- மிதவைகளுக்குப் பதிலாக உயரத்தை ஆதாயம் மற்றும் இழப்பு என்பது எண்கள்
@@ -1,4 +1,4 @@
v3.21.0: Opentracks
v3.21.0: திறந்தபாதைகள்
மாற்றங்கள்:
- பதிவு செய்யும்போது: டிராக் பாயிண்டுகளின் செயலற்ற அல்லாத உருவாக்கத்தை அகற்று
@@ -1,4 +1,4 @@
v3.24.0: Opentracks
v3.24.0: திறந்தபாதைகள்
மாற்றங்கள்:
- சி.பி.எச் விருப்பமானது (உட்புற பயன்பாட்டிற்கும்): தூரத்தை வழங்கும் பி.எல்.இ சென்சார்களைப் பயன்படுத்தினால் பயன்படுத்தப்பட்டால்
@@ -1,4 +1,4 @@
v3.24.1: Opentracks
v3.24.1: திறந்தபாதைகள்
மாற்றங்கள்:
- பிற பயன்பாடுகளிலிருந்து பதிவுகளைத் தொடங்க பநிஇ சேர்க்கப்பட்டது (இயல்பாக முடக்கப்பட்டுள்ளது)
@@ -1,8 +1,8 @@
V3.27.0: Opentracks
V3.27.0: திறந்தபாதைகள்
மாற்றங்கள்:
- குரல் அன்னியூசென்ச்: 100 மீ, 200 மீ மற்றும் 500 மீ
- இடைவெளி: 100 மீ மற்றும் 500 மீ சேர்க்கவும்
- இடைவெளி: 100 மீ மற்றும் 500 மீ சேர்
- கடிகாரம் இப்போது விநாடிகளையும் காட்டுகிறது
- சி.பி.எக்ச், கே.எம்.எல், கே.எம்.இசட்: கிடைமட்ட துல்லியத்தை ஏற்றுமதி செய்து இறக்குமதி செய்க
- சி.பி.எக்ச்: கிளச்ட்ரச்ட் நீட்டிப்பு வழியாக ஒட்டுமொத்த தூரத்தை ஏற்றுமதி செய்யுங்கள்
@@ -1,4 +1,4 @@
v4.0.0: Opentracks
v4.0.0: திறந்தபாதைகள்
மாற்றங்கள்:
- கீழ் பயன்பாட்டு பட்டியுடன் இடைமுகம் மறுசீரமைப்பு.
@@ -1,4 +1,4 @@
V4.0.1: Opentracks
V4.0.1: திறந்தபாதைகள்
மாற்றங்கள்:
- குரல் அறிவிப்புகள் இப்போது கட்டமைக்கப்படுகின்றன
@@ -1,4 +1,4 @@
V4.9.0: Opentracks
V4.9.0: திறந்தபாதைகள்
குறிப்பு: மாற்றத்தை உடைத்தல்
@@ -1,4 +1,4 @@
V4.10.0: Opentracks
V4.10.0: திறந்தபாதைகள்
மாற்றங்கள்:
- BLE சுற்றுச்சூழல் உணர்திறன் (காற்றழுத்தமானி) வழியாக உதவி ஆதாயம்/இழப்பு
@@ -1,4 +1,4 @@
v4.13.0: ஓபன்ட்ராக்ச்
v4.13.0: திறந்தபாதைகள்
மாற்றங்கள்:
- பொது பநிஇ வழியாகக் குறிப்பான்களை உருவாக்க முடியும்
@@ -0,0 +1,4 @@
v4.22.0: OpenTracks
மாற்றங்கள்:
- https://codeberg.org/OpenTracksApp/OpenTracks க்கு இடம்பெயர்தல்
@@ -0,0 +1,4 @@
v4.22.1: OpenTracks
மாற்றங்கள்:
- கோட்பெர்க் வழியாக முதல் வெளியீடு
@@ -0,0 +1,3 @@
v4.22.2: திறந்தபாதைகள்
பிழைத்திருத்தம்:
- நேரம் மற்றும் அலகுக்கான குரல் அறிவிப்பு அமைப்பு இணைக்கப்பட்டது
@@ -0,0 +1,10 @@
v4.23.0: OpenTracks
மாற்றங்கள்:
- UI: தற்போதைய பிரிவின் சராசரி வேகத்தைச் சேர்த்தது
(தற்போது புதிய தளவமைப்புகளில் மட்டுமே கிடைக்கிறது)
- அறிவிப்பைக் சொடுக்கு செய்வதன் மூலம் உண்மையான பதிவு இடைமுகம் திறக்கும்
டெவலப்பர்:
- நிராகரிக்கப்பட்ட JobIntentService, WorkManager உடன் மாற்றப்பட்டது
- CharSequence மற்றும் Stream.toList() தவிர .isEmpty() ஐப் பயன்படுத்தவும்
@@ -0,0 +1,7 @@
v4.23.1: OpenTracks
மாற்றங்கள்:
- தற்போதைய பிரிவில் கழிந்த நேரத்தைக் காட்ட புதிய இடைமுகம் கூறு
பிழைத்திருத்தம்:
- உடற்பயிற்சிக்குப் பிந்தைய உடனடி ஏற்றுமதி செயலிழந்தது (v4.23.0 இல் அறிமுகப்படுத்தப்பட்டது)
@@ -0,0 +1,10 @@
v4.23.2: OpenTracks
மாற்றங்கள்:
- முன்கணிப்பு முற்றிலும் வேலை செய்கிறது
பிழைத்திருத்தம்:
- அமைப்புகளில் டிராக் கோப்புப் பெயருடன் தொடர்புடைய செயலிழப்பில் சரி செய்யப்பட்டது
டெவலப்பர்:
- புள்ளிவிவரங்களுக்கான தரவு வகுப்பு அறிமுகப்படுத்தப்பட்டது
@@ -0,0 +1,15 @@
v4.24.0: OpenTracks
மாற்றங்கள்:
- PublicAPI: தரவு இருந்தால் ஸ்டார்ட் டிராக் மட்டுமே புலத்தை அமைக்கும்
- மார்க்கர் புகைப்படங்களை மீண்டும் பகிரலாம்
- புகைப்படங்கள் இல்லாமல் KMZ அகற்றப்பட்டது
- இதயத் துடிப்பு 0.0 தவறானதாகக் கருதப்படுகிறது (சென்சார் ஸ்டாடிஸ்டிக்ஸ் தவிர)
- சிறப்பு ஒருங்கிணைப்புகளுக்கான இறக்குமதி தீர்வு நீக்கப்பட்டது (100/200)
(சிறப்பு டிராக் பாயிண்ட்களை ஏற்றுமதி/இறக்குமதி செய்ய MyTracks பயன்படுத்தியது)
டெவலப்பர்:
- ட்ராக் மற்றும் மார்க்கர் பதிவுகள்
- திரட்டப்பட்ட புள்ளிவிவரங்கள் தரவுத்தளத்தில் கணக்கிடப்படுகின்றன
- TrackStatisticsUpdater மற்றும் SegmentStatisticUpdater எனப் பிரிக்கவும்
- அறிமுகப்படுத்தப்பட்ட இட்டரேட்டர் வகுப்புகள் (கர்சர் அல்லது பட்டியலைக் கடந்து செல்வதற்குப் பதிலாக)
@@ -0,0 +1,5 @@
v4.24.1: OpenTracks
பிழைத்திருத்தம்:
- OSM டாஷ்போர்டு v6.4.1 மற்றும் அதற்கு முந்தையவற்றுடன் இணக்கத்தன்மையை மீட்டெடுக்கவும் (v4.24.0 இல் அறிமுகப்படுத்தப்பட்டது)
- எண்ணின்படி இயல்புநிலை ட்ராக் பெயரைப் பயன்படுத்தும்போது செயலிழக்கிறது (v4.24.0 இல் அறிமுகப்படுத்தப்பட்டது)
@@ -0,0 +1,4 @@
v4.24.2: OpenTracks
பிழைத்திருத்தம்:
- அனைத்தையும் தேர்ந்தெடுக்கும்போது செயலிழக்க (v4.24.0 இல் அறிமுகப்படுத்தப்பட்டது)
@@ -0,0 +1,12 @@
v4.25.0: OpenTracks
மாற்றங்கள்:
- பதிவுப் பொத்தான் மையமாக உள்ளது
- BLE தெர்மோமீட்டருக்கான ஆதரவு (சுற்றுச்சூழல் உணர்தல்)
பிழைத்திருத்தம்:
- இறக்குமதியில்: கோப்பு பெயரிலிருந்து வகையைப் பெறுங்கள் (URI அல்ல)
டெவலப்பர்:
- TrackPoint ஒரு பதிவு
- Android Gradle செருகுநிரல் 9.0 க்கு புதுப்பிக்கவும்
@@ -1,22 +1,22 @@
一款尊重您的隐私的运动追踪应用。
尊重您隐私的运动追踪伙伴。
健康幸福的生活总少不了各种运动和户外活动。
运动与户外活动能带来快乐、提升技能、增强自信。
使用本应用,您可以保持对您的训练数据的有效追踪,为您的健康助力。
本应用可以在您跑步或散步时记录数据,也可以在您骑车时作为一个大屏幕自行车电脑。
您可以为路上的有趣位置创建标记,还可以用图片作为标记。
您的数据会被详尽地记录下来,供您查阅分析。
您的数据只会分享给您想要分享给的人。
通过跟踪训练数据,珍视您的健康。
记录您的跑步或步行数据,并提供大屏的专用骑行码表。
用照片标记沿途有趣的地点。
保留详细记录的统计数据以供分析。
仅分享您愿意公开的数据。
* 语音播报
* 支持蓝牙LE传感器:心率、速度和距离(骑车)、踏频(骑车)和功率计(骑车)。
* 海拔增减:通过气压传感器。
* EGM2008 海拔显示(平均海平面以上);导出为 WGS84。
* 导出路线数据,支持 KMZ(包括照片)、KML 和 GPX 格式。
* 不需要联网,不需要额外权限。
* 语音播报。
* 支持蓝牙低功耗传感器:心率、速度和距离(骑行)、踏频(骑行)、功率计(骑行)。
* 海拔上升和下降:通过气压传感器。
* 采用 EGM2008 显示海拔(平均海平面以上);导出为 WGS84。
* 以 KMZ(含照片)、KML 或 GPX 格式导出轨迹数据。
* 无需互联网访问或额外权限。
* 深色和浅色主题,跟随系统设置。
* 没有广告。
* 无广告。
本应用是开源软件
意味着您可以使用、学习、修改和分发本应用的源代码。
本应用使用 Apache 2.0 协议发布。
自由软件/开放源代码
意味着您可以使用、研究、修改和分享源代码。
采用 Apache 2.0 许可
@@ -1 +1 @@
一款尊重您的隐私的运动追踪应用。
尊重您隐私的运动追踪伙伴。
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
-3
View File
@@ -114,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@@ -172,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
Vendored
+93 -94
View File
@@ -1,94 +1,93 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
}
@@ -36,7 +36,8 @@ public class EspressoDeleteTrackTest {
@Test
public void espressoDeleteTrackTest() {
// TrackListActivity: start recording
onView(withId(R.id.track_list_fab_action)).perform(click());
onView(withId(R.id.track_list_fab_action))
.perform(click());
// TrackRecordingActivity
onView(withId(R.id.track_recording_fab_action))
@@ -46,13 +47,14 @@ public class EspressoDeleteTrackTest {
.perform(longClick());
// TrackStoppedActivity
onView(withId(R.id.finish_button)).perform(click());
onView(withId(R.id.finish_button))
.perform(click());
// select track
onView(allOf(withParent(withId(R.id.track_list)), withParentIndex(0))).perform(longClick());
onView(allOf(withParent(withId(R.id.track_list)), withParentIndex(0)))
.perform(longClick());
// open menu and delete selected track
//TODO openActionBarOverflowOrOptionsMenu(); doesn't work
onView(
allOf(withContentDescription("More options"),
childAtPosition(
@@ -63,9 +65,11 @@ public class EspressoDeleteTrackTest {
isDisplayed()))
.perform(click());
onView(withText("Delete")).perform(click());
onView(withText("Delete"))
.perform(click());
onView(withText("OK")).perform(click());
onView(withText("OK"))
.perform(click());
// tracklist is empty now
onView(allOf(withText("Start recording your next adventure here"), isDisplayed()));
@@ -15,7 +15,6 @@ import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
import androidx.test.espresso.ViewInteraction;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
@@ -45,41 +44,43 @@ public class EspressoEditTrackRecordingTest {
public void espressoEditTrackRecordingTest() {
{
// TrackListActivity: start recording
ViewInteraction fabRecordButton = onView(withId(R.id.track_list_fab_action));
fabRecordButton.perform(click());
onView(withId(R.id.track_list_fab_action))
.perform(click());
}
{
// TrackRecordingActivity
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
// wait; stay recording
trackControllerStopButton.perform(waitFor(4000));
onView(withId(R.id.track_recording_fab_action))
.perform(waitFor(4000));
// open menu
openContextualActionModeOverflowMenu();
// Click the item.
onView(withText(R.string.menu_edit)).perform(click());
onView(withText(R.string.menu_edit))
.perform(click());
// change name for "New Name"
ViewInteraction textInputEditText = onView(withId(R.id.track_edit_name));
textInputEditText.perform(scrollTo(), replaceText("New Name"));
onView(withId(R.id.track_edit_name))
.perform(scrollTo(), replaceText("New Name"));
ViewInteraction textInputEditText2 = onView(allOf(withId(R.id.track_edit_name), withText("New Name"), isDisplayed()));
textInputEditText2.perform(closeSoftKeyboard());
onView(allOf(withId(R.id.track_edit_name), withText("New Name"), isDisplayed()))
.perform(closeSoftKeyboard());
// save edition
ViewInteraction appCompatButton = onView(withId(R.id.track_edit_save));
appCompatButton.perform(click());
onView(withId(R.id.track_edit_save))
.perform(click());
// stop;
trackControllerStopButton.perform(longClick());
onView(withId(R.id.track_recording_fab_action))
.perform(longClick());
// 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()));
onView(withId(R.id.resume_button))
.check(matches(isDisplayed()));
onView(withId(R.id.finish_button))
.check(matches(isDisplayed()));
}
}
}
@@ -37,8 +37,8 @@ public class EspressoUITest {
public void record_stop_resume_stop_finish() {
{
// TrackListActivity: start recording
ViewInteraction trackControllerRecordButton = onView(withId(R.id.track_list_fab_action));
trackControllerRecordButton.perform(click());
onView(withId(R.id.track_list_fab_action))
.perform(click());
}
{
// TrackRecordingActivity: wait to record some time and then stop
@@ -66,13 +66,12 @@ public class EspressoUITest {
public void record_move_through_tabs() {
{
// TrackListActivity: start recording
ViewInteraction trackControllerRecordButton = onView(withId(R.id.track_list_fab_action));
trackControllerRecordButton.perform(click());
onView(withId(R.id.track_list_fab_action))
.perform(click());
}
{
// TrackRecordingActivity
ViewInteraction tabLayout = onView(withId(R.id.track_detail_activity_tablayout));
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
tabLayout.perform(selectTabAtIndex(1));
tabLayout.perform(waitFor(1000));
@@ -87,7 +86,8 @@ public class EspressoUITest {
tabLayout.perform(waitFor(1000));
// stop
trackControllerStopButton.perform(longClick());
onView(withId(R.id.track_recording_fab_action)).
perform(longClick());
}
}
@@ -1,99 +0,0 @@
package de.dennisguse.opentracks.chart;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.Duration;
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)
public class ChartPointTest {
@Test
public void create_by_time() {
// given
TrackStatistics statistics = new TrackStatistics();
statistics.setTotalTime(Duration.ofSeconds(1000));
// when
ChartPoint point = ChartPoint.create(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), false, UnitSystem.IMPERIAL_FEET);
// then
assertEquals(1000000, (long) point.timeOrDistance());
}
@Test
public void create_by_distance() {
// given
TrackStatistics statistics = new TrackStatistics();
statistics.setTotalDistance(Distance.of(1000));
// when
ChartPoint point = ChartPoint.create(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), true, UnitSystem.METRIC);
// then
assertEquals(1, (long) point.timeOrDistance());
}
@Test
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 = ChartPoint.create(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
// then
assertEquals(50, point.altitude(), 0.01);
assertEquals(36, point.speed(), 0.01);
assertEquals(1.66, point.pace(), 0.01);
}
@Test
public void create_sensorNotAvailable() {
// given
TrackStatistics statistics = new TrackStatistics();
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
.setAltitude(Altitude.EGM2008.of(50));
// when
ChartPoint point = ChartPoint.create(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
// then
assertNull(point.heartRate());
assertNull(point.cadence());
assertNull(point.power());
}
@Test
public void create_sensorAvailable() {
// given
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
.setAltitude(Altitude.EGM2008.of(50))
.setHeartRate(100f)
.setCadence(101f)
.setPower(102f);
TrackStatistics statistics = new TrackStatistics();
// when
ChartPoint point = ChartPoint.create(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
// then
assertEquals(100.0, point.heartRate(), 0.01);
assertEquals(101.0, point.cadence(), 0.01);
assertEquals(102.0, point.power(), 0.01);
}
}
@@ -1,70 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.chart;
import java.time.Instant;
import de.dennisguse.opentracks.data.models.Altitude;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.TrackPoint;
/**
* Commons utilities for creating stubs of track, location.
*
* @author Youtao Liu
*/
//TODO Move to ChartPointTest
class TrackStubUtils {
public static final double INITIAL_ALTITUDE = 22;
public static final long INITIAL_TIME = 1000L;
private static final double INITIAL_LATITUDE = 22;
private static final double INITIAL_LONGITUDE = 22;
private static final Distance INITIAL_ACCURACY = Distance.of(5);
private static final Speed INITIAL_SPEED = Speed.of(10);
private static final float INITIAL_BEARING = 3.0f;
/**
* Create a MyTracks location with default values.
*
* @return a track stub.
*/
public static TrackPoint createDefaultTrackPoint() {
return createDefaultTrackPoint(INITIAL_LATITUDE, INITIAL_LONGITUDE, INITIAL_ALTITUDE);
}
/**
* Creates a {@link TrackPoint} stub with specified values.
*
* @return a SensorDataSetLocation stub.
*/
private static TrackPoint createDefaultTrackPoint(double latitude, double longitude, double altitude) {
return new TrackPoint(TrackPoint.Type.TRACKPOINT,
new Position(
Instant.ofEpochMilli(INITIAL_TIME),
latitude,
longitude,
INITIAL_ACCURACY,
Altitude.WGS84.of(altitude),
INITIAL_ACCURACY,
INITIAL_BEARING,
INITIAL_SPEED
));
}
}
@@ -6,22 +6,28 @@ import android.util.Pair;
import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.MarkerIterator;
import de.dennisguse.opentracks.data.TrackPointIterator;
import de.dennisguse.opentracks.data.models.ActivityType;
import de.dennisguse.opentracks.data.models.Altitude;
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Statistics;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
import de.dennisguse.opentracks.ui.markers.MarkerUtils;
import de.dennisguse.opentracks.util.FileUtils;
@@ -37,11 +43,15 @@ public class TestDataUtil {
* Create a track without any trackPoints.
*/
public static Track createTrack(Track.Id trackId) {
Track track = new Track();
track.setId(trackId);
track.setName("Test: " + trackId.id());
return track;
return new Track(
trackId,
UUID.randomUUID(),
"Test: " + trackId.id(),
"",
"",
ActivityType.UNKNOWN,
ZoneOffset.UTC,
Statistics.DEFAULT);
}
/**
@@ -85,14 +95,10 @@ public class TestDataUtil {
createTrackPoint(i, TrackPoint.Type.SEGMENT_END_MANUAL)
);
//TODO Use TrackStatisticsUpdater
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(0));
stats.setTotalTime(Duration.ofMillis(0));
List<Marker> markers = List.of(
new Marker(trackId, trackPoints.get(1), "Marker 1", "Marker description 1", "Marker category 3", "", null),
new Marker(trackId, trackPoints.get(4), "Marker 2", "Marker description 2", "Marker category 3", "", null),
new Marker(trackId, trackPoints.get(5), "Marker 3", "Marker description 3", "Marker category 3", "", null)
new Marker(null, trackId, "Marker 1", "Marker description 1", "Marker typeLocalized 3", trackPoints.get(1).position(), null),
new Marker(null, trackId, "Marker 2", "Marker description 2", "Marker typeLocalized 3", trackPoints.get(4).position(), null),
new Marker(null, trackId, "Marker 3", "Marker description 3", "Marker typeLocalized 3", trackPoints.get(5).position(), null)
);
return new TrackData(track, trackPoints, markers);
@@ -110,8 +116,10 @@ public class TestDataUtil {
return pair.first;
}
public static TrackPoint createTrackPoint(int i) {
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT,
public static TrackPoint createTrackPoint(int i, TrackPoint.Type type, AltitudeGainLoss altitudeGainLoss) {
return new TrackPoint(
null,
type,
new Position(
Instant.ofEpochSecond(i),
INITIAL_LATITUDE + (double) i / 10000.0,
@@ -121,22 +129,25 @@ public class TestDataUtil {
null,
null,
Speed.of(5f + (i / 10f))
));
trackPoint.setHeartRate(100f + i % 80);
trackPoint.setCadence(300f + i);
trackPoint.setPower(400f + i);
trackPoint.setAltitudeGain(ALTITUDE_GAIN);
trackPoint.setAltitudeLoss(ALTITUDE_LOSS);
return trackPoint;
),
null,
HeartRate.of(100f + i % 80),
null,
Cadence.of(300f + i),
Power.of(400f + i),
altitudeGainLoss
);
}
public static TrackPoint createTrackPoint(int i, TrackPoint.Type type) {
TrackPoint trackPoint = createTrackPoint(i);
trackPoint.setType(type);
return trackPoint;
return createTrackPoint(i, TrackPoint.Type.TRACKPOINT, new AltitudeGainLoss(ALTITUDE_GAIN, ALTITUDE_LOSS));
}
public static TrackPoint createTrackPoint(int i) {
return createTrackPoint(i, TrackPoint.Type.TRACKPOINT);
}
/**
* Inserts a track with locations into the database.
*
@@ -145,45 +156,41 @@ public class TestDataUtil {
*/
public static void insertTrackWithLocations(ContentProviderUtils contentProviderUtils, Track track, List<TrackPoint> trackPoints) {
contentProviderUtils.insertTrack(track);
contentProviderUtils.bulkInsertTrackPoint(trackPoints, track.getId());
contentProviderUtils.bulkInsertTrackPoint(trackPoints, track.id());
}
public static Marker createMarkerWithPhoto(Context context, Track.Id trackId, TrackPoint trackPoint) throws IOException {
return createMarkerWithPhoto(context, trackId, trackPoint, "Marker description");
}
public static Marker createMarkerWithPhoto(Context context, Track.Id trackId, TrackPoint trackPoint, String description) throws IOException {
File dstFile = new File(MarkerUtils.getImageUrl(context, trackId));
dstFile.createNewFile();
Uri photoUri = FileUtils.getUriForFile(context, dstFile);
//TODO Use TrackStatisticsUpdater
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(0));
stats.setTotalTime(Duration.ofMillis(0));
return new Marker(trackId, trackPoint, "Marker name", "Marker description", "Marker category", "", photoUri);
return new Marker(
null,
trackId,
"Marker name",
description,
"Marker typeLocalized",
trackPoint.position(),
photoUri);
}
public static List<TrackPoint> getTrackPoints(ContentProviderUtils contentProviderUtils, Track.Id trackId) {
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointIterator(trackId, null)) {
ArrayList<TrackPoint> trackPoints = new ArrayList<>();
while (trackPointIterator.hasNext()) {
trackPoints.add(trackPointIterator.next());
}
trackPointIterator.forEachRemaining(trackPoints::add);
return trackPoints;
}
}
public static Pair<Track.Id, TrackStatistics> buildTrackWithTrackPoints(ContentProviderUtils contentProviderUtils, int numberOfPoints) {
Track dummyTrack = new Track();
dummyTrack.setId(new Track.Id(System.currentTimeMillis()));
dummyTrack.setName("Dummy Track");
contentProviderUtils.insertTrack(dummyTrack);
TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
for (int i = 0; i < numberOfPoints; i++) {
TrackPoint tp = TestDataUtil.createTrackPoint(i);
contentProviderUtils.insertTrackPoint(tp, dummyTrack.getId());
trackStatisticsUpdater.addTrackPoint(tp);
public static List<Marker> getMarkers(ContentProviderUtils contentProviderUtils, Track.Id trackId) {
try (MarkerIterator trackPointIterator = contentProviderUtils.getMarkerIterator(trackId)) {
ArrayList<Marker> markers = new ArrayList<>();
trackPointIterator.forEachRemaining(markers::add);
return markers;
}
dummyTrack.setTrackStatistics(trackStatisticsUpdater.getTrackStatistics());
contentProviderUtils.updateTrack(dummyTrack);
return new Pair<>(dummyTrack.getId(), trackStatisticsUpdater.getTrackStatistics());
}
}
@@ -6,8 +6,12 @@ import java.util.ArrayList;
import java.util.List;
import de.dennisguse.opentracks.data.models.Altitude;
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.TrackPoint;
@@ -20,7 +24,9 @@ public class TestSensorDataUtil {
sensorDataList.add(new TestSensorDataUtil.SensorData(time, hr, cadence, power, type));
int i = trackPointList.size() + 1;
TrackPoint tp = new TrackPoint(type,
TrackPoint tp = new TrackPoint(
null,
type,
new Position(
time,
TestDataUtil.INITIAL_LATITUDE + (double) i / 10000.0,
@@ -30,19 +36,15 @@ public class TestSensorDataUtil {
null,
null,
Speed.of(5f + (i / 10f))
)
),
null,
HeartRate.ofOrNull(hr),
null,
cadence != null ? Cadence.of(cadence) : null,
power != null ? Power.of(power) : null,
new AltitudeGainLoss(3, 3)
);
if (hr != null) {
tp.setHeartRate(hr);
}
if (cadence != null) {
tp.setCadence(cadence);
}
if (power != null) {
tp.setPower(power);
}
tp.setAltitudeGain(3f);
tp.setAltitudeLoss(3f);
trackPointList.add(tp);
}
@@ -1,73 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.data;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.content.ContentUris;
import android.content.Context;
import android.net.Uri;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
import org.junit.Test;
import de.dennisguse.opentracks.data.tables.MarkerColumns;
import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
import de.dennisguse.opentracks.data.tables.TracksColumns;
/**
* Tests {@link CustomContentProvider}.
*
* @author Youtao Liu
*/
public class CustomContentProviderTest {
private CustomContentProvider customContentProvider;
private final Context context = ApplicationProvider.getApplicationContext();
@Before
public void setUp() {
customContentProvider = new CustomContentProvider() {
};
}
/**
* Tests {@link CustomContentProvider#onCreate(android.content.Context)}.
*/
@Test
public void testOnCreate() {
assertTrue(customContentProvider.onCreate(context));
}
/**
* Tests {@link CustomContentProvider#getType(Uri)}.
*/
@Test
public void testGetType() {
assertEquals(TracksColumns.CONTENT_TYPE, customContentProvider.getType(TracksColumns.CONTENT_URI));
assertEquals(TracksColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TracksColumns.CONTENT_URI.buildUpon(), 1).build()));
assertEquals(TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI_BY_ID));
assertEquals(TrackPointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TrackPointsColumns.CONTENT_URI_BY_TRACKID.buildUpon(), 1).build()));
assertEquals(MarkerColumns.CONTENT_TYPE, customContentProvider.getType(MarkerColumns.CONTENT_URI));
assertEquals(MarkerColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(MarkerColumns.CONTENT_URI.buildUpon(), 1).build()));
}
}
@@ -27,7 +27,6 @@ import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.location.Location;
import android.util.Pair;
import androidx.test.core.app.ApplicationProvider;
@@ -42,28 +41,35 @@ import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.stream.Collectors;
import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.content.data.TestSensorDataUtil;
import de.dennisguse.opentracks.data.models.ActivityType;
import de.dennisguse.opentracks.data.models.Altitude;
import de.dennisguse.opentracks.data.models.AltitudeExtremities;
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.MarkerBuilder;
import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Statistics;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackBuilder;
import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.data.statistics.SensorStatistics;
import de.dennisguse.opentracks.data.tables.MarkerColumns;
import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
import de.dennisguse.opentracks.data.tables.TracksColumns;
import de.dennisguse.opentracks.stats.SensorStatistics;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.FileUtils;
/**
@@ -115,24 +121,32 @@ public class CustomContentProviderUtilsTest {
private void testIterator(Track.Id trackId, int numPoints) {
TrackPoint.Id lastPointId = initializeTrack(trackId, numPoints);
List<TrackPoint> locations = new ArrayList<>(numPoints);
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
try (TrackPointIterator it = contentProviderUtils.getTrackPointIterator(trackId, null)) {
while (it.hasNext()) {
TrackPoint trackPoint = it.next();
assertNotNull(trackPoint);
locations.add(trackPoint);
// Make sure the IDs are returned in the right order.
assertEquals(lastPointId.id() - numPoints + locations.size(), trackPoint.getId().id());
assertEquals(lastPointId.id() - numPoints + locations.size(), trackPoint.id().id());
}
assertEquals(numPoints, locations.size());
}
}
private TrackPoint.Id initializeTrack(Track.Id id, int numPoints) {
Track track = new Track();
track.setId(id);
track.setName("Test: " + id.id());
contentProviderUtils.insertTrack(track);
track = contentProviderUtils.getTrack(id);
Track createdTrack = new Track(
id,
UUID.randomUUID(),
"Test: " + id,
"",
"",
ActivityType.UNKNOWN,
ZoneOffset.UTC,
Statistics.DEFAULT);
contentProviderUtils.insertTrack(createdTrack);
Track track = contentProviderUtils.getTrack(id);
assertNotNull(track);
List<TrackPoint> trackPoints = new ArrayList<>(numPoints);
@@ -153,10 +167,10 @@ public class CustomContentProviderUtilsTest {
// Load all inserted trackPoints.
TrackPoint.Id lastPointId = null;
int counter = 0;
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(id, null)) {
try (TrackPointIterator it = contentProviderUtils.getTrackPointIterator(id, null)) {
while (it.hasNext()) {
TrackPoint trackPoint = it.next();
lastPointId = trackPoint.getId();
lastPointId = trackPoint.id();
counter++;
}
}
@@ -194,8 +208,8 @@ public class CustomContentProviderUtilsTest {
when(cursorMock.getString(columnIndex)).thenReturn(name);
Track track = ContentProviderUtils.createTrack(cursorMock);
assertEquals(trackId, track.getId());
assertEquals(name, track.getName());
assertEquals(trackId, track.id());
assertEquals(name, track.name());
}
private void assertCount(int trackCount, int trackPointCount, int markerCount) {
@@ -220,8 +234,15 @@ public class CustomContentProviderUtilsTest {
Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Marker marker = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
contentProviderUtils.insertMarker(marker);
contentProviderUtils.insertMarker(new Marker(
null,
trackId,
"",
"",
"",
getLastValidTrackPoint(trackId).position(),
null
));
assertCount(1, 10, 1);
@@ -239,14 +260,14 @@ public class CustomContentProviderUtilsTest {
public void testDeleteAllTracks_withMarkerAndPhoto() throws IOException {
// Insert track, points and marker with photo at first.
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
TrackPoint trackPoint = getLastValidTrackPoint(trackId);
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
contentProviderUtils.insertMarker(marker);
assertCount(1, 10, 1);
assertTrue(marker.hasPhoto());
assertNotNull(marker.photoUrl());
File dir = FileUtils.getPhotoDir(context, trackId);
assertTrue(dir.isDirectory());
assertEquals(1, dir.list().length);
@@ -276,8 +297,15 @@ public class CustomContentProviderUtilsTest {
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId2, 10);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10);
Marker marker = new Marker(trackId1, contentProviderUtils.getLastValidTrackPoint(trackId2));
contentProviderUtils.insertMarker(marker);
contentProviderUtils.insertMarker(new Marker(
null,
trackId1,
"",
"",
"",
getLastValidTrackPoint(trackId1).position(),
null
));
assertCount(3, 30, 1);
@@ -304,19 +332,19 @@ public class CustomContentProviderUtilsTest {
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10);
// Insert a marker in tracks trackId and trackId + 1.
TrackPoint trackPoint1 = contentProviderUtils.getLastValidTrackPoint(trackId1);
TrackPoint trackPoint1 = getLastValidTrackPoint(trackId1);
Marker marker1 = TestDataUtil.createMarkerWithPhoto(context, trackId1, trackPoint1);
contentProviderUtils.insertMarker(marker1);
File dir1 = FileUtils.getPhotoDir(context, trackId1);
TrackPoint trackPoint2 = contentProviderUtils.getLastValidTrackPoint(trackId2);
TrackPoint trackPoint2 = getLastValidTrackPoint(trackId2);
Marker marker2 = TestDataUtil.createMarkerWithPhoto(context, trackId2, trackPoint2);
contentProviderUtils.insertMarker(marker2);
File dir2 = FileUtils.getPhotoDir(context, trackId2);
// Check.
assertCount(3, 30, 2);
assertTrue(marker1.hasPhoto());
assertNotNull(marker1.photoUrl());
assertTrue(dir1.isDirectory());
assertEquals(1, dir1.list().length);
assertTrue(dir1.exists());
@@ -348,7 +376,7 @@ public class CustomContentProviderUtilsTest {
// then
assertEquals(initialTrackNumber + 1, allTracks.size());
assertEquals(trackId, allTracks.get(allTracks.size() - 1).getId());
assertEquals(trackId, allTracks.get(allTracks.size() - 1).id());
}
/**
@@ -375,61 +403,57 @@ public class CustomContentProviderUtilsTest {
contentProviderUtils.insertTrack(track);
// when / then
assertNotNull(contentProviderUtils.getTrack(track.getUuid()));
assertNotNull(contentProviderUtils.getTrack(track.uuid()));
}
/**
* Tests the method {@link ContentProviderUtils#updateTrack(Track)}
*/
@Test
public void testUpdateTrack() {
// given
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Track track = TestDataUtil.createTrack(trackId);
TrackBuilder track = new TrackBuilder(TestDataUtil.createTrack(trackId));
String nameOld = "name1";
String nameNew = "name2";
track.setName(nameOld);
// when / then
contentProviderUtils.insertTrack(track);
assertEquals(nameOld, contentProviderUtils.getTrack(trackId).getName());
contentProviderUtils.insertTrack(track.getTrack());
assertEquals(nameOld, contentProviderUtils.getTrack(trackId).name());
track.setName(nameNew);
contentProviderUtils.updateTrack(track);
assertEquals(nameNew, contentProviderUtils.getTrack(trackId).getName());
contentProviderUtils.updateTrack(track.getTrack());
assertEquals(nameNew, contentProviderUtils.getTrack(trackId).name());
}
/**
* Tests the method {@link ContentProviderUtils#createContentValues(Marker)}.
*/
@Test
public void testCreateContentValues_marker() {
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Pair<Track, List<TrackPoint>> track = TestDataUtil.createTrack(trackId, 10);
TrackBuilder trackBuilder = new TrackBuilder(track.first);
// AverageSpeed
TrackStatistics statistics = new TrackStatistics();
statistics.setStartTime(Instant.ofEpochMilli(1000));
statistics.setStopTime(Instant.ofEpochMilli(2500));
statistics.setTotalTime(Duration.ofMillis(1500));
statistics.setMovingTime(Duration.ofMillis(700));
statistics.setTotalDistance(Distance.of(750.0));
statistics.setTotalAltitudeGain(50.0f);
statistics.setMaxSpeed(Speed.of(60.0));
statistics.setMaxAltitude(1250.0);
statistics.setMinAltitude(1200.0);
trackBuilder.setStatistics(new Statistics(
Instant.ofEpochMilli(1000),
Instant.ofEpochMilli(2500),
Duration.ofMillis(1500),
Duration.ofMillis(700),
Distance.of(750),
Speed.of(60),
new AltitudeExtremities(1250, 1200),
new AltitudeGainLoss(50, 50),
null,
null
));
contentProviderUtils.insertTrack(trackBuilder.getTrack());
track.first.setTrackStatistics(statistics);
contentProviderUtils.insertTrack(track.first);
Marker marker = new Marker(trackId, track.second.get(0));
MarkerBuilder marker = new MarkerBuilder(trackId, track.second.get(0));
marker.setDescription(TEST_DESC);
contentProviderUtils.insertMarker(marker);
contentProviderUtils.insertMarker(marker.getMarker());
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(contentResolverMock);
Marker.Id markerId = new Marker.Id(System.currentTimeMillis());
marker.setId(markerId);
ContentValues contentValues = contentProviderUtils.createContentValues(marker);
ContentValues contentValues = contentProviderUtils.createContentValues(marker.getMarker(), trackId);
assertEquals(markerId.id(), contentValues.get(MarkerColumns._ID));
assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(MarkerColumns.LONGITUDE));
assertEquals(TEST_DESC, contentValues.get(MarkerColumns.DESCRIPTION));
@@ -446,15 +470,18 @@ public class CustomContentProviderUtilsTest {
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first.
Marker marker1 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
marker1.setDescription(TEST_DESC);
contentProviderUtils.insertMarker(marker1);
Marker.Id marker1Id = contentProviderUtils.insertMarker(new Marker(
null,
trackId,
"",
TEST_DESC,
"",
getLastValidTrackPoint(trackId).position(),
null
));
// Check insert was done.
assertEquals(contentProviderUtils.getMarkers(trackId).size(), 1);
// Get marker id that needs to delete.
Marker.Id marker1Id = contentProviderUtils.insertMarker(marker1);
assertEquals(1, TestDataUtil.getMarkers(contentProviderUtils, trackId).size());
// Delete
contentProviderUtils.deleteMarker(context, marker1Id);
@@ -473,18 +500,18 @@ public class CustomContentProviderUtilsTest {
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first.
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
TrackPoint trackPoint = getLastValidTrackPoint(trackId);
Marker marker1 = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
contentProviderUtils.insertMarker(marker1);
// Check insert was done.
assertEquals(contentProviderUtils.getMarkers(trackId).size(), 1);
assertEquals(1, TestDataUtil.getMarkers(contentProviderUtils, trackId).size());
// Get marker id that needs to delete.
Marker.Id marker1Id = contentProviderUtils.insertMarker(marker1);
// Check marker has photo and it's in the external storage.
assertTrue(marker1.hasPhoto());
assertNotNull(marker1.photoUrl());
File dir = FileUtils.getPhotoDir(context, trackId);
assertTrue(dir.isDirectory());
assertEquals(1, dir.list().length);
@@ -523,20 +550,31 @@ public class CustomContentProviderUtilsTest {
// TestDataUtil.insertTrackWithLocations(contentProviderUtils, track);
// Insert at first.
Marker marker1 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
marker1.setDescription(MOCK_DESC);
Marker.Id marker1Id = contentProviderUtils.insertMarker(marker1);
Marker marker2 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
marker2.setDescription(MOCK_DESC);
Marker.Id marker2Id = contentProviderUtils.insertMarker(marker2);
Marker.Id marker1Id = contentProviderUtils.insertMarker(new Marker(
null,
trackId,
"",
MOCK_DESC,
"",
getLastValidTrackPoint(trackId).position(),
null
));
Marker.Id marker2Id = contentProviderUtils.insertMarker(new Marker(
null,
trackId,
"",
MOCK_DESC,
"",
getLastValidTrackPoint(trackId).position(),
null
));
// Delete
assertNotNull(contentProviderUtils.getMarker(marker1Id));
contentProviderUtils.deleteMarker(context, marker1Id);
assertNull(contentProviderUtils.getMarker(marker1Id));
assertEquals(MOCK_DESC, contentProviderUtils.getMarker(marker2Id).getDescription());
assertEquals(MOCK_DESC, contentProviderUtils.getMarker(marker2Id).description());
}
/**
@@ -547,14 +585,17 @@ public class CustomContentProviderUtilsTest {
Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Marker marker1 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
Marker marker2 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
Marker marker3 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
Marker marker4 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
contentProviderUtils.insertMarker(marker1);
contentProviderUtils.insertMarker(marker2);
contentProviderUtils.insertMarker(marker3);
contentProviderUtils.insertMarker(marker4);
for (int i = 0; i < 4; i++) {
contentProviderUtils.insertMarker(new Marker(
null,
trackId,
"",
MOCK_DESC,
"",
getLastValidTrackPoint(trackId).position(),
null
));
}
assertEquals(Integer.valueOf(4), contentProviderUtils.getNextMarkerNumber(trackId));
}
@@ -568,11 +609,18 @@ public class CustomContentProviderUtilsTest {
Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Marker marker = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
marker.setDescription(TEST_DESC);
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
Marker.Id markerId = contentProviderUtils.insertMarker(
new Marker(
null,
trackId,
"",
TEST_DESC,
"",
getLastValidTrackPoint(trackId).position(),
null
));
assertEquals(TEST_DESC, contentProviderUtils.getMarker(markerId).getDescription());
assertEquals(TEST_DESC, contentProviderUtils.getMarker(markerId).description());
}
/**
@@ -584,16 +632,29 @@ public class CustomContentProviderUtilsTest {
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first.
Marker marker = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
marker.setDescription(TEST_DESC);
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
Marker.Id markerId = contentProviderUtils.insertMarker(
new Marker(
null,
trackId,
"",
TEST_DESC,
"",
getLastValidTrackPoint(trackId).position(),
null
));
// Update
marker = contentProviderUtils.getMarker(markerId);
marker.setDescription(TEST_DESC_NEW);
contentProviderUtils.updateMarker(context, marker);
contentProviderUtils.updateMarker(context, new Marker(
markerId,
trackId,
"",
TEST_DESC_NEW,
"",
getLastValidTrackPoint(trackId).position(),
null
));
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).getDescription());
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).description());
}
/**
@@ -607,10 +668,8 @@ public class CustomContentProviderUtilsTest {
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first.
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
marker.setDescription(TEST_DESC);
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
TrackPoint trackPoint = getLastValidTrackPoint(trackId);
Marker.Id markerId = contentProviderUtils.insertMarker(TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint, TEST_DESC));
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.id());
assertTrue(dir.exists());
@@ -618,14 +677,13 @@ public class CustomContentProviderUtilsTest {
assertEquals(1, dir.list().length);
// Update
marker = contentProviderUtils.getMarker(markerId);
MarkerBuilder marker = new MarkerBuilder(contentProviderUtils.getMarker(markerId));
marker.setName(TEST_NAME_NEW);
marker.setDescription(TEST_DESC_NEW);
contentProviderUtils.updateMarker(context, marker);
contentProviderUtils.updateMarker(context, marker.getMarker());
assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(markerId).getName());
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).getDescription());
assertTrue(marker.hasPhoto());
assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(markerId).name());
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).description());
assertTrue(dir.exists());
assertTrue(dir.isDirectory());
assertEquals(1, dir.list().length);
@@ -642,9 +700,8 @@ public class CustomContentProviderUtilsTest {
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first.
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
marker.setDescription(TEST_DESC);
TrackPoint trackPoint = getLastValidTrackPoint(trackId);
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint, TEST_DESC);
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.id());
@@ -653,15 +710,14 @@ public class CustomContentProviderUtilsTest {
assertEquals(1, dir.list().length);
// Update
marker = contentProviderUtils.getMarker(markerId);
marker.setName(TEST_NAME_NEW);
marker.setDescription(TEST_DESC_NEW);
marker.setPhotoUrl(null);
contentProviderUtils.updateMarker(context, marker);
MarkerBuilder markerBuilder = new MarkerBuilder(contentProviderUtils.getMarker(markerId));
markerBuilder.setName(TEST_NAME_NEW);
markerBuilder.setDescription(TEST_DESC_NEW);
markerBuilder.setPhotoUrl(null);
contentProviderUtils.updateMarker(context, markerBuilder.getMarker());
assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(markerId).getName());
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).getDescription());
assertFalse(marker.hasPhoto());
assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(markerId).name());
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).description());
assertFalse(dir.exists());
}
@@ -676,11 +732,9 @@ public class CustomContentProviderUtilsTest {
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert two markers with photos.
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
marker.setDescription(TEST_DESC);
Marker otherMarker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
otherMarker.setDescription(TEST_DESC);
TrackPoint trackPoint = getLastValidTrackPoint(trackId);
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint, TEST_DESC);
Marker otherMarker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint, TEST_DESC);
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
contentProviderUtils.insertMarker(otherMarker);
@@ -690,12 +744,14 @@ public class CustomContentProviderUtilsTest {
assertEquals(2, dir.list().length);
// Update one marker deleting photo.
marker = contentProviderUtils.getMarker(markerId);
marker.setPhotoUrl(null);
contentProviderUtils.updateMarker(context, marker);
MarkerBuilder markerBuilder = new MarkerBuilder(contentProviderUtils.getMarker(markerId));
markerBuilder.setPhotoUrl(null);
contentProviderUtils.updateMarker(context, markerBuilder.getMarker());
assertEquals(TEST_DESC, contentProviderUtils.getMarker(markerId).getDescription());
assertFalse(marker.hasPhoto());
// then
marker = contentProviderUtils.getMarker(markerId);
assertEquals(TEST_DESC, contentProviderUtils.getMarker(markerId).description());
assertNull(marker.photoUrl());
assertTrue(dir.exists());
assertTrue(dir.isDirectory());
assertEquals(1, dir.list().length);
@@ -726,50 +782,6 @@ public class CustomContentProviderUtilsTest {
}
}
/**
* Tests the method {@link ContentProviderUtils#createTrackPoint(Cursor)}.
*/
//TODO incomplete
@Test
public void testCreateTrackPoint() {
// given
when(cursorMock.getColumnIndex(TrackPointsColumns._ID)).thenReturn(1);
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE)).thenReturn(2);
when(cursorMock.isNull(2)).thenReturn(false);
int longitude = 11;
when(cursorMock.getInt(2)).thenReturn(longitude * 1000000);
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.LATITUDE)).thenReturn(3);
when(cursorMock.isNull(3)).thenReturn(false);
int latitude = 22;
when(cursorMock.getInt(3)).thenReturn(latitude * 1000000);
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.TIME)).thenReturn(4);
when(cursorMock.isNull(4)).thenReturn(false);
long time = System.currentTimeMillis();
when(cursorMock.getLong(4)).thenReturn(time);
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.SPEED)).thenReturn(5);
when(cursorMock.isNull(5)).thenReturn(false);
float speed = 2.2f;
when(cursorMock.getFloat(5)).thenReturn(speed);
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_HEARTRATE)).thenReturn(6);
when(cursorMock.isNull(6)).thenReturn(false);
when(cursorMock.getFloat(6)).thenReturn(75f);
// when
TrackPoint trackPoint = contentProviderUtils.createTrackPoint(cursorMock);
// then
assertEquals(latitude, trackPoint.getPosition().latitude(), 0.01);
assertEquals(longitude, trackPoint.getPosition().longitude(), 0.01);
assertEquals(time, trackPoint.getTime().toEpochMilli());
assertEquals(speed, trackPoint.getSpeed().toMPS(), 0.01);
assertEquals(HeartRate.of(75f), trackPoint.getHeartRate());
}
/**
* Tests the method
* {@link ContentProviderUtils#insertTrackPoint(TrackPoint, Track.Id)}.
@@ -778,7 +790,7 @@ public class CustomContentProviderUtilsTest {
public void testInsertTrackPoint() {
// Insert track, point at first.
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
contentProviderUtils.insertTrackPoint(TestDataUtil.createTrackPoint(22), trackId);
// when
@@ -792,35 +804,29 @@ public class CustomContentProviderUtilsTest {
public void testInsertAndLoadTrackPoint() {
// given
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
TrackPoint trackPoint = TestDataUtil.createTrackPoint(5);
trackPoint.setHeartRate(1F);
trackPoint.setCadence(2F);
trackPoint.setPower(3F);
TrackPoint trackPoint = new TrackPoint(
null,
TrackPoint.Type.TRACKPOINT,
TestDataUtil.createTrackPoint(5).position(),
null,
HeartRate.of(1),
null,
Cadence.of(2),
Power.of(3),
null
);
// when
contentProviderUtils.insertTrackPoint(trackPoint, trackId);
// then
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
assertTrue(trackPoints.get(10).hasHeartRate());
assertEquals(trackPoint.getHeartRate(), trackPoints.get(10).getHeartRate());
assertEquals(trackPoint.getCadence(), trackPoints.get(10).getCadence());
assertEquals(trackPoint.getPower(), trackPoints.get(10).getPower());
}
/**
* Tests the method {@link ContentProviderUtils#getLastValidTrackPoint(Track.Id)}.
*/
@Test
public void testGetLastValidTrackPoint() {
// Insert track, points at first.
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
TrackPoint lastTrackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
checkLocation(9, lastTrackPoint.getLocation());
assertNotNull(trackPoints.get(10).heartRate());
assertEquals(trackPoint.heartRate(), trackPoints.get(10).heartRate());
assertEquals(trackPoint.cadence(), trackPoints.get(10).cadence());
assertEquals(trackPoint.power(), trackPoints.get(10).power());
}
@Test
@@ -831,8 +837,8 @@ public class CustomContentProviderUtilsTest {
contentProviderUtils.insertTrack(track.first);
List<TrackPoint.Id> trackpointIds = track.second.stream()
.map(it -> ContentUris.parseId(contentProviderUtils.insertTrackPoint(it, track.first.getId())))
.map(TrackPoint.Id::new).collect(Collectors.toList());
.map(it -> ContentUris.parseId(contentProviderUtils.insertTrackPoint(it, track.first.id())))
.map(TrackPoint.Id::new).toList();
// when
try (Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds.get(8))) {
@@ -842,45 +848,36 @@ public class CustomContentProviderUtilsTest {
}
@Test
public void testGetTrackPointLocationIterator_asc() {
public void testGetTrackPointIterator_asc() {
// given
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Pair<Track, List<TrackPoint>> track = TestDataUtil.createTrack(trackId, 10);
contentProviderUtils.insertTrack(track.first);
List<TrackPoint.Id> trackpointIds = track.second.stream()
.map(it -> ContentUris.parseId(contentProviderUtils.insertTrackPoint(it, track.first.getId())))
.map(TrackPoint.Id::new).collect(Collectors.toList());
.map(it -> ContentUris.parseId(contentProviderUtils.insertTrackPoint(it, track.first.id())))
.map(TrackPoint.Id::new).toList();
TrackPoint.Id startTrackPointId = trackpointIds.get(0);
// when
TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, trackpointIds.get(0));
TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointIterator(trackId, trackpointIds.get(0));
// then
for (int i = 0; i < trackpointIds.size(); i++) {
assertTrue(trackPointIterator.hasNext());
TrackPoint trackPoint = trackPointIterator.next();
assertEquals(startTrackPointId.id() + i, trackPoint.getId().id());
assertEquals(startTrackPointId.id() + i, trackPoint.id().id());
checkLocation(i, trackPoint.getLocation());
Position position = trackPoint.position();
assertEquals(TestDataUtil.INITIAL_LATITUDE + i / 10000.0, position.latitude(), 0.01);
assertEquals(TestDataUtil.INITIAL_LONGITUDE - i / 10000.0, position.longitude(), 0.01);
assertEquals(i / 100.0, position.horizontalAccuracy().distance_m(), 0.01);
assertEquals(i * TestDataUtil.ALTITUDE_INTERVAL, position.altitude().toM(), 0.01);
}
assertFalse(trackPointIterator.hasNext());
}
/**
* Checks the value of a location.
*
* @param i the index of this location which created in the method {@link TestDataUtil#createTrack(Track.Id, int)}
* @param location the location to be checked
*/
private void checkLocation(int i, Location location) {
assertEquals(TestDataUtil.INITIAL_LATITUDE + (double) i / 10000.0, location.getLatitude(), 0.01);
assertEquals(TestDataUtil.INITIAL_LONGITUDE - (double) i / 10000.0, location.getLongitude(), 0.01);
assertEquals((float) i / 100.0f, location.getAccuracy(), 0.01);
assertEquals(i * TestDataUtil.ALTITUDE_INTERVAL, location.getAltitude(), 0.01);
}
@Test
public void testFormatIdListForUri() {
assertEquals("", ContentProviderUtils.formatIdListForUri());
@@ -892,11 +889,18 @@ public class CustomContentProviderUtilsTest {
public void testGetSensorStats_noSensorData() {
// given
List<TrackPoint> trackPointList = new ArrayList<>();
TrackPoint trackPoint = TestDataUtil.createTrackPoint(1);
trackPoint.setType(TrackPoint.Type.TRACKPOINT);
trackPoint.setPower(null);
trackPoint.setCadence(null);
trackPoint.setHeartRate(null);
TrackPoint trackPoint = new TrackPoint(
null,
TrackPoint.Type.TRACKPOINT,
TestDataUtil.createTrackPoint(1).position(),
null,
null,
null,
null,
null,
null
);
trackPointList.add(trackPoint);
Track.Id trackId = new Track.Id(System.currentTimeMillis());
Track track = TestDataUtil.createTrack(trackId);
@@ -1220,4 +1224,19 @@ public class CustomContentProviderUtilsTest {
public void testGetSensorStats_withSeveralRandomStartSegments() {
testGetSensorStats_randomData(5000, true);
}
private TrackPoint getLastValidTrackPoint(Track.Id trackId) {
try (TrackPointIterator trackPointsCursor = contentProviderUtils.getTrackPointIterator(trackId, null)) {
List<TrackPoint> trackpoints = new ArrayList<>();
while (trackPointsCursor.hasNext())
trackpoints.add(trackPointsCursor.next());
trackpoints = trackpoints.reversed();
return trackpoints.reversed()
.stream()
.filter(it -> List.of(TrackPoint.Type.TRACKPOINT, TrackPoint.Type.SEGMENT_START_AUTOMATIC).contains(it.type()))
.findFirst().orElseThrow();
}
}
}
@@ -48,10 +48,8 @@ public class CustomSQLiteOpenHelperTest {
public static Map<String, String> getSQL(SQLiteDatabase db, String type) {
HashMap<String, String> sqlMap = new HashMap<>();
try (Cursor cursor = db.query("sqlite_master", new String[]{"name", "SQL"}, "type=?", new String[]{type}, null, null, "name")) {
if (cursor != null) {
while (cursor.moveToNext()) {
sqlMap.put(cursor.getString(0), cursor.getString(1));
}
while (cursor.moveToNext()) {
sqlMap.put(cursor.getString(0), cursor.getString(1));
}
}
return sqlMap;
@@ -64,12 +62,10 @@ public class CustomSQLiteOpenHelperTest {
*/
private static boolean hasSqlCreate(SQLiteDatabase db, String sqlCreate) {
try (Cursor cursor = db.rawQuery("SELECT SQL FROM sqlite_master", null)) {
if (cursor != null) {
while (cursor.moveToNext()) {
String sql = cursor.getString(0);
if (sqlCreate.equals(sql)) {
return true;
}
while (cursor.moveToNext()) {
String sql = cursor.getString(0);
if (sqlCreate.equals(sql)) {
return true;
}
}
}
@@ -104,18 +100,18 @@ public class CustomSQLiteOpenHelperTest {
// Open database with SQL upgrade
Map<String, String> tableByUpgrade;
Map<String, String> indicesByUpgrade;
try (SQLiteDatabase dbUpgraded = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getReadableDatabase()) {
tableByUpgrade = getSQL(dbUpgraded, "table");
indicesByUpgrade = getSQL(dbUpgraded, "index");
try (CustomSQLiteOpenHelper dbUpgraded = new CustomSQLiteOpenHelper(context, DATABASE_NAME)) {
tableByUpgrade = getSQL(dbUpgraded.getReadableDatabase(), "table");
indicesByUpgrade = getSQL(dbUpgraded.getReadableDatabase(), "index");
}
context.deleteDatabase(DATABASE_NAME);
// Open database via creation script
Map<String, String> tablesByCreate;
Map<String, String> indicesByCreate;
try (SQLiteDatabase dbCreated = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getReadableDatabase()) {
tablesByCreate = getSQL(dbCreated, "table");
indicesByCreate = getSQL(dbCreated, "index");
try (CustomSQLiteOpenHelper dbCreated = new CustomSQLiteOpenHelper(context, DATABASE_NAME)) {
tablesByCreate = getSQL(dbCreated.getReadableDatabase(), "table");
indicesByCreate = getSQL(dbCreated.getReadableDatabase(), "index");
}
@@ -138,14 +134,16 @@ public class CustomSQLiteOpenHelperTest {
@Test
public void onDowngrade_ToVersion23() {
// Create most recent database schema
new CustomSQLiteOpenHelper(context, DATABASE_NAME).getReadableDatabase().close();
try(CustomSQLiteOpenHelper db = new CustomSQLiteOpenHelper(context, DATABASE_NAME)) {
db.getReadableDatabase();
}
// Downgrade schema to version 23 (base version)
Map<String, String> tablesByDowngrade;
Map<String, String> indicesByDowngrade;
try (SQLiteDatabase db = new CustomSQLiteOpenHelper(context, DATABASE_NAME, 23).getReadableDatabase()) {
tablesByDowngrade = getSQL(db, "table");
indicesByDowngrade = getSQL(db, "index");
try (CustomSQLiteOpenHelper db = new CustomSQLiteOpenHelper(context, DATABASE_NAME, 23)) {
tablesByDowngrade = getSQL(db.getReadableDatabase(), "table");
indicesByDowngrade = getSQL(db.getReadableDatabase(), "index");
}
// then - verify table structure
@@ -159,9 +157,9 @@ public class CustomSQLiteOpenHelperTest {
@Test
public void track_uuid_unique() {
try (SQLiteDatabase db = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getWritableDatabase()) {
db.execSQL("INSERT INTO tracks (uuid) VALUES (0x00)");
db.execSQL("INSERT INTO tracks (uuid) VALUES (0x00)");
try (CustomSQLiteOpenHelper db = new CustomSQLiteOpenHelper(context, DATABASE_NAME)) {
db.getWritableDatabase().execSQL("INSERT INTO tracks (uuid) VALUES (0x00)");
db.getWritableDatabase().execSQL("INSERT INTO tracks (uuid) VALUES (0x00)");
fail("unique constraint not enforced");
} catch (SQLiteConstraintException e) {
assertTrue(e.getMessage().contains("UNIQUE constraint failed: tracks.uuid"));
@@ -172,7 +170,8 @@ public class CustomSQLiteOpenHelperTest {
public void upgrade_data_to_30() {
// given: a track in version 29
createVersion23();
try (SQLiteDatabase db29 = new CustomSQLiteOpenHelper(context, DATABASE_NAME, 29).getWritableDatabase()) {
try (CustomSQLiteOpenHelper db = new CustomSQLiteOpenHelper(context, DATABASE_NAME, 29)) {
SQLiteDatabase db29 = db.getWritableDatabase();
db29.beginTransaction();
db29.execSQL("INSERT INTO tracks (_id) VALUES (1)");
@@ -205,7 +204,8 @@ public class CustomSQLiteOpenHelperTest {
}
// when / then
try (SQLiteDatabase db30 = new CustomSQLiteOpenHelper(context, DATABASE_NAME, 30).getWritableDatabase()) {
try (CustomSQLiteOpenHelper db = new CustomSQLiteOpenHelper(context, DATABASE_NAME, 30)) {
SQLiteDatabase db30 = db.getWritableDatabase();
{
// Track 1
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
@@ -265,7 +265,7 @@ public class CustomSQLiteOpenHelperTest {
private void createVersion23() {
// Manually create database schema with version 23 (base version)
SQLiteDatabase dbBase = new SQLiteOpenHelper(context, DATABASE_NAME, null, 23) {
try(SQLiteOpenHelper dbBase = new SQLiteOpenHelper(context, DATABASE_NAME, null, 23) {
@Override
public void onCreate(SQLiteDatabase db) {
}
@@ -273,12 +273,10 @@ public class CustomSQLiteOpenHelperTest {
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}.getWritableDatabase();
dbBase.execSQL(TRACKS_CREATE_TABLE_V23);
dbBase.execSQL(TRACKPOINTS_CREATE_TABLE_V23);
dbBase.execSQL(WAYPOINTS_CREATE_TABLE_V23);
dbBase.close();
}) {
dbBase.getWritableDatabase().execSQL(TRACKS_CREATE_TABLE_V23);
dbBase.getWritableDatabase().execSQL(TRACKPOINTS_CREATE_TABLE_V23);
dbBase.getWritableDatabase().execSQL(WAYPOINTS_CREATE_TABLE_V23);
}
}
}
@@ -9,6 +9,7 @@ import org.junit.runner.RunWith;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import de.dennisguse.opentracks.data.models.Track;
@@ -20,7 +21,7 @@ public class TrackSelectionTest extends TestCase {
TrackSelection filter = new TrackSelection();
// when
SelectionData selection = filter.buildSelection();
ContentProviderUtils.SelectionData selection = filter.buildSelection();
// Then
assertNull(selection.selection());
@@ -34,12 +35,11 @@ public class TrackSelectionTest extends TestCase {
TrackSelection filter = new TrackSelection().addTrackId(trackId);
// when
SelectionData selection = filter.buildSelection();
ContentProviderUtils.SelectionData selection = filter.buildSelection();
// Then
assertEquals(selection.selection(), "_id IN (?)");
assertEquals(selection.selectionArgs().length, 1);
assertEquals(selection.selectionArgs()[0], "1");
assertEquals("_id IN (?)", selection.selection());
assertEquals(List.of("1"), selection.selectionArgs());
}
@Test
@@ -54,28 +54,24 @@ public class TrackSelectionTest extends TestCase {
.addTrackId(trackId3);
// when
SelectionData selection = filter.buildSelection();
ContentProviderUtils.SelectionData selection = filter.buildSelection();
// Then
assertEquals(selection.selection(), "_id IN (?,?,?)");
assertEquals(selection.selectionArgs().length, 3);
assertEquals(selection.selectionArgs()[0], "1");
assertEquals(selection.selectionArgs()[1], "2");
assertEquals(selection.selectionArgs()[2], "3");
assertEquals("_id IN (?,?,?)", selection.selection());
assertEquals(List.of("1", "2", "3"), selection.selectionArgs());
}
@Test
public void testFilterBuildSelection_onlyOneCategory() {
public void testFilterBuildSelection_onlyOneactivity_type_localized() {
// given
TrackSelection filter = new TrackSelection().addActivityType("running");
// when
SelectionData selection = filter.buildSelection();
ContentProviderUtils.SelectionData selection = filter.buildSelection();
// Then
assertEquals(selection.selection(), "category IN (?)");
assertEquals(selection.selectionArgs().length, 1);
assertEquals(selection.selectionArgs()[0], "running");
assertEquals("activity_type_localized IN (?)", selection.selection());
assertEquals(List.of("running"), selection.selectionArgs());
}
@Test
@@ -88,15 +84,11 @@ public class TrackSelectionTest extends TestCase {
.addActivityType("trail walking");
// when
SelectionData selection = filter.buildSelection();
ContentProviderUtils.SelectionData selection = filter.buildSelection();
// Then
assertEquals(selection.selection(), "category IN (?,?,?,?)");
assertEquals(selection.selectionArgs().length, 4);
assertEquals(selection.selectionArgs()[0], "running");
assertEquals(selection.selectionArgs()[1], "road biking");
assertEquals(selection.selectionArgs()[2], "mountain biking");
assertEquals(selection.selectionArgs()[3], "trail walking");
assertEquals("activity_type_localized IN (?,?,?,?)", selection.selection());
assertEquals(List.of("running", "road biking", "mountain biking", "trail walking"), selection.selectionArgs());
}
@Test
@@ -109,9 +101,9 @@ public class TrackSelectionTest extends TestCase {
TrackSelection filterOk = new TrackSelection().addDateRange(instant, instant.plus(Duration.ofDays(1)));
// when
SelectionData selectionWrong1 = filterWrong1.buildSelection();
SelectionData selectionWrong2 = filterWrong2.buildSelection();
SelectionData selectionOk = filterOk.buildSelection();
ContentProviderUtils.SelectionData selectionWrong1 = filterWrong1.buildSelection();
ContentProviderUtils.SelectionData selectionWrong2 = filterWrong2.buildSelection();
ContentProviderUtils.SelectionData selectionOk = filterOk.buildSelection();
// Then
assertNull(selectionWrong1.selection());
@@ -120,10 +112,8 @@ public class TrackSelectionTest extends TestCase {
assertNull(selectionWrong2.selection());
assertNull(selectionWrong2.selectionArgs());
assertEquals(selectionOk.selection(), "starttime BETWEEN ? AND ?");
assertEquals(selectionOk.selectionArgs().length, 2);
assertEquals(selectionOk.selectionArgs()[0], Long.toString(instant.toEpochMilli()));
assertEquals(selectionOk.selectionArgs()[1], Long.toString(instant.toEpochMilli() + oneDay));
assertEquals("time_start BETWEEN ? AND ?", selectionOk.selection());
assertEquals(List.of(Long.toString(instant.toEpochMilli()), Long.toString(instant.toEpochMilli() + oneDay)) , selectionOk.selectionArgs());
}
@Test
@@ -140,16 +130,11 @@ public class TrackSelectionTest extends TestCase {
.addActivityType("road biking");
// when
SelectionData selection = filter.buildSelection();
ContentProviderUtils.SelectionData selection = filter.buildSelection();
// Then
assertEquals(selection.selection(), "_id IN (?,?,?) AND category IN (?,?)");
assertEquals(selection.selectionArgs().length, 5);
assertEquals(selection.selectionArgs()[0], "1");
assertEquals(selection.selectionArgs()[1], "2");
assertEquals(selection.selectionArgs()[2], "3");
assertEquals(selection.selectionArgs()[3], "running");
assertEquals(selection.selectionArgs()[4], "road biking");
assertEquals("_id IN (?,?,?) AND activity_type_localized IN (?,?)", selection.selection());
assertEquals(List.of("1", "2", "3", "running", "road biking"), selection.selectionArgs());
}
@Test
@@ -169,16 +154,11 @@ public class TrackSelectionTest extends TestCase {
.addDateRange(instant, instant.plusMillis(oneDay));
// when
SelectionData selection = filter.buildSelection();
ContentProviderUtils.SelectionData selection = filter.buildSelection();
// Then
assertEquals(selection.selection(), "_id IN (?,?,?) AND starttime BETWEEN ? AND ?");
assertEquals(selection.selectionArgs().length, 5);
assertEquals(selection.selectionArgs()[0], "1");
assertEquals(selection.selectionArgs()[1], "2");
assertEquals(selection.selectionArgs()[2], "3");
assertEquals(selection.selectionArgs()[3], Long.toString(instant.toEpochMilli()));
assertEquals(selection.selectionArgs()[4], Long.toString(instant.toEpochMilli() + oneDay));
assertEquals("_id IN (?,?,?) AND time_start BETWEEN ? AND ?", selection.selection());
assertEquals(List.of("1", "2", "3", Long.toString(instant.toEpochMilli()), Long.toString(instant.toEpochMilli() + oneDay)), selection.selectionArgs());
}
@Test
@@ -193,14 +173,10 @@ public class TrackSelectionTest extends TestCase {
.addDateRange(instant, instant.plusMillis(oneDay));
// when
SelectionData selection = filter.buildSelection();
ContentProviderUtils.SelectionData selection = filter.buildSelection();
// Then
assertEquals(selection.selection(), "category IN (?,?) AND starttime BETWEEN ? AND ?");
assertEquals(selection.selectionArgs().length, 4);
assertEquals(selection.selectionArgs()[0], "running");
assertEquals(selection.selectionArgs()[1], "road biking");
assertEquals(selection.selectionArgs()[2], Long.toString(instant.toEpochMilli()));
assertEquals(selection.selectionArgs()[3], Long.toString(instant.toEpochMilli() + oneDay));
assertEquals("activity_type_localized IN (?,?) AND time_start BETWEEN ? AND ?", selection.selection());
assertEquals(List.of("running", "road biking", Long.toString(instant.toEpochMilli()), Long.toString(instant.toEpochMilli() + oneDay)), selection.selectionArgs());
}
}
@@ -13,7 +13,7 @@ import de.dennisguse.opentracks.data.models.TrackPoint;
public class TrackPointTest {
@Test
public void distanceToPrevious() {
public void distanceToPrevious_gps() {
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.TRACKPOINT,
new Position(
Instant.ofEpochMilli(0),
@@ -38,20 +38,123 @@ public class TrackPointTest {
// without sensor distance
assertEquals(11.13, tp2.distanceToPrevious(tp1).toM(), 0.01);
}
@Test
public void distanceToPrevious_sensor_distance_first() {
TrackPoint tp1 = new TrackPoint(
null,
TrackPoint.Type.TRACKPOINT,
new Position(
Instant.ofEpochMilli(0),
0d,
0.0001,
null,
null,
null,
null,
null),
Distance.of(5),
null,
null,
null,
null,
null
);
TrackPoint tp2 = new TrackPoint(TrackPoint.Type.TRACKPOINT,
new Position(
Instant.ofEpochMilli(1),
0d,
0.0002,
null,
null,
null,
null,
null));
// tp1 has sensor distance
tp1.setSensorDistance(Distance.of(5));
tp1.setSensorDistance(null);
assertEquals(11.13, tp2.distanceToPrevious(tp1).toM(), 0.01);
}
@Test
public void distanceToPrevious_sensor_distance_second() {
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.TRACKPOINT,
new Position(
Instant.ofEpochMilli(0),
0d,
0.0001,
null,
null,
null,
null,
null));
TrackPoint tp2 = new TrackPoint(
null,
TrackPoint.Type.TRACKPOINT,
new Position(
Instant.ofEpochMilli(1),
0d,
0.0002,
null,
null,
null,
null,
null),
Distance.of(5),
null,
null,
null,
null,
null);
// tp2 has sensor distance
tp1.setSensorDistance(null);
tp2.setSensorDistance(Distance.of(5));
assertEquals(5, tp2.distanceToPrevious(tp1).toM(), 0.01);
}
@Test
public void distanceToPrevious_sensor_distance_both() {
TrackPoint tp1 = new TrackPoint(
null,
TrackPoint.Type.TRACKPOINT,
new Position(
Instant.ofEpochMilli(0),
0d,
0.0001,
null,
null,
null,
null,
null),
Distance.of(10),
null,
null,
null,
null,
null
);
TrackPoint tp2 = new TrackPoint(
null,
TrackPoint.Type.TRACKPOINT,
new Position(
Instant.ofEpochMilli(1),
0d,
0.0002,
null,
null,
null,
null,
null),
Distance.of(5),
null,
null,
null,
null,
null);
// tp1 and tp2 have sensor distance
tp1.setSensorDistance(Distance.of(10));
tp2.setSensorDistance(Distance.of(5));
assertEquals(5, tp2.distanceToPrevious(tp1).toM(), 0.01);
}
}
@@ -21,7 +21,7 @@ public class DistanceFormatterTest {
@Test
public void testFormatDistance_metric() {
DistanceFormatter formatter = DistanceFormatter.Builder()
.setDecimalCount(2)
.setDecimalPlaces(2)
.setUnit(UnitSystem.METRIC)
.build(context);
@@ -34,7 +34,7 @@ public class DistanceFormatterTest {
@Test
public void testFormatDistance_imperial() {
DistanceFormatter formatter = DistanceFormatter.Builder()
.setDecimalCount(2)
.setDecimalPlaces(2)
.setUnit(UnitSystem.IMPERIAL_FEET)
.build(context);
@@ -47,7 +47,7 @@ public class DistanceFormatterTest {
@Test
public void testFormatDistance_nautical() {
DistanceFormatter formatter = DistanceFormatter.Builder()
.setDecimalCount(2)
.setDecimalPlaces(2)
.setUnit(UnitSystem.NAUTICAL_IMPERIAL)
.build(context);
@@ -21,7 +21,6 @@ public class SpeedFormatterTest {
@Test
public void testGetSpeedParts_pace_metric() {
SpeedFormatter formatter = SpeedFormatter.Builder()
.setDecimalCount(2)
.setUnit(UnitSystem.METRIC)
.setReportSpeedOrPace(false)
.build(context);
@@ -29,73 +28,67 @@ public class SpeedFormatterTest {
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);
assertEquals("min/km", formatter.getSpeedParts(Speed.ZERO).second);
}
@Test
public void testGetSpeedParts_pace_imperial() {
SpeedFormatter formatter = SpeedFormatter.Builder()
.setDecimalCount(2)
.setUnit(UnitSystem.IMPERIAL_FEET)
.setReportSpeedOrPace(false)
.build(context);
assertEquals("8:02", formatter.getSpeedParts(Speed.of(3.34)).first);
assertEquals("min/mi", formatter.getSpeedParts(Speed.zero()).second);
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);
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);
assertEquals("km/h", formatter.getSpeedParts(Speed.ZERO).second);
}
@Test
public void testGetSpeedParts_speed_imperial() {
SpeedFormatter formatter = SpeedFormatter.Builder()
.setDecimalCount(2)
.setUnit(UnitSystem.IMPERIAL_FEET)
.setReportSpeedOrPace(true)
.build(context);
assertEquals("7.5", formatter.getSpeedParts(Speed.of(3.34)).first);
assertEquals("mph", formatter.getSpeedParts(Speed.zero()).second);
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);
assertEquals("kn", formatter.getSpeedParts(Speed.ZERO).second);
}
@Test
public void testFormatSpeed() {
SpeedFormatter formatter = SpeedFormatter.Builder()
.setDecimalCount(2)
.setUnit(UnitSystem.METRIC)
.setReportSpeedOrPace(false)
.build(context);
@@ -0,0 +1,65 @@
package de.dennisguse.opentracks.data.models;
import static org.junit.Assert.assertEquals;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.Duration;
import java.time.Instant;
@RunWith(AndroidJUnit4.class)
public class StatisticsTest {
@Test
public void testMerge() {
// given
Statistics subject = new Statistics(
Instant.ofEpochMilli(1000),
Instant.ofEpochMilli(2500),
Duration.ofMillis(1500),
Duration.ofMillis(700),
Distance.of(750.0),
Speed.of(60.0),
new AltitudeExtremities(1200, 1250),
new AltitudeGainLoss(50, 0),
HeartRate.of(100),
null
);
Statistics append = new Statistics(
Instant.ofEpochMilli(3000),
Instant.ofEpochMilli(4000),
Duration.ofMillis(1000),
Duration.ofMillis(600),
Distance.of(350.0),
Speed.of(30.0),
new AltitudeExtremities(2800.0, 3575.0),
new AltitudeGainLoss(850, 0),
HeartRate.of(200),
null
);
// when
Statistics result = subject.merge(append);
// then
assertEquals(
new Statistics(
Instant.ofEpochMilli(1000),
Instant.ofEpochMilli(4000),
Duration.ofMillis(2500),
Duration.ofMillis(1300),
Distance.of(1100),
Speed.of(60),
new AltitudeExtremities(1200, 3575),
new AltitudeGainLoss(900, 0),
HeartRate.of(150),
null
)
, result
);
}
}
@@ -4,11 +4,13 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import de.dennisguse.opentracks.data.models.ActivityType;
import de.dennisguse.opentracks.data.models.Statistics;
import de.dennisguse.opentracks.data.models.Track;
//TODO Merge with TrackFilenameGeneratorTest whenever Junit5 gets available.
@@ -33,12 +35,18 @@ public class TrackFilenameGenerator2Test {
@Test(expected = TrackFilenameGenerator.TemplateInvalidException.class)
public void testFilenameTemplate() {
// given
Track track = new Track();
track.setName("Best Track");
track.setUuid(UUID.fromString("f81d4fae-7dec-11d0-a765-00a0c91e6bf6"));
track.getTrackStatistics().setStartTime(Instant.parse("2020-02-02T02:02:02Z"));
Track track = new Track(
null,
UUID.fromString("f81d4fae-7dec-11d0-a765-00a0c91e6bf6"),
"Berlin",
"",
"",
ActivityType.CYCLING,
ZoneOffset.UTC,
Statistics.DEFAULT
);
// when
String filename = subject.format(track, TrackFileFormat.GPX);
subject.format(track, TrackFileFormat.GPX);
}
}
@@ -10,6 +10,7 @@ import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.Collection;
import java.util.TimeZone;
@@ -17,6 +18,7 @@ import java.util.UUID;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.TimezoneRule;
import de.dennisguse.opentracks.data.models.Statistics;
import de.dennisguse.opentracks.data.models.Track;
@RunWith(Parameterized.class)
@@ -47,10 +49,16 @@ public class TrackFilenameGeneratorTest {
@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"));
Track track = new Track(
null,
UUID.fromString("0000fee0-0000-1000-8000-00805f9b34fb"),
"Best Track",
"",
"",
null,
ZoneOffset.UTC,
new Statistics(Instant.parse("2020-02-02T02:02:02Z"), Instant.parse("2020-02-02T02:02:02Z"), null, null, null, null, null, null, null, null)
);
// when
String filename = subject.format(track, TrackFileFormat.GPX);
@@ -1,7 +1,6 @@
package de.dennisguse.opentracks.io.file.importer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -27,7 +26,6 @@ import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import java.io.BufferedReader;
import java.io.File;
@@ -52,6 +50,7 @@ import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.models.ActivityType;
import de.dennisguse.opentracks.data.models.Altitude;
import de.dennisguse.opentracks.data.models.AltitudeExtremities;
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance;
@@ -60,22 +59,25 @@ import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Statistics;
import de.dennisguse.opentracks.data.models.Temperature;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackBuilder;
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.sensors.BluetoothHandlerManagerCyclingPower;
import de.dennisguse.opentracks.sensors.SensorManager;
import de.dennisguse.opentracks.sensors.driver.CyclingPowerBluetooth;
import de.dennisguse.opentracks.sensors.sensorData.Aggregator;
import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer;
import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingCadence;
import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingDistanceSpeed;
import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingPower;
import de.dennisguse.opentracks.sensors.sensorData.AggregatorHeartRate;
import de.dennisguse.opentracks.sensors.sensorData.Raw;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
import de.dennisguse.opentracks.sensors.sensorData.AggregatorTemperature;
import de.dennisguse.opentracks.services.TrackRecordingService;
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.settings.PreferencesUtils;
/**
* Export a track to {@link TrackFileFormat} and verify that the import is identical.
@@ -87,7 +89,7 @@ import de.dennisguse.opentracks.stats.TrackStatistics;
public class ExportImportTest {
@Rule
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(50, TimeUnit.SECONDS); //TODO 5s
@Rule
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
@@ -96,13 +98,15 @@ public class ExportImportTest {
@Rule
public TimezoneRule timezoneRule = new TimezoneRule(TimeZone.getTimeZone("Europe/Berlin"));
private static final Context context = ApplicationProvider.getApplicationContext();
@BeforeClass
public static void preSetUp() {
// Prepare looper for Android's message queue
if (Looper.myLooper() == null) Looper.prepare();
}
private final Context context = ApplicationProvider.getApplicationContext();
PreferencesUtils.resetPreferences(context, true);
}
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
@@ -122,11 +126,13 @@ public class ExportImportTest {
private TrackImporter trackImporter;
@Before
public void fileSetup() throws IOException {
public void fileSetup() throws IOException, TimeoutException {
tmpFile = File.createTempFile("test", "test", context.getFilesDir());
tmpFileUri = Uri.fromFile(tmpFile);
trackImporter = new TrackImporter(context, contentProviderUtils, Distance.of(200), true);
setUp();
}
@After
@@ -150,24 +156,50 @@ public class ExportImportTest {
Distance sensorDistance = Distance.of(10); // recording distance interval
sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3.1234567, 14.0014567, 10, 13, 15, 1020.25, 1f);
contentProviderUtils.insertMarker(new Marker(trackId, service.getLastStoredTrackPointWithLocation(), "Marker 1", "Marker 1 desc", "Marker 1 category", null, null));
contentProviderUtils.insertMarker(
new Marker(
null,
trackId,
"Marker 1",
"Marker 1 desc",
"Marker 1 typeLocalized",
new Position(
Instant.parse("2020-02-02T02:02:03Z"),
3.1234567, 14.0014567,
Distance.of(10),
Altitude.WGS84.of(1020.25),
Distance.of(13),
null,
Speed.of(15)
),
null
));
// A sensor-only TrackPoint
trackPointCreator.setClock("2020-02-02T02:02:04Z");
mockSensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f, 1f);
mockSensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f, 1f, 27);
trackPointCreator.setClock("2020-02-02T02:02:14Z");
mockSensorData(trackPointCreator, 15f, null, 67f, 3f, 50f, null);
trackPointCreator.setClock("2020-02-02T02:02:14Z"); //ignored
mockSensorData(trackPointCreator, 15f, null, 67f, 3f, 50f, null, 28);
trackPointCreator.setClock("2020-02-02T02:02:15Z");
mockSensorData(trackPointCreator, null, null, 68f, 3f, 50f, null);
mockSensorData(trackPointCreator, null, null, 68f, 3f, 50f, null, 29);
trackPointCreator.setClock("2020-02-02T02:02:16Z");
mockSensorData(trackPointCreator, 5f, Distance.of(2), 69f, 3f, 50f, null); // Distance will be added to next TrackPoint
mockSensorData(trackPointCreator, 5f, Distance.of(2), 69f, 3f, 50f, null, 30); //Distance will be added to next TrackPoint
sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3.1234567, 14.0014567, 10, 13, 15, 1020.25, 0f);
contentProviderUtils.insertMarker(new Marker(trackId, service.getLastStoredTrackPointWithLocation(), "Marker 2", "Marker 2 desc", "Marker 2 category", null, null));
contentProviderUtils.insertMarker(
new Marker(
null,
trackId,
"Marker 2",
"Marker 2 desc",
"Marker 2 typeLocalized",
service.getLastStoredTrackPointWithLocation().position(),
null
));
trackPointCreator.setClock("2020-02-02T02:02:18Z");
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet(trackPointCreator);
trackPointCreator.getSensorManager().clear();
service.endCurrentTrack();
trackPointCreator.setClock("2020-02-02T02:03:20Z");
@@ -182,129 +214,170 @@ public class ExportImportTest {
sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3.1234567, 16.001, 10, 27, 15, 999.123, 0f);
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet(trackPointCreator);
trackPointCreator.getSensorManager().clear();
trackPointCreator.setClock("2020-02-02T02:04:00Z");
service.endCurrentTrack();
Track track = contentProviderUtils.getTrack(trackId);
track.setActivityType(TRACK_ACTIVITY_TYPE);
track.setActivityTypeLocalized(TRACK_ACTIVITY_TYPE_LOCALIZED);
track.setDescription(TRACK_DESCRIPTION);
contentProviderUtils.updateTrack(track);
TrackBuilder trackBuilder = new TrackBuilder(contentProviderUtils.getTrack(trackId));
trackBuilder.setActivityType(TRACK_ACTIVITY_TYPE);
trackBuilder.setActivityTypeLocalized(TRACK_ACTIVITY_TYPE_LOCALIZED);
trackBuilder.setDescription(TRACK_DESCRIPTION);
contentProviderUtils.updateTrack(trackBuilder.getTrack());
track = contentProviderUtils.getTrack(trackId);
trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
markers = contentProviderUtils.getMarkers(trackId);
markers = TestDataUtil.getMarkers(contentProviderUtils, trackId);
}
@LargeTest
@Test
public void track() throws TimeoutException {
setUp();
Track track = contentProviderUtils.getTrack(trackId);
TrackStatistics trackStatistics = track.getTrackStatistics();
Statistics trackStatistics = track.statistics();
assertEquals(ZoneOffset.of("+01:00"), track.getZoneOffset());
assertEquals(Instant.parse("2020-02-02T02:02:02Z"), trackStatistics.getStartTime());
assertEquals(Instant.parse("2020-02-02T02:04:00Z"), trackStatistics.getStopTime());
assertEquals(Duration.ofSeconds(56), trackStatistics.getTotalTime());
assertEquals(Duration.ofSeconds(26), trackStatistics.getMovingTime()); //TODO Likely too low
// Distance
assertEquals(222049.34375, trackStatistics.getTotalDistance().toM(), 0.01); //TODO Too low
// Speed
assertEquals(8540.359, trackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(3965.166, trackStatistics.getAverageSpeed().toMPS(), 0.01);
assertEquals(8540.359, trackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
// Altitude
assertEquals(999.122, trackStatistics.getMinAltitude(), 0.01);
assertEquals(1020.25, trackStatistics.getMaxAltitude(), 0.01);
assertEquals(2, trackStatistics.getTotalAltitudeGain(), 0.01);
assertEquals(2, trackStatistics.getTotalAltitudeLoss(), 0.01);
assertEquals(ZoneOffset.of("+01:00"), track.zoneOffset());
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
new TrackPointAssert().assertEquals(List.of(
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse("2020-02-02T02:02:02Z")),
new TrackPoint(TrackPoint.Type.TRACKPOINT,
new TrackPoint(
null,
TrackPoint.Type.TRACKPOINT,
new Position(
Instant.parse("2020-02-02T02:02:03Z"),
3.123456, 14.001456, Distance.of(10),
Altitude.WGS84.of(1020.25), null,
null,
Speed.of(15)))
.setAltitudeLoss(1f)
.setAltitudeGain(1f),
new TrackPoint(TrackPoint.Type.TRACKPOINT,
Instant.parse("2020-02-02T02:02:04Z"))
.setSensorDistance(Distance.of(10))
.setSpeed(Speed.of(15))
.setHeartRate(HeartRate.of(66))
.setCadence(3)
.setPower(50)
.setAltitudeLoss(1f)
.setAltitudeGain(1f),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:15Z"))
.setHeartRate(HeartRate.of(68))
.setCadence(3)
.setPower(50),
new TrackPoint(TrackPoint.Type.TRACKPOINT,
Speed.of(15)),
null,
null,
null,
null,
null,
new AltitudeGainLoss(1, 1)
),
new TrackPoint(
null,
TrackPoint.Type.TRACKPOINT,
new Position(
Instant.parse("2020-02-02T02:02:04Z"),
null, null, null,
null, null,
null,
Speed.of(15)),
Distance.of(10),
HeartRate.of(66),
Temperature.of(27),
Cadence.of(3),
Power.of(50),
new AltitudeGainLoss(1, 1)
),
new TrackPoint(
null,
TrackPoint.Type.TRACKPOINT,
Position.of(Instant.parse("2020-02-02T02:02:15Z")),
null,
HeartRate.of(68),
Temperature.of(29),
Cadence.of(3),
Power.of(50),
null
),
new TrackPoint(
null,
TrackPoint.Type.TRACKPOINT,
new Position(
Instant.parse("2020-02-02T02:02:17Z"),
3.123456, 14.001456, Distance.of(10),
Altitude.WGS84.of(1020.25), null,
null,
Speed.of(5)))
.setSensorDistance(Distance.of(2))
.setAltitudeLoss(0f)
.setAltitudeGain(0f)
.setHeartRate(HeartRate.of(69))
.setCadence(3)
.setPower(50),
Speed.of(5)),
Distance.of(2),
HeartRate.of(69),
Temperature.of(30),
Cadence.of(3),
Power.of(50),
new AltitudeGainLoss(0, 0)
),
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:02:18Z")),
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse("2020-02-02T02:03:20Z")),
new TrackPoint(TrackPoint.Type.TRACKPOINT,
new TrackPoint(
null,
TrackPoint.Type.TRACKPOINT,
new Position(
Instant.parse("2020-02-02T02:03:21Z"),
3.123456, 14.002456, Distance.of(10),
Altitude.WGS84.of(999.1229858398438), null,
null,
Speed.of(15)))
.setAltitudeLoss(0f)
.setAltitudeGain(0f),
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
Speed.of(15)),
null,
null,
null,
null,
null,
new AltitudeGainLoss(0, 0)
),
new TrackPoint(
null,
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
new Position(Instant.parse("2020-02-02T02:03:22Z"),
3.123456, 16d, Distance.of(10),
Altitude.WGS84.of(999.1229858398438), null,
null,
Speed.of(15)))
.setAltitudeLoss(0f)
.setAltitudeGain(0f),
new TrackPoint(TrackPoint.Type.IDLE, Instant.parse("2020-02-02T02:03:30Z"))
.setAltitudeLoss(0f)
.setAltitudeGain(0f),
new TrackPoint(TrackPoint.Type.TRACKPOINT,
Speed.of(15)),
null,
null,
null,
null,
null,
new AltitudeGainLoss(0, 0)
),
new TrackPoint(
null,
TrackPoint.Type.IDLE,
Position.of(Instant.parse("2020-02-02T02:03:30Z")),
null,
null,
null,
null,
null,
new AltitudeGainLoss(0, 0)
),
new TrackPoint(
null,
TrackPoint.Type.TRACKPOINT,
new Position(
Instant.parse("2020-02-02T02:03:50Z"),
3.123456, 16.001, Distance.of(10),
Altitude.WGS84.of(999.1229858398438), null,
null, Speed.of(15)))
.setAltitudeLoss(0f)
.setAltitudeGain(0f),
null, Speed.of(15)),
null,
null,
null,
null,
null,
new AltitudeGainLoss(0, 0)),
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:04:00Z"))
), actual);
assertEquals(new Statistics(
Instant.parse("2020-02-02T02:02:02Z"),
Instant.parse("2020-02-02T02:04:00Z"),
Duration.ofSeconds(56),
Duration.ofSeconds(26), //TODO Likely too low
Distance.of(222049.34375), //TODO Too low
Speed.of(22203.734375),
new AltitudeExtremities(999.1229858398438, 1020.25),
new AltitudeGainLoss(2, 2),
null,
null
),
trackStatistics);
}
//TODO Does not test marker images
@LargeTest
@Test
public void kmz_with_trackdetail_and_sensordata() throws TimeoutException, IOException {
setUp();
public void kmz_with_trackdetail_and_sensordata() throws IOException {
// given
Track track = contentProviderUtils.getTrack(trackId);
@@ -323,41 +396,32 @@ public class ExportImportTest {
// 1. track
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
assertNotNull(importedTrack);
assertEquals(track.getActivityType(), importedTrack.getActivityType());
assertEquals(track.getActivityTypeLocalized(), importedTrack.getActivityTypeLocalized());
assertEquals(track.getDescription(), importedTrack.getDescription());
assertEquals(track.getName(), importedTrack.getName());
assertEquals(track.activityType(), importedTrack.activityType());
assertEquals(track.activityTypeLocalized(), importedTrack.activityTypeLocalized());
assertEquals(track.description(), importedTrack.description());
assertEquals(track.name(), importedTrack.name());
// 2. trackpoints
TrackPointAssert a = new TrackPointAssert();
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
a.assertEquals(trackPoints, actual);
new TrackPointAssert().assertEquals(trackPoints, actual);
// 3. trackstatistics
TrackStatistics importedTrackStatistics = importedTrack.getTrackStatistics();
// Time
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
assertEquals(Instant.parse("2020-02-02T02:02:02Z"), importedTrackStatistics.getStartTime());
assertEquals(Instant.parse("2020-02-02T02:04:00Z"), importedTrackStatistics.getStopTime());
assertEquals(Duration.ofSeconds(56), importedTrackStatistics.getTotalTime());
assertEquals(Duration.ofSeconds(26), importedTrackStatistics.getMovingTime());
// Distance
assertEquals(222049.421, importedTrackStatistics.getTotalDistance().toM(), 0.01);
// Speed
assertEquals(8540.362, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(3965.168, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
assertEquals(8540.362, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
// Altitude
assertEquals(999.122, importedTrackStatistics.getMinAltitude(), 0.01);
assertEquals(1020.25, importedTrackStatistics.getMaxAltitude(), 0.01);
assertEquals(2, importedTrackStatistics.getTotalAltitudeGain(), 0.01);
assertEquals(2, importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
Statistics importedTrackStatistics = importedTrack.statistics();
assertEquals(track.zoneOffset(), importedTrack.zoneOffset());
assertEquals(new Statistics(
Instant.parse("2020-02-02T02:02:02Z"),
Instant.parse("2020-02-02T02:04:00Z"),
Duration.ofSeconds(56),
Duration.ofSeconds(26), //TODO Likely too low
Distance.of(222049.421875), //TODO Too low
Speed.of(22203.7421875),
new AltitudeExtremities(999.1229858398438, 1020.25),
new AltitudeGainLoss(2, 2),
null,
null
),
importedTrackStatistics);
// 4. markers
assertMarkers();
@@ -365,9 +429,7 @@ public class ExportImportTest {
@LargeTest
@Test(expected = ImportAlreadyExistsException.class)
public void kml_with_trackdetail_and_sensordata_duplicate_trackUUID() throws TimeoutException, IOException {
setUp();
public void kml_with_trackdetail_and_sensordata_duplicate_trackUUID() throws IOException {
// given
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putBoolean(context.getString(R.string.import_prevent_reimport_key), true);
@@ -392,8 +454,6 @@ public class ExportImportTest {
@LargeTest
@Test
public void gpx() throws TimeoutException, IOException {
setUp();
// given
Track track = contentProviderUtils.getTrack(trackId);
@@ -413,10 +473,10 @@ public class ExportImportTest {
// 1. track
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
assertNotNull(importedTrack);
assertEquals(track.getActivityType(), importedTrack.getActivityType());
assertEquals(track.getActivityTypeLocalized(), importedTrack.getActivityTypeLocalized());
assertEquals(track.getDescription(), importedTrack.getDescription());
assertEquals(track.getName(), importedTrack.getName());
assertEquals(track.activityType(), importedTrack.activityType());
assertEquals(track.activityTypeLocalized(), importedTrack.activityTypeLocalized());
assertEquals(track.description(), importedTrack.description());
assertEquals(track.name(), importedTrack.name());
// 2. trackpoints
// The GPX exporter does not support exporting TrackPoints without lat/lng.
@@ -426,82 +486,105 @@ public class ExportImportTest {
.setDelta(0.05); // speed is not fully
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
a.assertEquals(List.of(
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
new TrackPoint(
null,
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
new Position(
Instant.parse("2020-02-02T02:02:03Z"),
3.123456, 14.001456d, Distance.of(10),
Altitude.WGS84.of(1020.2), null,
null,
Speed.of(15)))
.setAltitudeLoss(1f)
.setAltitudeGain(1f),
new TrackPoint(TrackPoint.Type.TRACKPOINT,
Speed.of(15)),
null,
null,
null,
null,
null,
new AltitudeGainLoss(1, 1)
),
new TrackPoint(
null,
TrackPoint.Type.TRACKPOINT,
new Position(
Instant.parse("2020-02-02T02:02:17Z"),
3.123456, 14.001456, Distance.of(10),
Altitude.WGS84.of(1020.2), null,
null,
Speed.of(5)))
.setAltitudeLoss(1f)
.setAltitudeGain(1f)
.setSensorDistance(Distance.of(12))
.setHeartRate(69)
.setPower(50f)
.setCadence(3f),
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
Speed.of(5)),
Distance.of(12),
HeartRate.of(69),
Temperature.of(30),
Cadence.of(3),
Power.of(50),
new AltitudeGainLoss(1, 1)
),
new TrackPoint(
null,
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
new Position(
Instant.parse("2020-02-02T02:03:21Z"),
3.123456, 14.002456, Distance.of(10),
Altitude.WGS84.of(999.0999755859375), null,
null,
Speed.of(15)))
.setAltitudeLoss(0f)
.setAltitudeGain(0f),
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
Speed.of(15)),
null,
null,
null,
null,
null,
new AltitudeGainLoss(0, 0)
),
new TrackPoint(
null,
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
new Position(
Instant.parse("2020-02-02T02:03:22Z"),
3.123456, 16d, Distance.of(10),
Altitude.WGS84.of(999.0999755859375), null,
null,
Speed.of(15)))
.setAltitudeLoss(0f)
.setAltitudeGain(0f),
new TrackPoint(TrackPoint.Type.TRACKPOINT,
Speed.of(15)),
null,
null,
null,
null,
null,
new AltitudeGainLoss(0, 0)
),
new TrackPoint(
null,
TrackPoint.Type.TRACKPOINT,
new Position(
Instant.parse("2020-02-02T02:03:50Z"),
3.123456, 16.001, Distance.of(10),
Altitude.WGS84.of(999.0999755859375), null,
null,
Speed.of(10)))
.setAltitudeLoss(0f)
.setAltitudeGain(0f)
.setSpeed(Speed.of(15))
Speed.of(15)),
null,
null,
null,
null,
null,
new AltitudeGainLoss(0, 0)
)
), actual);
// 3. trackstatistics
TrackStatistics importedTrackStatistics = importedTrack.getTrackStatistics();
assertEquals(track.zoneOffset(), importedTrack.zoneOffset());
// Time
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
assertEquals(Instant.parse("2020-02-02T02:02:03Z"), importedTrackStatistics.getStartTime());
assertEquals(Instant.parse("2020-02-02T02:03:50Z"), importedTrackStatistics.getStopTime());
assertEquals(Duration.ofSeconds(107), importedTrackStatistics.getTotalTime());
assertEquals(Duration.ofSeconds(107), importedTrackStatistics.getMovingTime());
// Distance
assertEquals(222271.734, importedTrackStatistics.getTotalDistance().toM(), 0.01);
// Speed
assertEquals(2077.305, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(2077.305, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
assertEquals(2077.305, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
// Altitude
assertEquals(999.099, importedTrackStatistics.getMinAltitude(), 0.01);
assertEquals(1020.2, importedTrackStatistics.getMaxAltitude(), 0.01);
assertEquals(2, importedTrackStatistics.getTotalAltitudeGain(), 0.01);
assertEquals(2, importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
Statistics importedTrackStatistics = importedTrack.statistics();
assertEquals(new Statistics(
Instant.parse("2020-02-02T02:02:03Z"),
Instant.parse("2020-02-02T02:03:50Z"),
Duration.ofSeconds(107),
Duration.ofSeconds(107),
Distance.of(222271.734375),
Speed.of(2077.305908203125),
new AltitudeExtremities(999.0999755859375, 1020.2000122070312),
new AltitudeGainLoss(2, 2),
null,
null
),
importedTrackStatistics);
// 4. markers
assertMarkers();
@@ -509,9 +592,7 @@ public class ExportImportTest {
@LargeTest
@Test(expected = ImportAlreadyExistsException.class)
public void gpx_duplicate_trackUUID() throws TimeoutException, IOException {
setUp();
public void gpx_duplicate_trackUUID() throws IOException {
// given
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putBoolean(context.getString(R.string.import_prevent_reimport_key), true);
@@ -537,9 +618,7 @@ public class ExportImportTest {
@LargeTest
@Test
public void csv_export_only() throws TimeoutException, IOException {
setUp();
public void csv_export_only() throws IOException {
// given
Track track = contentProviderUtils.getTrack(trackId);
@@ -566,35 +645,34 @@ public class ExportImportTest {
}
private void assertMarkers() {
assertEquals(markers.size(), contentProviderUtils.getMarkers(importTrackId).size());
List<Marker> importedMarkers = TestDataUtil.getMarkers(contentProviderUtils, importTrackId);
assertEquals(markers.size(), importedMarkers.size());
List<Marker> importedMarkers = contentProviderUtils.getMarkers(importTrackId);
for (int i = 0; i < markers.size(); i++) {
Marker marker = markers.get(i);
Marker importMarker = importedMarkers.get(i);
assertEquals(marker.getCategory(), importMarker.getCategory());
assertEquals(marker.getDescription(), importMarker.getDescription());
// assertEquals(marker.getIcon(), importMarker.getIcon()); // TODO for KML
assertEquals(marker.getName(), importMarker.getName());
assertFalse(importMarker.hasPhoto());
assertEquals(marker.typeLocalized(), importMarker.typeLocalized());
assertEquals(marker.description(), importMarker.description());
assertEquals(marker.name(), importMarker.name());
assertNull(importMarker.photoUrl());
assertEquals(marker.getPosition().latitude(), importMarker.getPosition().latitude(), 0.001);
assertEquals(marker.getPosition().longitude(), importMarker.getPosition().longitude(), 0.001);
assertEquals(marker.getPosition().altitude().toM(), importMarker.getPosition().altitude().toM(), 0.1);
assertEquals(marker.position().latitude(), importMarker.position().latitude(), 0.001);
assertEquals(marker.position().longitude(), importMarker.position().longitude(), 0.001);
assertEquals(marker.position().altitude().toM(), importMarker.position().altitude().toM(), 0.1);
}
}
private void mockSensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power, Float altitudeGain) {
SensorDataSet sensorDataSet = trackPointCreator.getSensorManager().sensorDataSet;
private static void mockSensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power, Float altitudeGain, float temperature) {
SensorManager sensorManager = trackPointCreator.getSensorManager();
AggregatorCyclingPower cyclingPower = new AggregatorCyclingPower("", "");
cyclingPower.add(new Raw<>(trackPointCreator.createNow(), new BluetoothHandlerManagerCyclingPower.Data(Power.of(power), null)));
sensorDataSet.add(cyclingPower);
cyclingPower.add(trackPointCreator.getNow(), new CyclingPowerBluetooth.Data(Power.of(power), null));
sensorManager.setAggregator(cyclingPower);
AggregatorHeartRate avgHeartRate = new AggregatorHeartRate("", "");
avgHeartRate.add(new Raw<>(trackPointCreator.createNow(), HeartRate.of(heartRate)));
sensorDataSet.add(avgHeartRate);
avgHeartRate.add(trackPointCreator.getNow(), HeartRate.of(heartRate));
sensorManager.setAggregator(avgHeartRate);
AggregatorCyclingCadence cyclingCadence = new AggregatorCyclingCadence("", "") {
@NonNull
@@ -608,33 +686,65 @@ public class ExportImportTest {
return true;
}
};
sensorDataSet.add(cyclingCadence);
sensorManager.setAggregator(cyclingCadence);
if (distance != null && speed != null) {
AggregatorCyclingDistanceSpeed distanceSpeed = Mockito.mock(AggregatorCyclingDistanceSpeed.class);
Mockito.when(distanceSpeed.hasReceivedData()).thenReturn(true);
Mockito.when(distanceSpeed.getAggregatedValue(Mockito.any())).thenReturn(new AggregatorCyclingDistanceSpeed.Data(null, distance, Speed.of(speed)));
sensorDataSet.add(distanceSpeed);
AggregatorCyclingDistanceSpeed aggregatorCyclingDistanceSpeed = new AggregatorCyclingDistanceSpeed("", "") {
@NonNull
@Override
public Data getAggregatedValue(Instant now) {
return new AggregatorCyclingDistanceSpeed.Data(null, distance, Speed.of(speed));
}
@Override
public boolean hasReceivedData() {
return true;
}
};
sensorManager.setAggregator(aggregatorCyclingDistanceSpeed);
} else {
sensorDataSet.add(new AggregatorCyclingDistanceSpeed("", ""));
sensorManager.setAggregator(new AggregatorCyclingDistanceSpeed("", ""));
}
mockAltitudeChange(trackPointCreator, altitudeGain);
trackPointCreator.onChange(sensorDataSet);
sensorManager.setAggregator(new AggregatorTemperature("", "'") {
@NonNull
@Override
public Temperature getAggregatedValue(Instant now) {
return Temperature.of(temperature);
}
@Override
public boolean hasReceivedData() {
return true;
}
});
trackPointCreator.onChange();
}
private void mockAltitudeChange(TrackPointCreator trackPointCreator, Float altitudeGain) {
SensorDataSet sensorDataSet = trackPointCreator.getSensorManager().sensorDataSet;
private static void mockAltitudeChange(TrackPointCreator trackPointCreator, Float altitudeGain) {
SensorManager sensorManager = trackPointCreator.getSensorManager();
if (altitudeGain != null) {
AggregatorBarometer barometer = Mockito.mock(AggregatorBarometer.class);
Mockito.when(barometer.hasReceivedData()).thenReturn(true);
Mockito.when(barometer.getAggregatedValue(Mockito.any())).thenReturn(new AltitudeGainLoss(altitudeGain, altitudeGain));
sensorDataSet.add(barometer);
} else {
sensorDataSet.add(new AggregatorBarometer("test", null));
if (altitudeGain == null) {
sensorManager.getAltitudeChangeHandler().setAggregator(new AggregatorBarometer("test", null));
return;
}
AggregatorBarometer aggregatorBarometer = new AggregatorBarometer("", "") {
@NonNull
@Override
public AltitudeGainLoss getAggregatedValue(Instant now) {
return new AltitudeGainLoss(altitudeGain, altitudeGain);
}
@Override
public boolean hasReceivedData() {
return true;
}
};
sensorManager.getAltitudeChangeHandler().setAggregator(aggregatorBarometer);
}
private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, double altitude, Float altitudeGain) {
@@ -649,6 +759,6 @@ public class ExportImportTest {
mockAltitudeChange(trackPointCreator, altitudeGain);
trackPointCreator.setClock(time);
trackPointCreator.getSensorManager().getGpsManager().onLocationChanged(location);
trackPointCreator.getSensorManager().getGpsHandler().onDataReceived(location);
}
}
@@ -27,14 +27,18 @@ import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.models.ActivityType;
import de.dennisguse.opentracks.data.models.Altitude;
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Statistics;
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.
@@ -61,73 +65,6 @@ public class GPXTrackImporterTest {
}
}
/**
* Check that data that contains pause (lat=100, lng=0) and resume (lat=200, lng=0) locations are restored to a segment break.
*/
@LargeTest
@Test
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.test.R.raw.legacy_gpx_pause_resume);
// when
// 1. import
importTrackId = importer.importFile(inputStream).get(0);
// then
// 2. track
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
assertNotNull(importedTrack);
assertEquals(ActivityType.UNKNOWN, importedTrack.getActivityType());
assertEquals("the category", importedTrack.getActivityTypeLocalized());
assertEquals("the description", importedTrack.getDescription());
assertEquals("2021-01-07 22:51", importedTrack.getName());
assertEquals(ActivityType.UNKNOWN, importedTrack.getActivityType());
//TODO Check trackstatistics
// 3. trackpoints
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
assertEquals(6, importedTrackPoints.size());
// first segment
TrackPointAssert a = new TrackPointAssert();
a.assertEquals(List.of(
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
new Position(
Instant.parse("2021-01-07T21:51:59.179Z"),
3d, 14d, null,
Altitude.WGS84.of(10), null,
null,
null)),
new TrackPoint(TrackPoint.Type.TRACKPOINT,
new Position(
Instant.parse("2021-01-07T21:52:00.653Z"),
3d, 14.001, null,
Altitude.WGS84.of(10), null,
null,
Speed.of(75.4192))),
new TrackPoint(TrackPoint.Type.TRACKPOINT,
new Position(
Instant.parse("2021-01-07T21:52:01.010Z"),
3d, 14.002, null,
Altitude.WGS84.of(10), null,
null, Speed.of(311.3948))),
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2021-01-07T21:52:02.658Z")),
// created resume trackpoint with time of next valid trackpoint
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse("2021-01-07T21:52:03.873Z")),
new TrackPoint(TrackPoint.Type.TRACKPOINT,
new Position(
Instant.parse("2021-01-07T21:52:04.103Z"),
3d, 14.003, null,
Altitude.WGS84.of(10), null,
null,
null))
), importedTrackPoints);
}
@LargeTest
@Test
public void gpx_without_speed() throws IOException {
@@ -143,15 +80,15 @@ public class GPXTrackImporterTest {
// 2. track
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
assertNotNull(importedTrack);
assertEquals("", importedTrack.getActivityTypeLocalized());
assertEquals("", importedTrack.getDescription());
assertEquals("20210907_213924.gpx", importedTrack.getName());
assertEquals(ActivityType.UNKNOWN, importedTrack.getActivityType());
assertEquals("", importedTrack.activityTypeLocalized());
assertEquals("", importedTrack.description());
assertEquals("20210907_213924.gpx", importedTrack.name());
assertEquals(ActivityType.UNKNOWN, importedTrack.activityType());
// 3. trackstatistics
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
assertEquals(0.75, trackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(Duration.ofSeconds(101), trackStatistics.getMovingTime());
Statistics trackStatistics = importedTrack.statistics();
assertEquals(0.75, trackStatistics.maxSpeed().toMPS(), 0.01);
assertEquals(Duration.ofSeconds(101), trackStatistics.movingDuration());
// 4. trackpoints
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
@@ -198,15 +135,15 @@ public class GPXTrackImporterTest {
// 2. track
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
assertNotNull(importedTrack);
assertEquals("", importedTrack.getActivityTypeLocalized());
assertEquals("", importedTrack.getDescription());
assertEquals("20210907_213924.gpx", importedTrack.getName());
assertEquals(ActivityType.UNKNOWN, importedTrack.getActivityType());
assertEquals("", importedTrack.activityTypeLocalized());
assertEquals("", importedTrack.description());
assertEquals("20210907_213924.gpx", importedTrack.name());
assertEquals(ActivityType.UNKNOWN, importedTrack.activityType());
// 3. trackstatistics
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
assertEquals(5.0, trackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(Duration.ofSeconds(101), trackStatistics.getMovingTime());
Statistics trackStatistics = importedTrack.statistics();
assertEquals(5.0, trackStatistics.maxSpeed().toMPS(), 0.01);
assertEquals(Duration.ofSeconds(101), trackStatistics.movingDuration());
// 4. trackpoints
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
@@ -260,56 +197,86 @@ public class GPXTrackImporterTest {
TrackPointAssert a = new TrackPointAssert()
.setDelta(0.05); // speed is not fully
a.assertEquals(List.of(
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
new TrackPoint(
null,
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
new Position(
Instant.parse("2020-02-02T02:02:03Z"),
3d, 14d, Distance.of(10),
Altitude.WGS84.of(10), null,
null,
Speed.of(15)))
.setAltitudeLoss(1f)
.setAltitudeGain(1f),
new TrackPoint(TrackPoint.Type.TRACKPOINT,
Speed.of(15)),
null,
null,
null,
null,
null,
new AltitudeGainLoss(1, 1)
),
new TrackPoint(
null,
TrackPoint.Type.TRACKPOINT,
new Position(
Instant.parse("2020-02-02T02:02:17Z"),
3d, 14.001, Distance.of(10),
Altitude.WGS84.of(10), null,
null,
Speed.of(5)))
.setAltitudeLoss(1f)
.setAltitudeGain(1f)
.setSensorDistance(Distance.of(12))
.setHeartRate(69)
.setPower(50f)
.setCadence(3f),
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
Speed.of(5)),
Distance.of(12),
HeartRate.of(69),
null,
Cadence.of(3),
Power.of(50),
new AltitudeGainLoss(1, 1)
),
new TrackPoint(
null,
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
new Position(
Instant.parse("2020-02-02T02:03:21Z"),
3d, 14.002, Distance.of(10),
Altitude.WGS84.of(10), null,
null,
Speed.of(15)))
.setAltitudeLoss(0f)
.setAltitudeGain(0f),
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
Speed.of(15)),
null,
null,
null,
null,
null,
new AltitudeGainLoss(0, 0)
),
new TrackPoint(
null,
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
new Position(
Instant.parse("2020-02-02T02:03:22Z"),
3d, 16d, Distance.of(10),
Altitude.WGS84.of(10), null,
null,
Speed.of(15)))
.setAltitudeLoss(0f)
.setAltitudeGain(0f),
new TrackPoint(TrackPoint.Type.TRACKPOINT,
Speed.of(15)),
null,
null,
null,
null,
null,
new AltitudeGainLoss(0, 0)
),
new TrackPoint(
null,
TrackPoint.Type.TRACKPOINT,
new Position(
Instant.parse("2020-02-02T02:03:50Z"),
3d, 16.001, Distance.of(10),
Altitude.WGS84.of(10), null,
null,
Speed.of(10)))
.setAltitudeLoss(0f)
.setAltitudeGain(0f)
.setSpeed(Speed.of(15))
Speed.of(15)),
null,
null,
null,
null,
null,
new AltitudeGainLoss(0, 0)
)
), actual);
}
@@ -319,19 +286,19 @@ public class GPXTrackImporterTest {
// 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);
try (InputStream inputStreamExpected = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx_timezone)) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// when
// 1. import
importTrackId = importer.importFile(inputStream).get(0);
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
// when
// 1. import
importTrackId = importer.importFile(inputStream).get(0);
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context, contentProviderUtils);
trackExporter.writeTrack(List.of(importedTrack), outputStream);
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context, contentProviderUtils);
trackExporter.writeTrack(List.of(importedTrack), outputStream);
// then
assertEquals(new String(inputStreamExpected.readAllBytes(), StandardCharsets.UTF_8), outputStream.toString());
// then
assertEquals(new String(inputStreamExpected.readAllBytes(), StandardCharsets.UTF_8), outputStream.toString());
}
}
}
@@ -73,13 +73,13 @@ public class KMLTrackImporterTest {
// 1. track
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
assertNotNull(importedTrack);
assertEquals("", importedTrack.getActivityTypeLocalized());
assertEquals("", importedTrack.getDescription());
assertEquals("", importedTrack.getName());
assertEquals(ActivityType.UNKNOWN, importedTrack.getActivityType());
assertEquals("", importedTrack.activityTypeLocalized());
assertEquals("", importedTrack.description());
assertEquals("", importedTrack.name());
assertEquals(ActivityType.UNKNOWN, importedTrack.activityType());
// 2. markers
assertEquals(0, contentProviderUtils.getMarkers(importTrackId).size());
assertEquals(0, TestDataUtil.getMarkers(contentProviderUtils, importTrackId).size());
// 3. trackpoints
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
@@ -129,13 +129,13 @@ public class KMLTrackImporterTest {
// 1. track
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
assertNotNull(importedTrack);
assertEquals("", importedTrack.getActivityTypeLocalized());
assertEquals("", importedTrack.getDescription());
assertEquals("", importedTrack.getName());
assertEquals(ActivityType.UNKNOWN, importedTrack.getActivityType());
assertEquals("", importedTrack.activityTypeLocalized());
assertEquals("", importedTrack.description());
assertEquals("", importedTrack.name());
assertEquals(ActivityType.UNKNOWN, importedTrack.activityType());
// 2. markers
assertEquals(0, contentProviderUtils.getMarkers(importTrackId).size());
assertEquals(0, TestDataUtil.getMarkers(contentProviderUtils, importTrackId).size());
// 3. trackpoints
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
@@ -170,13 +170,14 @@ public class KMLTrackImporterTest {
// 1. track
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
assertNotNull(importedTrack);
assertEquals("unknown", importedTrack.getActivityTypeLocalized());
assertEquals("Test Track", importedTrack.getDescription());
assertEquals("2020-11-28 18:06", importedTrack.getName());
assertEquals(ActivityType.UNKNOWN, importedTrack.getActivityType());
assertEquals("unknown", importedTrack.activityTypeLocalized());
assertEquals("Test Track", importedTrack.description());
assertEquals("2020-11-28 18:06", importedTrack.name());
assertEquals(ActivityType.UNKNOWN, importedTrack.activityType());
// 2. markers
assertEquals(1, contentProviderUtils.getMarkers(importTrackId).size());
//TODO Check actual content (Marker should be record for this)
assertEquals(1, TestDataUtil.getMarkers(contentProviderUtils, importTrackId).size());
// 3. trackpoints
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
@@ -189,24 +190,21 @@ public class KMLTrackImporterTest {
12.340097, 1.234156, null,
Altitude.WGS84.of(469.286376953125), null,
null,
Speed.of(0.539)))
.setAltitudeGain(0f),
Speed.of(0.539))),
new TrackPoint(TrackPoint.Type.TRACKPOINT,
new Position(
Instant.parse("2020-11-28T17:06:25.448Z"),
12.340036, 1.23415, null,
Altitude.WGS84.of(439.1626281738281), null,
null,
Speed.of(0.1577)))
.setAltitudeGain(0f),
Speed.of(0.1577))),
new TrackPoint(TrackPoint.Type.TRACKPOINT,
new Position(
Instant.parse("2020-11-28T17:06:47.888Z"),
12.340057, 1.23405, null,
Altitude.WGS84.of(421.8070983886719), null,
null,
Speed.of(0)))
.setAltitudeGain(0f),
Speed.ZERO)),
// created resume trackpoint with time of next valid trackpoint
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
new Position(
@@ -214,24 +212,21 @@ public class KMLTrackImporterTest {
12.340057, 1.23405, null,
Altitude.WGS84.of(419.93902587890625), null,
null,
Speed.of(0)))
.setAltitudeGain(0f),
Speed.ZERO)),
new TrackPoint(TrackPoint.Type.TRACKPOINT,
new Position(
Instant.parse("2020-11-28T17:06:56.905Z"),
12.340057, 1.23405, null,
Altitude.WGS84.of(419.9036560058594), null,
null,
Speed.of(0)))
.setAltitudeGain(0f),
Speed.ZERO)),
new TrackPoint(TrackPoint.Type.TRACKPOINT,
new Position(
Instant.parse("2020-11-28T17:07:20.870Z"),
12.340082, 1.234046, null,
Altitude.WGS84.of(417.99432373046875), null,
null,
Speed.of(0)))
.setAltitudeGain(0f)
Speed.ZERO))
), importedTrackPoints);
}
@@ -16,61 +16,47 @@ public class TrackPointAssert {
public void assertEquals(TrackPoint expected, TrackPoint actual) {
Assert.assertEquals("time", expected.getTime(), actual.getTime());
Assert.assertEquals("type", expected.getType(), actual.getType());
Assert.assertEquals("type", expected.type(), actual.type());
Assert.assertEquals("has location,", expected.hasLocation(), actual.hasLocation());
if (expected.hasLocation()) {
Assert.assertEquals("latitude", expected.getPosition().latitude(), actual.getPosition().latitude(), 0.001);
Assert.assertEquals("longitude", expected.getPosition().longitude(), actual.getPosition().longitude(), 0.001);
Assert.assertEquals("has location,", expected.position().hasLocation(), actual.position().hasLocation());
if (expected.position().hasLocation()) {
Assert.assertEquals("latitude", expected.position().latitude(), actual.position().latitude(), 0.001);
Assert.assertEquals("longitude", expected.position().longitude(), actual.position().longitude(), 0.001);
}
Assert.assertEquals("has altitude", expected.hasAltitude(), actual.hasAltitude());
if (expected.hasAltitude()) {
Assert.assertEquals("altitude", expected.getAltitude().toM(), actual.getAltitude().toM(), delta);
Assert.assertEquals("has altitude", expected.position().hasAltitude(), actual.position().hasAltitude());
if (expected.position().hasAltitude()) {
Assert.assertEquals("altitude", expected.position().altitude().toM(), actual.position().altitude().toM(), delta);
}
Assert.assertEquals("has altitudeGain", expected.hasAltitudeGain(), actual.hasAltitudeGain());
if (expected.hasAltitudeGain()) {
Assert.assertEquals("altitudeGain", expected.getAltitudeGain(), actual.getAltitudeGain(), delta);
}
Assert.assertEquals("has altitudeLoss", expected.hasAltitudeLoss(), actual.hasAltitudeLoss());
if (expected.hasAltitudeLoss()) {
Assert.assertEquals("altitudeLoss", expected.getAltitudeLoss(), actual.getAltitudeLoss(), delta);
Assert.assertEquals("altitudeGainLoss", expected.altitudeGainLoss(), actual.altitudeGainLoss());
Assert.assertEquals("has speed", expected.position().hasSpeed(), actual.position().hasSpeed());
if (expected.position().hasSpeed()) {
Assert.assertEquals("speed", expected.position().speed().toMPS(), actual.position().speed().toMPS(), delta);
}
Assert.assertEquals("has speed", expected.hasSpeed(), actual.hasSpeed());
if (expected.hasSpeed()) {
Assert.assertEquals("speed", expected.getSpeed().toMPS(), actual.getSpeed().toMPS(), delta);
Assert.assertEquals("has horizontalAccuracy", expected.position().hasHorizontalAccuracy(), actual.position().hasHorizontalAccuracy());
if (expected.position().hasHorizontalAccuracy()) {
Assert.assertEquals("horizontalAccuracy", expected.position().horizontalAccuracy().toM(), actual.position().horizontalAccuracy().toM(), delta);
}
Assert.assertEquals("has verticalAccuracy", expected.position().hasVerticalAccuracy(), actual.position().hasVerticalAccuracy());
if (expected.position().hasVerticalAccuracy()) {
Assert.assertEquals("verticalAccuracy", expected.position().verticalAccuracy().toM(), actual.position().verticalAccuracy().toM(), delta);
}
Assert.assertEquals("has horizontalAccuracy", expected.hasHorizontalAccuracy(), actual.hasHorizontalAccuracy());
if (expected.hasHorizontalAccuracy()) {
Assert.assertEquals("horizontalAccuracy", expected.getHorizontalAccuracy().toM(), actual.getHorizontalAccuracy().toM(), delta);
}
Assert.assertEquals("has verticalAccuracy", expected.hasVerticalAccuracy(), actual.hasVerticalAccuracy());
if (expected.hasVerticalAccuracy()) {
Assert.assertEquals("verticalAccuracy", expected.getVerticalAccuracy().toM(), actual.getVerticalAccuracy().toM(), delta);
Assert.assertEquals("has sensorDistance", expected.sensorDistance() != null, actual.sensorDistance() != null);
if (expected.sensorDistance() != null) {
Assert.assertEquals("sensorDistance", expected.sensorDistance().toM(), actual.sensorDistance().toM(), delta);
}
Assert.assertEquals("has sensorDistance", expected.hasSensorDistance(), actual.hasSensorDistance());
if (expected.hasSensorDistance()) {
Assert.assertEquals("sensorDistance", expected.getSensorDistance().toM(), actual.getSensorDistance().toM(), delta);
}
Assert.assertEquals("heartRate", expected.heartRate(), actual.heartRate());
Assert.assertEquals("has heartrate", expected.hasHeartRate(), actual.hasHeartRate());
if (expected.hasHeartRate()) {
Assert.assertEquals("heartrate", expected.getHeartRate(), actual.getHeartRate());
}
Assert.assertEquals("temperature", expected.temperature(), actual.temperature());
Assert.assertEquals("has power", expected.hasPower(), actual.hasPower());
if (expected.hasPower()) {
Assert.assertEquals("power", expected.getPower(), actual.getPower());
}
Assert.assertEquals("power", expected.power(), actual.power());
Assert.assertEquals("has cadence", expected.hasCadence(), actual.hasCadence());
if (expected.hasCadence()) {
Assert.assertEquals("cadence", expected.getCadence(), actual.getCadence());
}
Assert.assertEquals("cadence", expected.cadence(), actual.cadence());
}
public void assertEquals(List<TrackPoint> expected, List<TrackPoint> actual) {
@@ -0,0 +1,224 @@
package de.dennisguse.opentracks.publicapi;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
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.data.ContentProviderUtils;
@RunWith(AndroidJUnit4.class)
public class DataProviderTest {
private final ContentResolver resolver = ApplicationProvider.getApplicationContext().getContentResolver();
@Test
public void supportsVersion2_tracks() {
// given
Uri uri = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/dashboard/tracks/1");
String[] expectedTracksColumns = new String[]{
"_id",
"name",
"description",
"category",
"activity_type_localized",
"starttime",
"stoptime",
"totaltime",
"movingtime",
"totaldistance",
"maxspeed",
"avgspeed",
"avgmovingspeed",
"minelevation",
"maxelevation",
"elevationgain",
"elevationloss"
};
// when
try (Cursor cursor = resolver.query(
uri,
expectedTracksColumns,
null,
null,
null
)) {
// then
assertEquals("vnd.android.cursor.dir/vnd.de.dennisguse.track", resolver.getType(uri));
assertNotNull(cursor);
assertArrayEquals(expectedTracksColumns, cursor.getColumnNames());
}
}
@Test
public void supportsVersion2_trackpoints() {
// given
Uri uri = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/dashboard/trackpoints/1");
String[] expectedTrackpointsColumns = {
"_id",
"trackid",
"latitude",
"longitude",
"time",
"type",
"speed"
};
// when
try (Cursor cursor = resolver.query(
uri,
expectedTrackpointsColumns,
null,
null,
null
)) {
// then
assertEquals("vnd.android.cursor.dir/vnd.de.dennisguse.trackpoint", resolver.getType(uri));
assertNotNull(cursor);
assertArrayEquals(expectedTrackpointsColumns, cursor.getColumnNames());
}
}
@Test
public void supportsVersion2_markers() {
// given
Uri uri = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/dashboard/markers/1");
String[] expectedMarkersColumns = {
"_id",
"name",
"description",
"category",
"trackid",
"longitude",
"latitude",
"photoUrl",
"icon"
};
// when
try (Cursor cursor = resolver.query(
uri,
expectedMarkersColumns,
null,
null,
null
)) {
// then
assertEquals("vnd.android.cursor.dir/vnd.de.dennisguse.waypoint", resolver.getType(uri));
assertNotNull(cursor);
assertArrayEquals(expectedMarkersColumns, cursor.getColumnNames());
}
}
@Test
public void supportsVersion3_tracks() {
// given
Uri uri = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/dashboard/tracks/1");
String[] expectedTracksColumns = {
"_id",
"name",
"description",
"activity_type",
"activity_type_localized",
"time_start",
"time_stop",
"duration_total",
"duration_moving",
"distance",
"speed_max",
"altitude_min",
"altitude_max",
"altitude_gain",
"altitude_loss"
};
// when
try (Cursor cursor = resolver.query(
uri,
expectedTracksColumns,
null,
null,
null
)) {
// then
assertEquals("vnd.android.cursor.dir/vnd.de.dennisguse.track", resolver.getType(uri));
assertNotNull(cursor);
assertArrayEquals(expectedTracksColumns, cursor.getColumnNames());
}
}
@Test
public void supportsVersion3_trackpoints() {
//given
Uri uri = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/dashboard/trackpoints/1");
String[] expectedTrackpointsColumns = {
"_id",
"trackid",
"latitude",
"longitude",
"time",
"type",
"speed"
};
// when
try (Cursor cursor = resolver.query(
uri,
expectedTrackpointsColumns,
null,
null,
null
)) {
//then
assertEquals("vnd.android.cursor.dir/vnd.de.dennisguse.trackpoint", resolver.getType(uri));
assertNotNull(cursor);
assertArrayEquals(expectedTrackpointsColumns, cursor.getColumnNames());
}
}
@Test
public void supportsVersion3_markers() {
// given
Uri uri = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/dashboard/markers/1");
String[] expectedMarkersColumns = {
"_id",
"name",
"description",
"marker_type_localized",
"trackid",
"latitude",
"longitude",
"photoUrl"
};
// when
try (Cursor cursor = resolver.query(
uri,
expectedMarkersColumns,
null,
null,
null
)) {
// then
assertEquals("vnd.android.cursor.dir/vnd.de.dennisguse.waypoint", resolver.getType(uri));
assertNotNull(cursor);
assertArrayEquals(expectedMarkersColumns, cursor.getColumnNames());
}
}
}
@@ -49,9 +49,9 @@ public class PublicApiTest {
List<Track> tracks = new ContentProviderUtils(context).getTracks();
Assert.assertEquals(1, tracks.size());
Track track = tracks.get(0);
Assert.assertEquals("trackName", track.getName());
Assert.assertEquals("activityTypeLocalized", track.getActivityTypeLocalized());
Assert.assertEquals("airplane", track.getActivityType().getId());
Assert.assertEquals("trackName", track.name());
Assert.assertEquals("activityTypeLocalized", track.activityTypeLocalized());
Assert.assertEquals("airplane", track.activityType().getId());
}
@Test

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