forked from upstream-mirrors/OpenTracks
Compare commits
322 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4f040ac79f | |||
| dcad42bf4d | |||
| 9225d90e45 | |||
| 32dc6fef82 | |||
| 96fb65b6b0 | |||
| 936bbf9264 | |||
| 241a52ee7c | |||
| c66e285bf5 | |||
| 8c1ddc077d | |||
| 61c18af639 | |||
| b83df3ac4b | |||
| 53f1c4b7f3 | |||
| 6290bdbab0 | |||
| bd0d85cd37 | |||
| ba08417236 | |||
| 63281a3ca8 | |||
| fae0cf8454 | |||
| efddb31391 | |||
| 3a8e547e10 | |||
| fe09d18197 | |||
| 93bcf8d494 | |||
| 819f454bd8 | |||
| 7d58014b15 | |||
| 9dac95569c | |||
| 96c98ad275 | |||
| 7dcc0ec8bc | |||
| 09ecc03be5 | |||
| d7a5ffb938 | |||
| 0605dbb0ab | |||
| bf03de6be7 | |||
| d65ce361f7 | |||
| 3f600f6be7 | |||
| 9aab3c216c | |||
| 7935185812 | |||
| 4c1b1be96c | |||
| 5762830655 | |||
| 6d61206782 | |||
| b9dedef292 | |||
| f3caecd049 | |||
| e16780277f | |||
| 13484685a3 | |||
| 7b745d9837 | |||
| 52742064f6 | |||
| 2edf5582aa | |||
| 808eb92bf2 | |||
| c0b84c3d72 | |||
| e275d3e5c0 | |||
| b4ebad2c3c | |||
| 1ab76e1317 | |||
| 3d1104dd0b | |||
| 2df45d31de | |||
| 5436981e0d | |||
| fd2b1bec7d | |||
| bb1eaa5b56 | |||
| dae8eaa97a | |||
| 565b1ce94d | |||
| 543d5e18bf | |||
| 01368df6b1 | |||
| c666d55211 | |||
| 8cc4fd1d4f | |||
| c241a35861 | |||
| 674d231b7a | |||
| bebee59eb2 | |||
| 24f843b0db | |||
| f0259d13dd | |||
| 9dff965a52 | |||
| b4e6d10fbe | |||
| 1a9d88277f | |||
| 4ea956ca0e | |||
| 8060c2e1e3 | |||
| 8c96b65e59 | |||
| a407e94d3c | |||
| 310c790324 | |||
| 12b3d31f5c | |||
| d43dca61c1 | |||
| 20c14a03b2 | |||
| a6601f4eea | |||
| 220c7b1443 | |||
| 33bb63976d | |||
| 8db8f68d69 | |||
| 99b417217e | |||
| ac80386b8a | |||
| b96441a964 | |||
| 207f9563df | |||
| 7b84bf448b | |||
| 41a62fd412 | |||
| 1087dbf387 | |||
| e16d1d53c6 | |||
| 00c9ba6595 | |||
| 7c1995c67f | |||
| d2e0ae8b11 | |||
| f0aa49537d | |||
| 14ace294f3 | |||
| b53ef455a6 | |||
| 3dc6c6607d | |||
| dd91e05fb6 | |||
| 19fe5275b9 | |||
| 1aeb9ca332 | |||
| 520c240a19 | |||
| 48aaca13d0 | |||
| 88a31f4f39 | |||
| 5ce9e6606b | |||
| 4ea70b15e7 | |||
| d3cf2957cc | |||
| dbac595754 | |||
| d70e7b8788 | |||
| f6835852b6 | |||
| d5864726f6 | |||
| 3cc3271726 | |||
| 8aef9c8d28 | |||
| bcca00d110 | |||
| aa929161b4 | |||
| 70cb9cbf51 | |||
| fef3b97661 | |||
| 945f366195 | |||
| 8c0a851d87 | |||
| 3eb89d7899 | |||
| f12ff4a625 | |||
| 71dac06ce8 | |||
| 07090cd72c | |||
| 26a20fe32d | |||
| d74ef256d7 | |||
| 0392ba5128 | |||
| 3d12ebdaa3 | |||
| b3cb5e8091 | |||
| 8f9cbcc077 | |||
| 14b6746dff | |||
| a5eca8fa64 | |||
| 498ee20e2a | |||
| 2207865a70 | |||
| 8073f12947 | |||
| cacfcbd4d6 | |||
| 08673de331 | |||
| aecd1504f3 | |||
| f3bf42083a | |||
| 8a9cd40bd5 | |||
| fee1d1a871 | |||
| 0787ae6686 | |||
| 747ef9fc56 | |||
| 5911d60818 | |||
| b89dbf5819 | |||
| 99e2c1f38d | |||
| faa1ffe53b | |||
| 8461122df0 | |||
| 2ca7aa8f1d | |||
| 9db2aff631 | |||
| b7cedab1fb | |||
| 0a193c8b5c | |||
| 7049fab204 | |||
| 80fa49bcf0 | |||
| a1eb5fb15f | |||
| 9160b9b104 | |||
| 85a73c3c85 | |||
| 8389f9150a | |||
| 3075f4010f | |||
| 82e305e6a7 | |||
| 6351a335c0 | |||
| 6538daedef | |||
| 82fe859c84 | |||
| f567b03be5 | |||
| 7dc4e8dbc8 | |||
| 2a272a887e | |||
| 868509e00b | |||
| 477bdd5214 | |||
| 286cec8d77 | |||
| 58d4ee7d7d | |||
| 0e93a304f0 | |||
| 0adc4c7986 | |||
| a93424629d | |||
| da29766fd0 | |||
| dc7afbd07f | |||
| 67b174feac | |||
| b6b81b5d3d | |||
| 7d804b603d | |||
| 8696a15ce7 | |||
| 989fe39849 | |||
| ed1e432691 | |||
| 4f612e32d6 | |||
| e5d1e421a4 | |||
| 08ab8cc61d | |||
| c3b30815c8 | |||
| 5a4919efd2 | |||
| bb549fdc93 | |||
| 73e59cbba2 | |||
| 27727b6dcf | |||
| 06d2957407 | |||
| 8b08af6bcf | |||
| 78dad95ef6 | |||
| be9d56721e | |||
| e5785dd3ac | |||
| 11d912d14d | |||
| 807ac3fc9f | |||
| 45874d3749 | |||
| 594f69320a | |||
| d4927c9444 | |||
| 00dbd056d6 | |||
| 8f19ad8f66 | |||
| 2fdad54a7c | |||
| 5a5f040ed5 | |||
| 0ada9a2463 | |||
| 719bb981ec | |||
| 3c95d8744d | |||
| 63d1c9ee5e | |||
| 1ccd2e3c0a | |||
| cdbddabc1b | |||
| 9f6ca83f27 | |||
| f2ee99a477 | |||
| 7009ede095 | |||
| 2b530a1c53 | |||
| 33673f5921 | |||
| a7398d43e3 | |||
| 59b016a4a3 | |||
| 78aa5d51c6 | |||
| 95f584b554 | |||
| cf759d526d | |||
| 4c44ea12a4 | |||
| 9d09865907 | |||
| 5667d2d83e | |||
| 386f017fb7 | |||
| 87594e822d | |||
| ee832c7bd2 | |||
| 30042d5bc3 | |||
| 73cbb7a29c | |||
| c100fdde4f | |||
| 04e1496084 | |||
| 21d1852505 | |||
| 1502b0cfa7 | |||
| e7d6f65508 | |||
| 0e69a444a5 | |||
| 3b62999fde | |||
| 97431233ac | |||
| d49675afc8 | |||
| c1f7ab36a8 | |||
| eb14cc7bc6 | |||
| a47b9164af | |||
| 502e3ecb21 | |||
| 71903f174a | |||
| ab6606a295 | |||
| c4e2023992 | |||
| cb10bdda3f | |||
| f913472aae | |||
| af4826c578 | |||
| 63172ec66f | |||
| 3d6c339509 | |||
| 49f86c4eb9 | |||
| 7664bd248d | |||
| 73615aa0d6 | |||
| 81bb6699d8 | |||
| 93c67e3728 | |||
| 993816a34a | |||
| 3c0999c3a0 | |||
| 4528e7148f | |||
| 74042f5d85 | |||
| 92f6fbe04a | |||
| 5fa50250de | |||
| 8ee9daf7af | |||
| a54c8c1d25 | |||
| 8137ec8de2 | |||
| ae2a97576b | |||
| 3e830a086e | |||
| 618a1df3ea | |||
| 90cfc67c59 | |||
| cecc586868 | |||
| f956e7fab3 | |||
| 128b95cd83 | |||
| 895a5c2b2a | |||
| 91ce5cf8d7 | |||
| a283464331 | |||
| 1c3a2bd2e4 | |||
| 0b326a77c2 | |||
| d68008941e | |||
| b822d05a78 | |||
| 74bfed768c | |||
| ebfcce6d8d | |||
| d97d8f85e4 | |||
| 37c2c5029e | |||
| 105ff89fd8 | |||
| 0ac033106c | |||
| 21ec9fc568 | |||
| 2e3a7a31d4 | |||
| ecd7405a8c | |||
| 28abe360cb | |||
| 9cf88c00b0 | |||
| 8bbde4aacd | |||
| 00351136f2 | |||
| 30b85bd215 | |||
| 14b705b43e | |||
| 31fc0adabc | |||
| 1714da5d3b | |||
| 6ae47b5e01 | |||
| df8e4c2e2e | |||
| 6f9fa3719a | |||
| b9014c7219 | |||
| d44ab44620 | |||
| bcffe05744 | |||
| ed60734868 | |||
| e314ff8f43 | |||
| 1108684d06 | |||
| 6c0033c107 | |||
| 8a3e50ef9b | |||
| 05b572253e | |||
| f755a086cd | |||
| b0605323da | |||
| 585c2234eb | |||
| c5d7ea4245 | |||
| 281cd8a861 | |||
| 36400c8ad0 | |||
| fb00f697c5 | |||
| f529726218 | |||
| 059f3d8f43 | |||
| cae716925f | |||
| d29ee6da3d | |||
| 033b5211ea | |||
| 5f4f5baba4 | |||
| 4421046556 | |||
| 30482d5a34 | |||
| f12094c953 | |||
| c5147e7664 | |||
| 03b66bc6a2 | |||
| 7b40f2c949 | |||
| 785a922d03 | |||
| 5d2414977b |
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Data not recorded
|
||||
about: Somehow OpenTracks did not record GPS data
|
||||
title: ''
|
||||
labels: potential bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Please describe the problem and how to reproduce it:**
|
||||
|
||||
...
|
||||
|
||||
**Checklist**
|
||||
|
||||
- [ ] Consulted [https://dontkillmyapp.com/]
|
||||
- [ ] Power management: battery optimization is disabled for OpenTracks
|
||||
|
||||
**Technical information**
|
||||
|
||||
- Device: [e.g. Nexus 7 (2013)]
|
||||
- OS: [e.g. LineageOS 15.1]
|
||||
- OpenTracks version: [e.g. v3.2.4]
|
||||
- OpenTracks commit id (only for nightly builds): [e.g. 4b333bb]
|
||||
@@ -1,9 +1,10 @@
|
||||
#https://github.com/marketplace/actions/android-emulator-runner
|
||||
name: Build nightly
|
||||
on: [ push ]
|
||||
name: Build APK
|
||||
on:
|
||||
push
|
||||
|
||||
jobs:
|
||||
nightly-build:
|
||||
apk-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
@@ -12,9 +13,9 @@ jobs:
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
|
||||
- name: Assemble nighlty APK
|
||||
- name: Assemble APK
|
||||
shell: bash
|
||||
run: |
|
||||
echo ${NIGHTLY_STORE_FILE} | base64 -d > KEY_NIGHTLY.jks
|
||||
@@ -28,7 +29,7 @@ jobs:
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: OpenTracks-nightly.apk
|
||||
name: OpenTracks-APK.apk
|
||||
path: ./build/outputs/apk/nightly/*.apk
|
||||
retention-days: 7
|
||||
|
||||
@@ -2,7 +2,7 @@ name: Linting
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request_target:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
analyse:
|
||||
@@ -16,6 +16,6 @@ jobs:
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
|
||||
- run: ./gradlew lint
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
# We have flaky test results due to performance limitations in Github Actions.
|
||||
# Happens too often nowadays; See https://github.com/OpenTracksApp/OpenTracks/issues/1409
|
||||
|
||||
#https://github.com/marketplace/actions/android-emulator-runner
|
||||
name: Test
|
||||
on:
|
||||
push:
|
||||
pull_request_target:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
@@ -23,7 +26,7 @@ jobs:
|
||||
java-version: '11'
|
||||
|
||||
- name: run tests
|
||||
uses: reactivecircus/android-emulator-runner@v2.24.0
|
||||
uses: reactivecircus/android-emulator-runner@v2.26.0
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
target: ${{ matrix.target }}
|
||||
@@ -2,14 +2,13 @@
|
||||
|
||||
_OpenTracks_ is a sport tracking application that completely respects your privacy.
|
||||
|
||||
[](https://github.com/humanetech-community/awesome-humane-tech)
|
||||
[](https://delightful.club/delightful-humane-design/#health)
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Free (F-Droid)</th>
|
||||
<th>Free (Nightly for F-Droid)</th>
|
||||
<th>Donations</th>
|
||||
<th>Translations</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
@@ -27,47 +26,28 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
||||
<img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg" height="45" align="middle">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://weblate.bubu1.eu/engage/OpenTracks/">
|
||||
<img src="https://weblate.bubu1.eu/widgets/OpenTracks/-/opentracks/svg-badge.svg" alt="Translation status" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<img alt="OpenTracks version published on F-Droid" src="https://img.shields.io/f-droid/v/de.dennisguse.opentracks.svg" align="middle" >
|
||||
</td>
|
||||
<td></td>
|
||||
<td align="center">
|
||||
<a href="bitcoin:bc1qckpls3lpjlcpyxzm66hcy65hnesas9zgacy6yc">
|
||||
<img alt="bitcoin:bc1qckpls3lpjlcpyxzm66hcy65hnesas9zgacy6yc" src="promotion/donations-bitcoin.png" height="90" align="middle">
|
||||
</a>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td align="center">
|
||||
<a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">
|
||||
<img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" height="60" align="middle">
|
||||
</a>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td align="center">
|
||||
<a href="https://www.amazon.de/dp/B09K2KNQ7X/">
|
||||
<img alt="Get it on Amazon App Store" src="https://images-na.ssl-images-amazon.com/images/G/01/mobile-apps/devportal2/res/images/amazon-appstore-badge-english-white.png" height="40" align="middle">
|
||||
</a>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
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" />
|
||||
</a>
|
||||
|
||||
## Screenshots
|
||||
|
||||
<div>
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot1.png">
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot2.png">
|
||||
@@ -86,19 +66,19 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
||||
* __Voice announcements__
|
||||
* __Photos and Markers:__ mark interesting locations while tracking
|
||||
* __Export:__
|
||||
* export tracks either as KMZ (incl. photos), KML, or GPX
|
||||
* export automatically after each recording (e.g., to sync via [Nextcloud](https://nextcloud.com/))
|
||||
* avoid duplication: each exported file contain a random unique identifier (i.e., `opentracks:trackid`)
|
||||
* export tracks either as [KMZ 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html) (incl. photos), [KML 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html), or [GPX 1.1](https://www.topografix.com/GPX/1/1/)
|
||||
* export automatically after each recording (e.g., to sync via [Nextcloud](https://nextcloud.com/))
|
||||
* avoid duplication: each exported file contain a random unique identifier (i.e., `opentracks:trackid`)
|
||||
* __Altitude:__
|
||||
* gain/loss via barometric sensor (if present)
|
||||
* shown in EGM2008 (above mean sea level); exported as WGS84
|
||||
* gain/loss via barometric sensor (if present)
|
||||
* shown in EGM2008 (above mean sea level); exported as WGS84
|
||||
* __Bluetooth LE sensors:__
|
||||
* heart rate
|
||||
* cycling: speed and distance
|
||||
* cycling: cadence
|
||||
* cycling: power meter
|
||||
* running: speed and cadence
|
||||
* support for BLE sensor training only (i.e., without GPS) for indoor training
|
||||
* heart rate
|
||||
* cycling: speed and distance
|
||||
* cycling: cadence
|
||||
* cycling: power meter
|
||||
* running: speed and cadence
|
||||
* support for BLE sensor training only (i.e., without GPS) for indoor training
|
||||
|
||||
An overview of tested sensors: [README_TESTED_SENSORS.md](README_TESTED_SENSORS.md)
|
||||
|
||||
@@ -117,7 +97,7 @@ OpenTracks can be used with [Gadgetbridge](https://www.gadgetbridge.org/):
|
||||
__Only required permission:__
|
||||
* _ACCESS_FINE_LOCATION_: required to use the GPS.
|
||||
|
||||
### API
|
||||
### Public API
|
||||
|
||||
OpenTracks includes an API for starting/stopping recording by another installed application (e.g., [Automate](https://llamalab.com/automate/), [Tasker](https://tasker.joaoapps.com), or [Easer](https://github.com/renyuneyun/Easer)).
|
||||
The API can be invoked by sending an explicit Intent to start an activity.
|
||||
@@ -129,27 +109,50 @@ The API can be invoked by sending an explicit Intent to start an activity.
|
||||
* Nightly: `de.dennisguse.opentracks.nightly`
|
||||
|
||||
`Class`:
|
||||
|
||||
* **Start a recording:** `de.dennisguse.opentracks.publicapi.StartRecording`
|
||||
* **Start a recording:** `de.dennisguse.opentracks.publicapi.StopRecording`
|
||||
* **Stop a recording:** `de.dennisguse.opentracks.publicapi.StopRecording`
|
||||
|
||||
For testing via adb: `adb shell am start -n "package/class"`
|
||||
For testing via adb: `adb shell am start -e someParameter someValue -n "package/class"`
|
||||
|
||||
The Start/Stop API is disabled by default to protect the user's privacy, but it can easily be enabled in the settings.
|
||||
`StartRecording` supports the following parameters:
|
||||
|
||||
## Custom Dashboards (incl. map)
|
||||
As of v3.3.1, OpenTracks supports custom dashboards for displaying previously recorded and live tracks.
|
||||
* Set track data: `TRACK_NAME`, `TRACK_DESCRIPTION`, `TRACK_CATEGORY`, and `TRACK_ICON` (non-localized identifier see [/src/main/java/de/dennisguse/opentracks/util/TrackIconUtils.java#L38](/src/main/java/de/dennisguse/opentracks/util/TrackIconUtils.java#L38)).
|
||||
NOTE: if `TRACK_ICON` is not present, `TRACK_CATEGORY` will be used to determine the icon (localized).
|
||||
* Send recorded data to another application via _Dashboard API_: `STATS_TARGET_PACKAGE` and `STATS_TARGET_CLASS`
|
||||
|
||||
The reference implementation is [OSMDashboard](https://github.com/OpenTracksApp/OSMDashboard), which presents an OpenStreetMap map (showing the current track, incl. updates). The Dashboard API is also used by [Gadgetbridge](https://codeberg.org/Freeyourgadget/Gadgetbridge/) for displaying live track statistics on supported wearables.
|
||||
The Public API is disabled by default to protect the user's privacy, but it can easily be enabled in the settings.
|
||||
|
||||
Alternatively, recorded tracks can be shared as KMZ/GPX with installed applications (e.g., [OsmAnd](https://play.google.com/store/apps/details?id=net.osmand)).
|
||||
However, this is rather slow and does not provide updates while recording.
|
||||
## File formats compatibility with open-source software
|
||||
|
||||
| | [GPX 1.1](https://www.topografix.com/GPX/1/1/) | [KML 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html) | [KMZ 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html) |
|
||||
|------------------------------------------------------|------------------------------------------------|----------------------------------------------------------------------|----------------------------------------------------------------------|
|
||||
| [OpenLayers 7.1.0](https://openlayers.org/) | ? | [no](https://github.com/openlayers/openlayers/issues/14104) | [no](https://github.com/openlayers/openlayers/issues/14104) |
|
||||
| [Golden Cheetah 3.5](https://www.goldencheetah.org/) | ? | [no](https://github.com/GoldenCheetah/GoldenCheetah/issues/4271) | [no](https://github.com/GoldenCheetah/GoldenCheetah/issues/4271) |
|
||||
| [GpxPod](https://apps.nextcloud.com/apps/gpxpod) | ? | ? | ? |
|
||||
| [OsmAnd](https://github.com/osmandapp/OsmAnd) | ? | [no](https://github.com/osmandapp/OsmAnd/issues/15271) | [no](https://github.com/osmandapp/OsmAnd/issues/15271) |
|
||||
| [FitTrackee](https://github.com/SamR1/FitTrackee) | yes | n/a | n/a |
|
||||
|
||||
## Dashboard API (incl. map)
|
||||
|
||||
As of v3.3.1, OpenTracks supports custom dashboards for displaying previously recorded and live
|
||||
tracks.
|
||||
|
||||
The reference implementation is [OSMDashboard](https://github.com/OpenTracksApp/OSMDashboard), which
|
||||
presents an OpenStreetMap map (showing the current track, incl. updates). The Dashboard API is also
|
||||
used by [Gadgetbridge](https://codeberg.org/Freeyourgadget/Gadgetbridge/) for displaying live track
|
||||
statistics on supported wearables.
|
||||
|
||||
Alternatively, recorded tracks can be shared as KMZ/GPX with installed applications (
|
||||
e.g., [OsmAnd](https://play.google.com/store/apps/details?id=net.osmand)). However, this is rather
|
||||
slow and does not provide updates while recording.
|
||||
|
||||
The developer documentation is in [README_API.md](README_API.md).
|
||||
|
||||
## Project history
|
||||
|
||||
_OpenTracks_ is based upon Google _My Tracks app_ ([code](https://code.google.com/archive/p/mytracks/)).
|
||||
Initially, _My Tracks_ was initially released by Google in 2010 as [open-source software](http://google-latlong.blogspot.fr/2010/05/code-for-my-tracks-is-now-yours.html).
|
||||
_My Tracks_ was initially released by Google in 2010 as [open-source software](http://google-latlong.blogspot.fr/2010/05/code-for-my-tracks-is-now-yours.html).
|
||||
In 2016, [Google decided to discontinue](https://support.google.com/maps/answer/6333516) _My Tracks_ and stopped distributing it via the Google Play store in April 2016.
|
||||
Then [Plonk42](https://github.com/plonk42) conducted some maintenance work until 2016, so _My Tracks_ could still be used (based upon version _Google's MyTracks_ version 2.0.6).
|
||||
Plonk42's version is available [here](https://github.com/Plonk42/mytracks).
|
||||
|
||||
+5
-3
@@ -17,10 +17,10 @@ Application id: de.dennisguse.opentracks
|
||||
It if it properly tagged F-Droid takes care of building the app and publishing.
|
||||
Usually, it takes two days before the update is published (build, signed, and published).
|
||||
|
||||
The recent build activity can be found [here](https://f-droid.org/wiki/index.php?title=Special:RecentChanges&days=7&from=&hidebots=0&hideanons=1&hideliu=1&limit=500)
|
||||
The recent build activity can be found [here](https://monitor.f-droid.org/builds/build)
|
||||
|
||||
## Google Play Store
|
||||
Application id: de.dennisguse.opentracks.playstore
|
||||
Application id: `de.dennisguse.opentracks.playstore`
|
||||
|
||||
Requirement: `gem install fastlane`
|
||||
|
||||
@@ -35,4 +35,6 @@ Supported languages: https://support.google.com/googleplay/android-developer/tab
|
||||
|
||||
## Translations (localization)
|
||||
|
||||
Translations of the OpenTracks are handled in Weblate: [https://weblate.bubu1.eu/projects/OpenTracks/](https://weblate.bubu1.eu/projects/OpenTracks/).
|
||||
Translations of the OpenTracks are handled in
|
||||
Weblate: [https://hosted.weblate.org/projects/opentracks/](https://hosted.weblate.org/projects/opentracks)
|
||||
.
|
||||
+12
-16
@@ -2,7 +2,7 @@
|
||||
|
||||
## 0x180D: Heartrate Service
|
||||
|
||||
* Decathlon|Geonaute|Kalenji Dual HRM Belt
|
||||
* Decathlon/Geonaute/Kalenji Dual HRM Belt
|
||||
* moofit heart rate monitor
|
||||
* Polar H7
|
||||
* Polar H10
|
||||
@@ -38,17 +38,13 @@ However, often only one value is provided.
|
||||
|
||||
We only support _Instantaneous Power_.
|
||||
|
||||
_WARNING_: we only support physically movements with valid GPS coordinates.
|
||||
Using a device indoors (no GPS available) will only show the current power values, but won't store anything.
|
||||
Also the distance is not computed.
|
||||
|
||||
For cycling:
|
||||
For cycling (incl. cadence):
|
||||
* Assioma Duo Powermeter
|
||||
* Elite drivo II
|
||||
* QUARQ Red DZero Powermeter
|
||||
* Tacx Satori Smart
|
||||
* Wahoo Kickr v4.0
|
||||
* Rotor 2INpower DM Road (only supports power measurement, cadence measurement is propietary @ firmware v1.061)
|
||||
* Rotor 2INpower DM Road (only supports power measurement, cadence measurement is proprietary @ firmware v1.061)
|
||||
|
||||
For running:
|
||||
* Stryd
|
||||
@@ -67,12 +63,12 @@ There are smartphones that have a barometric sensor that OpenTracks uses to comp
|
||||
|
||||
In our tests there are smartphones whose barometric sensor works well and others don't. Here is a list of the smartphones tested:
|
||||
|
||||
|Brand|Model|Android Version|Tests|
|
||||
|-----|-----|---------------|-----------|
|
||||
|Google|Pixel|LineageOS 17.1|Ok|
|
||||
|Google|Pixel 2 XL|LineageOS 17.1|Ok on the bike handlebars but it fails when I carry it in a pocket or backpack|
|
||||
|Google|Pixel 3|Android 11|Failure|
|
||||
|Google|Pixel 3a|Android 11|Ok|
|
||||
|Google|Pixel 3a|Lineage 18.1|Ok|
|
||||
|Google|Pixel 4a|Android 11|Ok|
|
||||
|Sony|Xperia XZ1 Compact|LineageOS 16.1|Failure|
|
||||
| Brand | Model | Android Version | Tests |
|
||||
|--------|--------------------|-----------------|--------------------------------------------------------------------------------|
|
||||
| Google | Pixel | LineageOS 17.1 | Ok |
|
||||
| Google | Pixel 2 XL | LineageOS 17.1 | Ok on the bike handlebars but it fails when I carry it in a pocket or backpack |
|
||||
| Google | Pixel 3 | Android 11 | Failure |
|
||||
| Google | Pixel 3a | Android 11 | Ok |
|
||||
| Google | Pixel 3a | Lineage 18.1 | Ok |
|
||||
| Google | Pixel 4a | Android 11 | Ok |
|
||||
| Sony | Xperia XZ1 Compact | LineageOS 16.1 | Failure |
|
||||
|
||||
+31
-20
@@ -6,7 +6,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.2.0'
|
||||
classpath 'com.android.tools.build:gradle:8.1.0-beta01'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,25 +47,30 @@ android {
|
||||
compileOptions {
|
||||
coreLibraryDesugaringEnabled true
|
||||
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
compileSdkVersion 32
|
||||
compileSdk 33
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
buildConfig true
|
||||
}
|
||||
|
||||
androidResources {
|
||||
generateLocaleConfig = true
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "de.dennisguse.opentracks"
|
||||
versionCode 4874
|
||||
versionName "v4.0.0"
|
||||
versionCode 5196
|
||||
versionName "v4.3.4"
|
||||
|
||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 32
|
||||
minSdk 21
|
||||
targetSdk 33
|
||||
|
||||
testInstrumentationRunner "de.dennisguse.opentracks.TestRunner"
|
||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||
@@ -81,6 +86,8 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
namespace 'de.dennisguse.opentracks'
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
@@ -100,14 +107,15 @@ android {
|
||||
releasePlayStore {
|
||||
applicationIdSuffix ".playstore"
|
||||
versionNameSuffix "-PlayStore"
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
applicationVariants.configureEach { variant ->
|
||||
variant.resValue "string", "applicationId", variant.applicationId
|
||||
|
||||
if (variant.buildType.name == 'nightly') {
|
||||
variant.outputs.all {
|
||||
variant.outputs.configureEach {
|
||||
setVersionCodeOverride(getVersionCode())
|
||||
setVersionNameOverride(getVersionName())
|
||||
outputFileName = "${applicationId}_${variant.versionCode}.apk"
|
||||
@@ -117,20 +125,23 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.4.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.preference:preference:1.2.0'
|
||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||
implementation 'com.google.android.material:material:1.6.0'
|
||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.8.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||
implementation 'androidx.mediarouter:mediarouter:1.3.1'
|
||||
|
||||
androidTestImplementation 'androidx.test:core:1.4.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test:rules:1.4.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.4.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
androidTestImplementation 'org.mockito:mockito-android:4.5.1'
|
||||
androidTestImplementation 'androidx.test:core:1.5.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test:rules:1.5.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.5.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.3.1'
|
||||
|
||||
androidTestUtil 'androidx.test:orchestrator:1.4.1'
|
||||
androidTestUtil 'androidx.test:orchestrator:1.4.2'
|
||||
}
|
||||
|
||||
@@ -7,4 +7,4 @@ Funktionen & Features:
|
||||
* zeichnet deine Herzfrequenz auf (Bluetooth LE)
|
||||
* nutze beim Radfahren deine Trittfrequenz- und Geschwindigkeitssensoren sowie Leistungsmesser (Bluetooth LE)
|
||||
* exportiert deine Daten als KML oder GPX
|
||||
* funktioniert ganz ohne Internet
|
||||
* funktioniert ganz ohne Internet
|
||||
|
||||
@@ -1 +1 @@
|
||||
OpenTracks – ein GPS-Tracker, der deine Daten für sich behält.
|
||||
OpenTracks – ein GPS-Tracker, der deine Daten für sich behält.
|
||||
|
||||
@@ -7,5 +7,5 @@ Changes:
|
||||
Bugfix:
|
||||
- Speech announcements use MAY_DUCK (again)
|
||||
- ChartView shows markers (again)
|
||||
- Fixed landscape layout of TrackListActivity
|
||||
- Fixed landscape recordingLayout of TrackListActivity
|
||||
- Request legacy file API behavior (necessary for Android 10)
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
v4.0.1: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Voice announcements are now configurable
|
||||
- Fullscreen mode now also hides Android's navigation bar
|
||||
- Connection to BLE sensors should now be faster
|
||||
- Allow to reset settings while recording
|
||||
- Allow to change import/export settings while recording
|
||||
- KML/KMZ: do not use icons to be loaded from Google
|
||||
|
||||
Bugfix:
|
||||
- TrackStoppedActivity now works on smaller screens
|
||||
- Requests permissions on track list
|
||||
@@ -0,0 +1,10 @@
|
||||
v4.0.2: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Add nautical miles
|
||||
- Settings can be reset while recording
|
||||
|
||||
Bugfix:
|
||||
- Voice announcement rounding
|
||||
- Chart: elevation was weirdly smoothed
|
||||
- Export was not checking if file exists
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.0.3: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Max. speed as reported by sensor (GPS or BLE)
|
||||
|
||||
Bugfix:
|
||||
- Prevent crash if OSM Dashboard is not installed
|
||||
- GPX track export: do not export empty track segments
|
||||
@@ -0,0 +1,13 @@
|
||||
v4.0.4: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Store BLE sensor data even without distance (every 10s)
|
||||
- Public API: allows setting name, category, and description
|
||||
|
||||
Bugfix:
|
||||
- Crash when taking a picture and rotating the phone
|
||||
-
|
||||
|
||||
Developer:
|
||||
- Target SDK 33
|
||||
- Barometer uses interval of 5s
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.0.5: OpenTracks
|
||||
|
||||
Changes:
|
||||
- PlayStore relase get's another logo
|
||||
|
||||
Bugfix:
|
||||
- Interval list shows "-" if gain/loss is not present
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
v4.0.6: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- EGM2008 offset computed wrong for negative longitudes
|
||||
- PublicAPI crashed if OpenTracks was in background (API26+)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
v4.0.7: OpenTracks
|
||||
|
||||
Changes:
|
||||
- GPS signal lost timeout: 30s
|
||||
- Android13: request permission to show notifications
|
||||
- Faster deletion of tracks (vacuum database only if needed)
|
||||
|
||||
Bugfix:
|
||||
- PublicAPI stop command resulted in crash (introduced in v4.0.6)
|
||||
- Permissions: request all permissions during on startup
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
v4.0.8: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Altitude always shown in m/ft
|
||||
- Show track timezone if different than device timezone
|
||||
- PublicAPI: support setting the track icon explicitly
|
||||
|
||||
Developer:
|
||||
- Introduced DistanceFormatter
|
||||
- Upgrade to AGP 7.3/desugar for Duration.class
|
||||
- Android12+: do splashscreen via Androidx
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.1.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Export KML/KMZ version 2.3 only (was 2.2)
|
||||
- For crank-based powermeters: fetch cadence
|
||||
|
||||
Bugfix:
|
||||
- Workaround for crash on "import all"
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.1.1: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Add introduction screen (show once)
|
||||
|
||||
Bugfix:
|
||||
- Do not crash if no GPS device is present
|
||||
@@ -0,0 +1,11 @@
|
||||
v4.1.2: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Voice announcements only use on decimal for distance
|
||||
|
||||
Bugfix:
|
||||
- Crash when stopping (race condition)
|
||||
|
||||
Developer:
|
||||
- Github actions: disabled instrumentation testing (too slow)
|
||||
- Tests are done with only fake GPS data
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.2.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Show heartrate zone information
|
||||
|
||||
Developer:
|
||||
- Migrated to hosted.weblate.org
|
||||
- Recording UI uses Views directly
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.2.1: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Speed sensor: acceptable wheel diameter is 100mm-4000mm
|
||||
|
||||
Bugfix:
|
||||
- Reconnect to a sensor if connection is lost
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
v4.2.2: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Track icon wrongly shown in dialog
|
||||
- Textcolor for heartrate zone 1 was ignoring theme
|
||||
- Not all activityTypes were shown in dropdownlist
|
||||
- Crash due to color not found
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.3.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Allow backups (incl. encrypted to the Google)
|
||||
- Android 13: allow app-specific language settings
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.3.1: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Setting to skip voice announcements on device speaker
|
||||
|
||||
Bugfix:
|
||||
- GPS disabled warning was falsely shown
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
v4.3.2: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- KML header was misformed (#1522)
|
||||
|
||||
Developer:
|
||||
- Use Java 17 (multi-line text, switch)
|
||||
- API33: locale config is generated
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.3.3: OpenTracks
|
||||
|
||||
Bugfixes:
|
||||
- Hidden fields were shown (introduced in v4.2.0)
|
||||
- Speed was shown instead of pace (introduced in v4.2.0)
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.3.4: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Android 13: regional locales could not be selected (introduced in v4.3.0)
|
||||
@@ -0,0 +1,11 @@
|
||||
v3.20.2 : OpenTracks
|
||||
|
||||
Changements :
|
||||
- Le gain et la perte d'altitude sont des entiers au lieu de floats
|
||||
- Demander la permission avant d'utiliser Bluetooth par un message Toast
|
||||
|
||||
Correction d'un bug :
|
||||
- Définir FLAG_IMMUTABLE pour PendingIntents pour éviter un crash sur Android 12
|
||||
- Le capteur de cadence renvoie 0 au lieu de None lorsque l'utilisateur ne pédale pas.
|
||||
- Le capteur de vitesse renvoie 0 au lieu de la dernière vitesse connue quand c'est arrêté
|
||||
- Avec les capteurs, le temps de déplacement s'arrête lorsqu'il n'est pas en mouvement
|
||||
@@ -7,5 +7,11 @@ Fonctionnalités :
|
||||
* Dénivelé positif et négatif: via capteur de pression barométrique
|
||||
* Synthèse vocale
|
||||
* Prise en charge des capteurs Bluetooth LE : rythme cardiaque, vitesse et distance (vélo), cadence de pédalage (vélo), et capteur de puissance (vélo)
|
||||
* Export : export des traces au format KMZ (photos incluses), KML ou GPX
|
||||
* Export : export des traces au format KMZ (photos incluses), KML ou GPX
|
||||
* Pas d'accès internet requis : Internet n'est pas nécessaire pour le fonctionnement de l'application
|
||||
* Thème sombre ou claire, avec le respect des paramètres du système
|
||||
* Pas de publicités
|
||||
|
||||
Logiciel Open Source
|
||||
Vous pouvez utiliser, étudier, changer et partager le code source.
|
||||
Licence Apache 2.0
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
v3.18.6: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- "Taispeáin ar léarscáil" - feidhmiúlacht trí KML in ionad KMZ
|
||||
- Ní chuimsíonn fógra gutha ainm na haipe
|
||||
- Agus é ag taifeadadh: déantar gnóthachan/caillteanas ingearchló a nuashonrú gach soicind
|
||||
- Tuairiscí an tsiopa aistrithe
|
||||
|
||||
Maoláin:
|
||||
- Aistriúcháin fógartha gutha seasta neamhbhailí
|
||||
|
||||
Forbróir:
|
||||
- ceadanna Android12 (BLUETOOTH_SCAN)
|
||||
- ceadanna Android12 (ACCESS_COARSE_LOCATION)
|
||||
- Ag dearbhú do Trackpoints (tástáil)
|
||||
- Aistriúcháin neamhúsáidte bainte
|
||||
@@ -0,0 +1,8 @@
|
||||
v3.19.1: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Aistriúcháin nuashonraithe.
|
||||
|
||||
Ceartúchán Bug:
|
||||
- Easpórtáil Gach Rianta fabht seasta.
|
||||
- Lean bug Track socraithe.
|
||||
@@ -0,0 +1,7 @@
|
||||
v3.20.0: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Agus tú ag taifeadadh: tá UI staitisticí inchumraithe anois
|
||||
|
||||
Ceartúchán Bug:
|
||||
- Níor tugadh aird ar athrú ainm/catagóir an traic le linn an taifeadta (a tugadh isteach i v3.19.0)
|
||||
@@ -0,0 +1,6 @@
|
||||
v3.20.1: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Is féidir fógraí a úsáid anois de réir achair agus ama
|
||||
- Bíp fógraí mura bhfuil aon bhogearraí Téacs-go-hurlabhra suiteáilte
|
||||
- Baineadh minicíocht oiriúnaitheach suíomh GPS
|
||||
@@ -0,0 +1,11 @@
|
||||
v3.22.0: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Ní stórálfar láithreacha ach amháin má thaistilítear > an t-eatramh faid a thaifeadtar
|
||||
(feidhmiúlacht fágtha bainte den bhrath díomhaoin/neamhdhíomhaoin)
|
||||
- Is féidir Chomhéadain a chumrú chun clog a thaispeáint
|
||||
- Is féidir luas díomhaoin a chumrú anois
|
||||
|
||||
Ceartúchán Bug:
|
||||
- Eatraimh: tuairteála nuair a stóráiltear gnóthachan ach ní caillteanas
|
||||
- Agus tú ag taifeadadh: seachain tuairteáil agus fógraí gutha á ndíchumasú
|
||||
@@ -0,0 +1,10 @@
|
||||
v3.23.0: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Cuireadh próifílí Chomhéadain leis
|
||||
- Staitisticí comhiomlánaithe le haghaidh rianta roghnaithe
|
||||
- TrackPoints díomhaoin/neamhdhomhaoin athfhorfheidhmithe de réir mar a stop an t-am bogtha ag obair (tugtha isteach in v3.22.0)
|
||||
|
||||
Ceartúcháin fhabht:
|
||||
- Braiteoirí BLE: sonraí soiléire maidir le dínascadh
|
||||
- Ní mór an t-easpórtáil uathoibríoch a cheadú go fóill
|
||||
@@ -0,0 +1,11 @@
|
||||
v3.25.0: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Tá GPS roghnach anois lena n-áirítear. Rabhadh GPS díchumasaithe
|
||||
- PublicAPI: is féidir le feidhmchláir eile sonraí taifeadta a fháil anois má thosaíonn siad ar thaifeadadh (curtha i bhfeidhm do Gadgetbridge)
|
||||
- Gníomhaíochtaí nua: snámh, snámh oscailte, agus workout
|
||||
- Baineadh feidhmiúlacht pic sciar
|
||||
|
||||
Ceartúchán Bug:
|
||||
- Chomhéadain: luas / airde nuair a léim idir 0.0 agus an luach iarbhír
|
||||
- Is féidir rianta le emojis a onnmhairiú
|
||||
@@ -0,0 +1,5 @@
|
||||
v3.25.1: OpenTracks
|
||||
|
||||
Ceartúchán Bug:
|
||||
- Easpórtáil uathoibríoch: tá súil againn teipeanna ciúine a chosc
|
||||
- Easpórtáil uathoibríoch: cosc tuairteála
|
||||
@@ -0,0 +1,8 @@
|
||||
v3.26.0: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Is féidir fógraí gutha a chumrú
|
||||
- Fógraí gutha faoi ráta croí
|
||||
|
||||
Ceartúchán Bug:
|
||||
- Tuairteála mar gheall ar riocht cine
|
||||
@@ -0,0 +1,4 @@
|
||||
v3.26.1: OpenTracks
|
||||
|
||||
Ceartúchán Bug:
|
||||
- Níor sháraigh barra gníomhaíochta comhthéacsúla an barra uirlisí i dtéama na hoíche.
|
||||
@@ -0,0 +1,13 @@
|
||||
v4.0.1: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Is féidir fógraí gutha a chumrú anois
|
||||
- Folaíonn mód lánscáileáin anois barra nascleanúna Android freisin
|
||||
- Ba cheart go mbeadh nasc le braiteoirí BLE níos tapúla anois
|
||||
- Ceadaigh socruithe a athshocrú agus tú ag taifeadadh
|
||||
- Ceadaigh socruithe iompórtála/easpórtála a athrú agus tú ag taifeadadh
|
||||
- KML/KMZ: ná húsáid deilbhíní le luchtú ó Google
|
||||
|
||||
Ceartúchán Bug:
|
||||
- Oibríonn TrackStoppedActivity ar scáileáin níos lú anois
|
||||
- Iarratas ar cheadanna ar an rianliosta
|
||||
@@ -0,0 +1,10 @@
|
||||
v4. 0. 2: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- - Agus muirmhíle
|
||||
- Is féidir socruithe a athshocrú agus taifeadadh á dhéanamh
|
||||
|
||||
Ceartúcháin:
|
||||
- Fógra gutha slánú
|
||||
- Cairt: bhí ingearchló smoothed weirdly
|
||||
- Ní raibh easpórtáil ag seiceáil an bhfuil an comhad ann
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.0. 3: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
Sonraíocht: Max. luas mar a thuairiscigh braiteoir (GPS nó BLE)
|
||||
|
||||
Ceartúcháin:
|
||||
- Cosc ar thuairteáil mura bhfuil Painéal Na Nionstraimí OSM suiteáilte
|
||||
- Easpórtáil rian GPX: ná heaspórtáil deighleáin an amhráin folamh
|
||||
@@ -0,0 +1,13 @@
|
||||
v4. 0. 4: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Stóráil sonraí braiteoir BLE fiú gan achar (gach 10s)
|
||||
- PUBLIC API: ceadaíonn leagan ainm, catagóir, agus cur síos
|
||||
|
||||
Ceartúcháin:
|
||||
- Tuairteála nuair a ghlacadh pictiúr agus rothlach an fón
|
||||
-
|
||||
|
||||
Forbróir:
|
||||
Sprioc-SDK 33
|
||||
- Baraiméadar úsáideann eatramh de 5s
|
||||
@@ -0,0 +1,7 @@
|
||||
v4. 0. 5: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- - PlayStore release faigh lógó eile
|
||||
|
||||
Ceartúcháin:
|
||||
- Taispeánann liosta eatramh " - " mura bhfuil gnóthachan/caillteanas i láthair
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.0. 6: OpenTracks
|
||||
|
||||
Ceartúcháin:
|
||||
- Egm2008 fhritháireamh mícheart ríomh le haghaidh domhanfhad diúltach
|
||||
- - Crashed API poiblí má Bhí Rianta Oscailte sa chúlra (26 AIBREÁN+)
|
||||
@@ -0,0 +1,10 @@
|
||||
v4. 0. 7: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Gps comhartha caillte teorainn ama: 30s
|
||||
- - Android 13: cead a iarraidh chun fógraí a thaispeáint
|
||||
- Scrios rianta níos tapúla (bunachar sonraí folúis amháin más gá)
|
||||
|
||||
Ceartúcháin:
|
||||
- PublicAPI stop ordú mar thoradh ar tuairteála (a tugadh isteach i v4. 0. 6)
|
||||
- Ceadanna: ceadanna a iarraidh le linn tosaithe
|
||||
@@ -0,0 +1,11 @@
|
||||
v4. 0. 8: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Airde a thaispeántar i gcónaí i m/ft
|
||||
- Taispeáin crios ama an riain mura bhfuil sé éagsúil le crios ama na feiste
|
||||
- PublicAPI: tacaíocht deilbhín an riain a shocrú go sainráite
|
||||
|
||||
Forbróir:
|
||||
- A Tugadh Isteach Cianfhormáideoir
|
||||
- Uasghrádú GO AGP 7.3/desugar Ar Feadh Ré.aicme
|
||||
- Android12+: déan splancscáileán trí Androidx
|
||||
@@ -0,0 +1,8 @@
|
||||
v4. 1. 0: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Easpórtáil leagan KML/KMZ 2. 3 amháin (2. 2)
|
||||
- Le haghaidh crank-bhunaithe powermeters: faigh cadence
|
||||
|
||||
Ceartúcháin:
|
||||
- Workaround do tuairteála ar "iompórtáil gach rud"
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.1. 1: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Cuir scáileán réamhrá leis (taispeáin uair amháin)
|
||||
|
||||
Ceartúcháin:
|
||||
- Ná tuairteála mura bhfuil gléas GPS i láthair
|
||||
@@ -0,0 +1,22 @@
|
||||
Cairde rianaithe spóirt a bhfuil meas aige ar do phríobháideachas.
|
||||
|
||||
Tugann gníomhaíochtaí spóirt agus lasmuigh sonas, scil agus muinín.
|
||||
|
||||
Cuir luach ar do shláinte trí súil a choinneáil ar d'oiliúint.
|
||||
Taifeadann tú de réir mar a théann tú ag rith nó ag siúl, agus tugann sé ríomhaire rothar duit le scáileán níos mó le haghaidh rothaíochta.
|
||||
Marcáil láithreacha suimiúla ar do bhealach le pictiúir.
|
||||
Coinnigh staitisticí taifeadta go mion go leor le haghaidh anailíse.
|
||||
Comhroinn ach na sonraí a theastaíonn uait a bheith ag daoine eile.
|
||||
|
||||
* Fógraí gutha.
|
||||
* Tacaíonn braiteoirí Bluetooth LE: ráta croí, luas agus fad (rothaíocht), deireadh (rothaíocht), agus méadar cumhachta (rothaíocht).
|
||||
* Gnóthachan agus caillteanas airde: trí bhraiteoir barometric.
|
||||
* Airde léirithe in EGM2008 (os cionn meánleibhéal na farraige); onnmhairiú mar WGS84.
|
||||
* Easpórtáil sonraí mar rianta mar KMZ (grianghraif san áireamh), KML, nó GPX.
|
||||
* Gan rochtain ar an Idirlíon nó ceadanna breise.
|
||||
* Téama dorcha agus éadrom, ag urramú socruithe an chórais.
|
||||
* Gan fógraí.
|
||||
|
||||
Bogearraí Libre / Bogearraí Saor in Aisce / Foinse Oscailte
|
||||
rud a chiallaíonn gur féidir leat an cód foinse a úsáid, a staidéar, a athrú agus a roinnt.
|
||||
Ceadúnaithe Apache 2.0
|
||||
@@ -0,0 +1 @@
|
||||
Cairde rianaithe spóirt a bhfuil meas aige ar do phríobháideachas.
|
||||
@@ -0,0 +1 @@
|
||||
OpenTracks
|
||||
@@ -0,0 +1,16 @@
|
||||
v3.18.6: OpenTracks
|
||||
|
||||
Thay đổi:
|
||||
- Tính năng "Hiện trên bản đồ" thông qua KML thay vì KMZ
|
||||
- Thông báo bằng giọng nói không bao gồm tên ứng dụng
|
||||
- Trong lúc ghi: Tăng/ giảm độ cao được cập nhật mỗi giây
|
||||
- Thông tin cửa hàng được cập nhật
|
||||
|
||||
Sửa lỗi:
|
||||
- Sửa lỗi các bản dịch Thông báo giọng nói không hợp lệ
|
||||
|
||||
Phát triển:
|
||||
- Quyền Android 12 (BLUETOOTH_SCAN)
|
||||
- Quyền Android 12 (ACCESS_COARSE_LOCATION)
|
||||
- Khẳng định cho TrackPoints (thử nghiệm)
|
||||
- Loại bỏ các bản dịch thừa
|
||||
@@ -1,11 +1,22 @@
|
||||
OpenTracks là một môn thể thao theo dõi ứng dụng hoàn toàn tôn trọng sự riêng tư.
|
||||
OpenTracks là một ứng dụng theo dõi thể theo tôn trọng sự riêng tư của bạn.
|
||||
|
||||
Tính năng:
|
||||
* Thể theo dõi theo dõi thể thao và hoạt động ngoài trời
|
||||
* Ảnh và đánh dấu: đánh dấu địa điểm thú vị trong khi theo dõi
|
||||
* Độ cao được và mất: thông qua khí quyển cảm biến
|
||||
* Độ cao thể hiện trong EGM2008 (trên mực nước biển); xuất khẩu như WGS84
|
||||
* Tiếng nói thông báo
|
||||
* Hỗ trợ Bluetooth LE cảm biến: tim, tốc độ và khoảng cách (đi xe đạp), nhịp (đi xe đạp), và đồng hồ điện (đi xe đạp)
|
||||
* Xuất dữ liệu: xuất bài hát hoặc là BIỂU (bao gồm. ảnh) DÕI hay C
|
||||
* Không có Internet: Internet là không được sử dụng
|
||||
Thể thao và các hoạt động ngoài trời cho bạn niềm vui, kĩ năng và sự tự tin.
|
||||
|
||||
Quý trọng sức khỏe của bạn bằng cách theo dõi tập luyện của bạn.
|
||||
Ghi lại quá trình khi bạn chạy bộ hoặc đi bộ, và cho bạn một màn hình xe đạp khi đạp xe.
|
||||
Đánh dấu những địa điểm thú vị trong lúc bạn di chuyển bằng hình ảnh.
|
||||
Giữ các thống kê chi tiết để phân tích kĩ càng.
|
||||
Chỉ chia sẻ những dữ liệu bạn muốn chia sẻ.
|
||||
|
||||
* Thông báo bằng giọng nói.
|
||||
* Hỗ trợ Bluetooth LE cảm biến: nhịp tim, tốc độ và khoảng cách (đi xe đạp), nhịp (đi xe đạp), và đồng hồ sức mạnh (đi xe đạp)
|
||||
* Tăng hoặc giảm độ cao: thông qua cảm biến khí áp.
|
||||
* Độ cao được hiển thị dưới EGM2009 (trên nghĩa là trên mực nước biển), KML hoặc GPX.
|
||||
* Xuất dữ liệu được lại dưới KMZ (gồm hình ảnh), KML hoặc GPX.
|
||||
* Không cần Internet hoặc các quyền không cần thiết.
|
||||
* Chế độ tối hoặc sáng, theo cài đặt hệ thống.
|
||||
* Không quảng cáo.
|
||||
|
||||
Phần mềm Libre / Free Software / Mã nguồn mở
|
||||
có nghĩa là bạn có thể dùng, học tập, thay đổi và chia sẻ mã nguồn.
|
||||
Licensed Apache 2.0
|
||||
|
||||
@@ -1 +1 @@
|
||||
Một môn thể thao theo dõi mà hoàn toàn tôn trọng sự riêng tư.
|
||||
Một người bạn theo dõi mà tôn trọng sự riêng tư của bạn.
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
OpenTracks是一个完全尊重你的隐私的运动跟踪应用程序。
|
||||
一款尊重您的隐私的运动追踪应用。
|
||||
|
||||
健康幸福的生活总少不了各种运动和户外活动。
|
||||
|
||||
使用本应用,您可以保持对您的训练数据的有效追踪,为您的健康助力。
|
||||
本应用可以在您跑步或散步时记录数据,也可以在您骑车时作为一个大屏幕自行车电脑。
|
||||
您可以为路上的有趣位置创建标记,还可以用图片作为标记。
|
||||
您的数据会被详尽地记录下来,供您查阅分析。
|
||||
您的数据只会分享给您想要分享给的人。
|
||||
|
||||
特点。
|
||||
* 运动追踪:追踪你的运动和户外活动
|
||||
* 照片和标记:在跟踪时标记有趣的地点
|
||||
* 海拔增减:通过气压传感器
|
||||
* 高度显示为EGM2008(平均海平面以上);导出为WGS84
|
||||
* 语音播报
|
||||
* 支持蓝牙LE传感器:心率、速度和距离(骑车)、节奏(骑车)和功率计(骑车)。
|
||||
* 导出数据:以KMZ(包括照片)、KML或GPX形式导出轨迹
|
||||
* 没有互联网接入。不使用互联网
|
||||
* 支持蓝牙LE传感器:心率、速度和距离(骑车)、踏频(骑车)和功率计(骑车)。
|
||||
* 海拔增减:通过气压传感器。
|
||||
* EGM2008 海拔显示(平均海平面以上);导出为 WGS84。
|
||||
* 导出路线数据,支持 KMZ(包括照片)、KML 和 GPX 格式。
|
||||
* 不需要联网,不需要额外权限。
|
||||
* 深色和浅色主题,跟随系统设置。
|
||||
* 没有广告。
|
||||
|
||||
本应用是开源软件
|
||||
意味着您可以使用、学习、修改和分发本应用的源代码。
|
||||
本应用使用 Apache 2.0 协议发布。
|
||||
|
||||
@@ -1 +1 @@
|
||||
一个完全尊重你隐私的运动追踪器。
|
||||
一款尊重您的隐私的运动追踪应用。
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
一個尊重您的隱私的運動蹤跡紀錄器。
|
||||
|
||||
運動與戶外活動賦予幸福、技能與自信。
|
||||
|
||||
由保持追蹤您的訓練來重視您的健康。
|
||||
紀錄您的跑步或健行,並且在您騎自行車時給您一台具有大螢幕的自行車電腦。
|
||||
用照片標記您一路上有趣的地點。
|
||||
保持紀錄非常詳細的統計資料以供分析。
|
||||
僅分享資料給您想要分享的其他人。
|
||||
|
||||
* 語音播報。
|
||||
* 支援藍芽 LE 感測器:心律、速度與距離 (騎自行車)、踏頻 (騎自行車)、功率計 (騎自行車)。
|
||||
* 海拔上升與下降:通過氣壓感測器。
|
||||
* EGM2008 海拔顯示 (平均海平面以上);匯出為 WGS84。
|
||||
* 匯出蹤跡資料為 KMZ (包含照片)、KML、GPX。
|
||||
* 沒有網路存取或額外的權限需求。
|
||||
* 深色與淺色主題,取決於系統設定。
|
||||
* 沒有廣告。
|
||||
|
||||
此為 自由軟體 / 免費軟體 / 開放原始碼
|
||||
這意味您可以使用、學習、修改與分享此專案的原始碼。
|
||||
授權條款:Apache 2.0
|
||||
@@ -0,0 +1 @@
|
||||
一個尊重您的隱私的運動蹤跡紀錄器。
|
||||
@@ -0,0 +1 @@
|
||||
OpenTracks
|
||||
+2
-1
@@ -1 +1,2 @@
|
||||
android.useAndroidX=true
|
||||
android.useAndroidX=true
|
||||
org.gradle.jvmargs=-Xmx1024m
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -205,6 +205,12 @@ set -- \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
||||
Vendored
+8
-6
@@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -25,7 +25,7 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
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!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
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
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.6 KiB |
@@ -3,48 +3,25 @@ package de.dennisguse.opentracks;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onData;
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.Espresso.pressBack;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.scrollTo;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.action.ViewActions.longClick;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.anything;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.childAtPosition;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.veryLongTouch;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.withListSize;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Instrumentation;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.test.espresso.IdlingRegistry;
|
||||
import androidx.test.espresso.IdlingResource;
|
||||
import androidx.test.espresso.ViewInteraction;
|
||||
import androidx.test.espresso.assertion.ViewAssertions;
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.services.TrackDeleteService;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class EspressoDeleteTrackTest {
|
||||
@@ -55,149 +32,33 @@ public class EspressoDeleteTrackTest {
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
private MyIdlingResource idlingResource;
|
||||
|
||||
@Before
|
||||
public void registerIntentServiceIdlingResource() {
|
||||
Instrumentation instrumentation
|
||||
= InstrumentationRegistry.getInstrumentation();
|
||||
idlingResource = new MyIdlingResource(instrumentation.getTargetContext());
|
||||
IdlingRegistry.getInstance().register(idlingResource);
|
||||
}
|
||||
|
||||
@After
|
||||
public void unregisterIntentServiceIdlingResource() {
|
||||
IdlingRegistry.getInstance().unregister(idlingResource);
|
||||
}
|
||||
|
||||
@Ignore("Test fails permanently")
|
||||
@Deprecated
|
||||
@Test
|
||||
public void espressoDeleteTrackTest() {
|
||||
int countBefore;
|
||||
// TrackListActivity: start recording
|
||||
onView(withId(R.id.track_list_fab_action)).perform(click());
|
||||
|
||||
{
|
||||
// TrackListActivity: start recording
|
||||
ViewInteraction trackControllerRecordButton = onView(withId(R.id.track_list_fab_action));
|
||||
trackControllerRecordButton.perform(click());
|
||||
}
|
||||
// TrackRecordingActivity
|
||||
onView(withId(R.id.track_recording_fab_action))
|
||||
// wait; stay recording
|
||||
.perform(waitFor(5000))
|
||||
// stop;
|
||||
.perform(longClick());
|
||||
|
||||
{
|
||||
// TrackRecordingActivity
|
||||
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
|
||||
|
||||
// wait; stay recording
|
||||
trackControllerStopButton.perform(waitFor(5000));
|
||||
|
||||
// stop;
|
||||
trackControllerStopButton.perform(veryLongTouch(1600));
|
||||
}
|
||||
|
||||
// back
|
||||
pressBack();
|
||||
|
||||
// get number of items before deleting a track
|
||||
countBefore = numberOfItemsListView();
|
||||
// TrackStoppedActivity
|
||||
onView(withId(R.id.finish_button)).perform(click());
|
||||
|
||||
// select track
|
||||
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(veryLongTouch(5000));
|
||||
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(longClick());
|
||||
|
||||
// open menu and delete selected track
|
||||
ViewInteraction overflowMenuButton = onView(
|
||||
allOf(withContentDescription("More options"),
|
||||
childAtPosition(
|
||||
childAtPosition(
|
||||
withId(R.id.bottom_app_bar),
|
||||
2),
|
||||
2),
|
||||
isDisplayed()));
|
||||
overflowMenuButton.perform(click());
|
||||
onView(allOf(withContentDescription("More options"), isDisplayed()))
|
||||
.perform(click());
|
||||
|
||||
ViewInteraction appCompatTextView = onView(
|
||||
allOf(withId(R.id.title), withText("Delete"),
|
||||
childAtPosition(
|
||||
childAtPosition(
|
||||
withId(R.id.content),
|
||||
0),
|
||||
0),
|
||||
isDisplayed()));
|
||||
appCompatTextView.perform(click());
|
||||
onView(withText("Delete")).perform(click());
|
||||
|
||||
ViewInteraction appCompatButton = onView(
|
||||
allOf(withId(android.R.id.button1), withText("Yes"),
|
||||
childAtPosition(
|
||||
childAtPosition(
|
||||
withId(R.id.buttonPanel),
|
||||
0),
|
||||
3)));
|
||||
appCompatButton.perform(scrollTo(), click());
|
||||
onView(withText("OK")).perform(click());
|
||||
|
||||
// check number of items after delete
|
||||
onView(withId(R.id.track_list)).check(ViewAssertions.matches(withListSize(countBefore - 1)));
|
||||
}
|
||||
|
||||
|
||||
private int numberOfItemsListView() {
|
||||
final int[] counts = new int[1];
|
||||
onView(withId(R.id.track_list)).check(matches(new TypeSafeMatcher<View>() {
|
||||
@Override
|
||||
public boolean matchesSafely(View view) {
|
||||
ListView listView = (ListView) view;
|
||||
|
||||
counts[0] = listView.getCount();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
}
|
||||
}));
|
||||
|
||||
return counts[0];
|
||||
}
|
||||
|
||||
|
||||
private static class MyIdlingResource implements IdlingResource {
|
||||
|
||||
private final Context context;
|
||||
private ResourceCallback resourceCallback;
|
||||
|
||||
public MyIdlingResource(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return MyIdlingResource.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerIdleTransitionCallback(IdlingResource.ResourceCallback resourceCallback) {
|
||||
this.resourceCallback = resourceCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIdleNow() {
|
||||
boolean idle = !isDeleteServiceRunning();
|
||||
if (idle && resourceCallback != null) {
|
||||
resourceCallback.onTransitionToIdle();
|
||||
}
|
||||
return idle;
|
||||
}
|
||||
|
||||
private boolean isDeleteServiceRunning() {
|
||||
ActivityManager manager =
|
||||
(ActivityManager) context.getSystemService(
|
||||
Context.ACTIVITY_SERVICE);
|
||||
for (ActivityManager.RunningServiceInfo info :
|
||||
manager.getRunningServices(Integer.MAX_VALUE)) {
|
||||
if (TrackDeleteService.class.getName().equals(
|
||||
info.service.getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// tracklist is empty now
|
||||
onView(allOf(withText("Start recording your next adventure here"), isDisplayed()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,25 +5,20 @@ import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.longClick;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.anything;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.selectTabAtIndex;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.test.espresso.UiController;
|
||||
import androidx.test.espresso.ViewAction;
|
||||
import androidx.test.espresso.ViewInteraction;
|
||||
import androidx.test.espresso.action.MotionEvents;
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -53,7 +48,7 @@ public class EspressoUITest {
|
||||
.perform(longClick());
|
||||
|
||||
// TrackStoppedActivity: resume
|
||||
onView(withId(R.id.resume_button))
|
||||
onView(allOf(withId(R.id.resume_button), isClickable()))
|
||||
.perform(click());
|
||||
|
||||
// TrackRecordingActivity: wait and then stop
|
||||
@@ -93,7 +88,7 @@ public class EspressoUITest {
|
||||
tabLayout.perform(waitFor(1000));
|
||||
|
||||
// stop
|
||||
trackControllerStopButton.perform(veryLongTouch(1600));
|
||||
trackControllerStopButton.perform(longClick());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,36 +96,6 @@ public class EspressoUITest {
|
||||
@Test
|
||||
public void selectAndDeleteTrack() {
|
||||
onView(withId(R.id.track_list)).check(matches(isDisplayed()));
|
||||
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(veryLongTouch(2000));
|
||||
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(longClick());
|
||||
}
|
||||
|
||||
private static ViewAction veryLongTouch(final int duration_ms) {
|
||||
return new ViewAction() {
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Perform long touch.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(UiController uiController, final View view) {
|
||||
// Get view absolute position
|
||||
int[] location = new int[2];
|
||||
view.getLocationOnScreen(location);
|
||||
|
||||
// Offset coordinates by view position
|
||||
float[] coordinates = new float[]{location[0] + 1, location[1] + 1};
|
||||
|
||||
// Send down event, pause, and send up
|
||||
MotionEvent down = MotionEvents.sendDown(uiController, coordinates, new float[]{1f, 1f}).down;
|
||||
uiController.loopMainThreadForAtLeast(duration_ms);
|
||||
MotionEvents.sendUp(uiController, down, coordinates);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public class ChartPointTest {
|
||||
statistics.setTotalTime(Duration.ofSeconds(1000));
|
||||
|
||||
// when
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), Altitude.EGM2008.of(0), false, UnitSystem.IMPERIAL);
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), false, UnitSystem.IMPERIAL);
|
||||
|
||||
// then
|
||||
assertEquals(1000000, (long) point.getTimeOrDistance());
|
||||
@@ -40,7 +40,7 @@ public class ChartPointTest {
|
||||
statistics.setTotalDistance(Distance.of(1000));
|
||||
|
||||
// when
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), Altitude.EGM2008.of(0), true, UnitSystem.METRIC);
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), true, UnitSystem.METRIC);
|
||||
|
||||
// then
|
||||
assertEquals(1, (long) point.getTimeOrDistance());
|
||||
@@ -50,9 +50,11 @@ public class ChartPointTest {
|
||||
public void create_get_altitude_speed_and_pace() {
|
||||
// given
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
|
||||
.setAltitude(Altitude.EGM2008.of(50));
|
||||
|
||||
// when
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), Altitude.EGM2008.of(50), false, UnitSystem.METRIC);
|
||||
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
||||
|
||||
// then
|
||||
assertEquals(50, point.getAltitude(), 0.01);
|
||||
@@ -64,9 +66,10 @@ public class ChartPointTest {
|
||||
public void create_sensorNotAvailable() {
|
||||
// given
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
|
||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
|
||||
.setAltitude(Altitude.EGM2008.of(50));
|
||||
// when
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), Altitude.EGM2008.of(50), false, UnitSystem.METRIC);
|
||||
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
||||
|
||||
// then
|
||||
assertNull(point.getHeartRate());
|
||||
@@ -77,15 +80,16 @@ public class ChartPointTest {
|
||||
@Test
|
||||
public void create_sensorAvailable() {
|
||||
// given
|
||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
|
||||
trackPoint.setHeartRate(100f);
|
||||
trackPoint.setCadence(101f);
|
||||
trackPoint.setPower(102f);
|
||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
|
||||
.setAltitude(Altitude.EGM2008.of(50))
|
||||
.setHeartRate(100f)
|
||||
.setCadence(101f)
|
||||
.setPower(102f);
|
||||
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
|
||||
// when
|
||||
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), Altitude.EGM2008.of(50), false, UnitSystem.METRIC);
|
||||
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
||||
|
||||
// then
|
||||
assertEquals(100.0, point.getHeartRate(), 0.01);
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
*/
|
||||
package de.dennisguse.opentracks.chart;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@@ -25,10 +29,6 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests {@link ChartValueSeries}.
|
||||
*
|
||||
@@ -47,6 +47,7 @@ public class ChartValueSeriesTest {
|
||||
new int[]{1, 100, 1000},
|
||||
R.string.description_altitude_metric,
|
||||
R.string.description_altitude_imperial,
|
||||
R.string.description_altitude_imperial,
|
||||
R.color.chart_altitude_fill,
|
||||
R.color.chart_altitude_border,
|
||||
15,
|
||||
|
||||
@@ -7,7 +7,6 @@ import android.util.Pair;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
@@ -24,10 +23,7 @@ public class ShareContentProviderTest {
|
||||
|
||||
@Test
|
||||
public void testCreateAndParseURI_valid() {
|
||||
Set<Track.Id> trackIds = new HashSet<>();
|
||||
trackIds.add(new Track.Id(1));
|
||||
trackIds.add(new Track.Id(3));
|
||||
trackIds.add(new Track.Id(5));
|
||||
Set<Track.Id> trackIds = Set.of(new Track.Id(1), new Track.Id(3), new Track.Id(5));
|
||||
|
||||
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(trackIds, "filename.suffix", TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA);
|
||||
|
||||
@@ -36,9 +32,7 @@ public class ShareContentProviderTest {
|
||||
|
||||
@Test
|
||||
public void testCreateURIescapeFilename() {
|
||||
Set<Track.Id> trackIds = new HashSet<>();
|
||||
trackIds.add(new Track.Id(1));
|
||||
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(trackIds, "../../&1=1", TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA);
|
||||
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(Set.of(new Track.Id(1)), "../../&1=1", TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA);
|
||||
|
||||
assertEquals(Uri.parse("content://de.dennisguse.opentracks.debug.content/tracks/KML_WITH_TRACKDETAIL_AND_SENSORDATA/1/..%2F..%2F%261%3D1"), shareURIandMIME.first);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import junit.framework.TestCase;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
@@ -105,7 +106,7 @@ public class TrackSelectionTest extends TestCase {
|
||||
long oneDay = 24 * 60 * 60 * 1000;
|
||||
TrackSelection filterWrong1 = new TrackSelection().addDateRange(instant, null);
|
||||
TrackSelection filterWrong2 = new TrackSelection().addDateRange(null, instant);
|
||||
TrackSelection filterOk = new TrackSelection().addDateRange(instant, instant.plusMillis(oneDay));
|
||||
TrackSelection filterOk = new TrackSelection().addDateRange(instant, instant.plus(Duration.ofDays(1)));
|
||||
|
||||
// when
|
||||
SelectionData selectionWrong1 = filterWrong1.buildSelection();
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
|
||||
//TODO Parametrized tests
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DistanceFormatterTest {
|
||||
|
||||
@@ -42,4 +43,17 @@ public class DistanceFormatterTest {
|
||||
// A small number in imperial
|
||||
assertEquals("328.08 ft", formatter.formatDistance(Distance.of(100)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatDistance_nautical() {
|
||||
DistanceFormatter formatter = DistanceFormatter.Builder()
|
||||
.setDecimalCount(2)
|
||||
.setUnit(UnitSystem.NAUTICAL_IMPERIAL)
|
||||
.build(context);
|
||||
|
||||
// A large number in nautical
|
||||
assertEquals("2.70 NM", formatter.formatDistance(Distance.of(5000)));
|
||||
// A small number in nautical
|
||||
assertEquals("328.08 ft", formatter.formatDistance(Distance.of(100)));
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
|
||||
//TODO Use make parametrized tests?
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SpeedFormatterTest {
|
||||
|
||||
@@ -39,12 +40,22 @@ public class SpeedFormatterTest {
|
||||
.setReportSpeedOrPace(false)
|
||||
.build(context);
|
||||
|
||||
// assertEquals("TODO", formatter.getSpeedParts(Speed.of(3.34)).first);
|
||||
// assertEquals("TODO", formatter.getSpeedParts(Speed.of(3.33)).first);
|
||||
|
||||
assertEquals("8:02", formatter.getSpeedParts(Speed.of(3.34)).first);
|
||||
assertEquals("min/mi", formatter.getSpeedParts(Speed.zero()).second);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSpeedParts_pace_nautical() {
|
||||
SpeedFormatter formatter = SpeedFormatter.Builder()
|
||||
.setDecimalCount(2)
|
||||
.setUnit(UnitSystem.NAUTICAL_IMPERIAL)
|
||||
.setReportSpeedOrPace(false)
|
||||
.build(context);
|
||||
|
||||
assertEquals("9:14", formatter.getSpeedParts(Speed.of(3.34)).first);
|
||||
assertEquals("min/NM", formatter.getSpeedParts(Speed.zero()).second);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSpeedParts_speed_metric() {
|
||||
SpeedFormatter formatter = SpeedFormatter.Builder()
|
||||
@@ -69,6 +80,18 @@ public class SpeedFormatterTest {
|
||||
assertEquals("mph", formatter.getSpeedParts(Speed.zero()).second);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSpeedParts_speed_nautical() {
|
||||
SpeedFormatter formatter = SpeedFormatter.Builder()
|
||||
.setDecimalCount(2)
|
||||
.setUnit(UnitSystem.NAUTICAL_IMPERIAL)
|
||||
.setReportSpeedOrPace(true)
|
||||
.build(context);
|
||||
|
||||
assertEquals("6.5", formatter.getSpeedParts(Speed.of(3.34)).first);
|
||||
assertEquals("kn", formatter.getSpeedParts(Speed.zero()).second);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatSpeed() {
|
||||
SpeedFormatter formatter = SpeedFormatter.Builder()
|
||||
|
||||
+12
-10
@@ -27,21 +27,23 @@ public class KmlTrackExporterTest {
|
||||
*/
|
||||
@Test
|
||||
public void writeCloseSegment_only_write_sensordata_if_present() {
|
||||
String expected = "<when>1970-01-01T00:00:00Z</when>\n" +
|
||||
"<gx:coord/>\n" +
|
||||
"<when>1970-01-01T01:00:00+01:00</when>\n" +
|
||||
"<gx:coord/>\n" +
|
||||
"<ExtendedData>\n" +
|
||||
"<SchemaData schemaUrl=\"#schema\">\n" +
|
||||
"</SchemaData>\n" +
|
||||
"</ExtendedData>\n" +
|
||||
"</gx:Track>\n";
|
||||
String expected = """
|
||||
<when>1970-01-01T00:00:00Z</when>
|
||||
<coord/>
|
||||
<when>1970-01-01T01:00:00+01:00</when>
|
||||
<coord/>
|
||||
<ExtendedData>
|
||||
<SchemaData schemaUrl="#schema">
|
||||
</SchemaData>
|
||||
</ExtendedData>
|
||||
</Track>
|
||||
""";
|
||||
|
||||
// given
|
||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0));
|
||||
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
KMLTrackExporter kmlTrackWriter = (KMLTrackExporter) TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
||||
KMLTrackExporter kmlTrackWriter = (KMLTrackExporter) TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, null);
|
||||
kmlTrackWriter.prepare(outputStream);
|
||||
|
||||
kmlTrackWriter.writeTrackPoint(ZoneOffset.UTC, trackPoint);
|
||||
|
||||
+73
-69
@@ -12,19 +12,16 @@ import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -61,7 +58,8 @@ import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
||||
import de.dennisguse.opentracks.sensors.AltitudeSumManager;
|
||||
import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||
@@ -72,6 +70,9 @@ import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
/**
|
||||
* Export a track to {@link TrackFileFormat} and verify that the import is identical.
|
||||
* <p>
|
||||
* Note: those tests are affected by {@link de.dennisguse.opentracks.sensors.sensorData.SensorData}.isRecent().
|
||||
* If the test device is too slow (like in a CI) these are likely to fail as the sensor data will be omitted from actual.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExportImportTest {
|
||||
@@ -81,9 +82,6 @@ public class ExportImportTest {
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
//For csv_export_only() as we the timezone is hardcoded in the expectation.
|
||||
@Rule
|
||||
public TimezoneRule timezoneRule = new TimezoneRule(TimeZone.getTimeZone("Europe/Berlin"));
|
||||
@@ -155,27 +153,33 @@ public class ExportImportTest {
|
||||
mockAltitudeChange(trackPointCreator, 1);
|
||||
mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f);
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:05Z");
|
||||
mockBLESensorData(trackPointCreator, 5f, Distance.of(2), 66f, 3f, 50f); // Distance will be added to next TrackPoint
|
||||
trackPointCreator.setClock("2020-02-02T02:02:14Z");
|
||||
mockBLESensorData(trackPointCreator, 15f, null, 67f, 3f, 50f);
|
||||
trackPointCreator.setClock("2020-02-02T02:02:15Z");
|
||||
mockBLESensorData(trackPointCreator, null, null, 68f, 3f, 50f);
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:05Z", 3, 14.001, 10, 13, 15, 10, 0);
|
||||
trackPointCreator.setClock("2020-02-02T02:02:16Z");
|
||||
mockBLESensorData(trackPointCreator, 5f, Distance.of(2), 69f, 3f, 50f); // Distance will be added to next TrackPoint
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3, 14.001, 10, 13, 15, 10, 0);
|
||||
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:06Z");
|
||||
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator));
|
||||
service.pauseCurrentTrack();
|
||||
trackPointCreator.setClock("2020-02-02T02:02:18Z");
|
||||
BluetoothRemoteSensorManager mockRemoteSensorManager = Mockito.mock(BluetoothRemoteSensorManager.class);
|
||||
trackPointCreator.getSensorManager().setBluetoothSensorManager(mockRemoteSensorManager);
|
||||
service.endCurrentTrack();
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:20Z");
|
||||
service.resumeCurrentTrack();
|
||||
trackPointCreator.setClock("2020-02-02T02:03:20Z");
|
||||
service.resumeTrack(trackId);
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:21Z", 3, 14.002, 10, 13, 15, 10, 0);
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:21Z", 3, 14.002, 10, 13, 15, 10, 0);
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:22Z", 3, 16, 10, 13, 15, 10, 0);
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:22Z", 3, 16, 10, 13, 15, 10, 0);
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:23Z", 3, 16.001, 10, 27, 15, 10, 0);
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:23Z", 3, 16.001, 10, 27, 15, 10, 0);
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:24Z");
|
||||
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator));
|
||||
trackPointCreator.setClock("2020-02-02T02:03:24Z");
|
||||
trackPointCreator.getSensorManager().setBluetoothSensorManager(mockRemoteSensorManager);
|
||||
service.endCurrentTrack();
|
||||
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
@@ -187,7 +191,7 @@ public class ExportImportTest {
|
||||
track = contentProviderUtils.getTrack(trackId);
|
||||
trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
markers = contentProviderUtils.getMarkers(trackId);
|
||||
assertEquals(10, trackPoints.size());
|
||||
assertEquals(12, trackPoints.size());
|
||||
assertEquals(2, markers.size());
|
||||
}
|
||||
|
||||
@@ -200,7 +204,7 @@ public class ExportImportTest {
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
||||
TrackExporter trackExporter = TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, contentProviderUtils);
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
@@ -231,29 +235,29 @@ public class ExportImportTest {
|
||||
// Time
|
||||
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:02Z"), importedTrackStatistics.getStartTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:24Z"), importedTrackStatistics.getStopTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:03:24Z"), importedTrackStatistics.getStopTime());
|
||||
|
||||
TrackStatistics originalTrackStatistics = track.getTrackStatistics();
|
||||
|
||||
assertEquals(originalTrackStatistics.getTotalTime(), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(7), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(19), importedTrackStatistics.getTotalTime());
|
||||
|
||||
assertEquals(originalTrackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(3), importedTrackStatistics.getMovingTime());
|
||||
|
||||
// Distance
|
||||
assertEquals(originalTrackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance());
|
||||
assertEquals(123.16, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
assertEquals(125.16, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
|
||||
// Speed
|
||||
assertEquals(originalTrackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed());
|
||||
assertEquals(41.05, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(41.72, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
|
||||
assertEquals(originalTrackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed());
|
||||
assertEquals(17.59, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(6.58, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
|
||||
assertEquals(originalTrackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed());
|
||||
assertEquals(41.05, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(41.72, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
|
||||
// Altitude
|
||||
assertEquals(originalTrackStatistics.getMinAltitude(), importedTrackStatistics.getMinAltitude(), 0.01);
|
||||
@@ -283,7 +287,7 @@ public class ExportImportTest {
|
||||
editor.commit();
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
||||
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, contentProviderUtils);
|
||||
|
||||
// when
|
||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
@@ -306,7 +310,7 @@ public class ExportImportTest {
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context, contentProviderUtils);
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
@@ -345,19 +349,19 @@ public class ExportImportTest {
|
||||
.setAltitudeLoss(1f)
|
||||
.setAltitudeGain(1f)
|
||||
.setHorizontalAccuracy(Distance.of(10)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:05Z"))
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:17Z"))
|
||||
.setLatitude(3)
|
||||
.setLongitude(14.001)
|
||||
.setAltitude(10)
|
||||
.setSpeed(Speed.of(5))
|
||||
.setAltitudeLoss(1f)
|
||||
.setAltitudeGain(1f)
|
||||
.setSensorDistance(Distance.of(12))
|
||||
.setHeartRate(66f)
|
||||
.setSensorDistance(Distance.of(14))
|
||||
.setHeartRate(69)
|
||||
.setPower(50f)
|
||||
.setCadence(3f)
|
||||
.setHorizontalAccuracy(Distance.of(10)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:21Z"))
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:03:21Z"))
|
||||
.setLatitude(3)
|
||||
.setLongitude(14.002)
|
||||
.setAltitude(10)
|
||||
@@ -365,7 +369,7 @@ public class ExportImportTest {
|
||||
.setAltitudeGain(0f)
|
||||
.setSpeed(Speed.of(15))
|
||||
.setHorizontalAccuracy(Distance.of(10)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:22Z"))
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:03:22Z"))
|
||||
.setLatitude(3)
|
||||
.setLongitude(16)
|
||||
.setAltitude(10)
|
||||
@@ -373,7 +377,7 @@ public class ExportImportTest {
|
||||
.setAltitudeGain(0f)
|
||||
.setSpeed(Speed.of(15))
|
||||
.setHorizontalAccuracy(Distance.of(10)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:23Z"))
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:03:23Z"))
|
||||
.setLatitude(3)
|
||||
.setLongitude(16.001)
|
||||
.setAltitude(10)
|
||||
@@ -390,18 +394,18 @@ public class ExportImportTest {
|
||||
// Time
|
||||
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:03Z"), importedTrackStatistics.getStartTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:23Z"), importedTrackStatistics.getStopTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:03:23Z"), importedTrackStatistics.getStopTime());
|
||||
|
||||
assertEquals(Duration.ofSeconds(3), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(3), importedTrackStatistics.getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(15), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(15), importedTrackStatistics.getMovingTime());
|
||||
|
||||
// Distance
|
||||
assertEquals(123.16, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
assertEquals(125.16, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
|
||||
// Speed
|
||||
assertEquals(41.05, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(41.05, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(41.05, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
assertEquals(8.34, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(8.34, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(8.34, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
|
||||
// Altitude
|
||||
assertEquals(10, importedTrackStatistics.getMinAltitude(), 0.01);
|
||||
@@ -424,7 +428,7 @@ public class ExportImportTest {
|
||||
editor.commit();
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context, contentProviderUtils);
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
@@ -441,7 +445,6 @@ public class ExportImportTest {
|
||||
assertNull(trackImported);
|
||||
}
|
||||
|
||||
@Ignore(value = "TODO Fails on CI; works on API24 and API30 locally")
|
||||
@LargeTest
|
||||
@Test
|
||||
public void csv_export_only() throws TimeoutException, IOException {
|
||||
@@ -450,7 +453,7 @@ public class ExportImportTest {
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.CSV.createTrackExporter(context);
|
||||
TrackExporter trackExporter = TrackFileFormat.CSV.createTrackExporter(context, contentProviderUtils);
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
@@ -458,7 +461,7 @@ public class ExportImportTest {
|
||||
contentProviderUtils.deleteTrack(context, trackId);
|
||||
|
||||
// then
|
||||
InputStream expected = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.csv_export);
|
||||
InputStream expected = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.csv_export);
|
||||
String expectedText = new BufferedReader(new InputStreamReader(expected, StandardCharsets.UTF_8))
|
||||
.lines()
|
||||
.collect(Collectors.joining("\n"));
|
||||
@@ -492,37 +495,38 @@ public class ExportImportTest {
|
||||
}
|
||||
|
||||
private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) {
|
||||
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator) {
|
||||
@Override
|
||||
public SensorDataSet fill(@NonNull TrackPoint trackPoint) {
|
||||
SensorDataSet sensorDataSet = new SensorDataSet();
|
||||
sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power)));
|
||||
sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", HeartRate.of(heartRate)));
|
||||
BluetoothRemoteSensorManager remoteSensorManager = Mockito.mock(BluetoothRemoteSensorManager.class);
|
||||
Mockito.when(remoteSensorManager.fill(Mockito.any())).thenAnswer(invocation -> {
|
||||
TrackPoint trackPoint = invocation.getArgument(0);
|
||||
|
||||
SensorDataCycling.CyclingCadence cyclingCadence = Mockito.mock(SensorDataCycling.CyclingCadence.class);
|
||||
Mockito.when(cyclingCadence.hasValue()).thenReturn(true);
|
||||
Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence));
|
||||
sensorDataSet.set(cyclingCadence);
|
||||
SensorDataSet sensorDataSet = new SensorDataSet();
|
||||
sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power)));
|
||||
sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", HeartRate.of(heartRate)));
|
||||
|
||||
if (distance != null && speed != null) {
|
||||
SensorDataCycling.DistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCycling.DistanceSpeed.Data.class);
|
||||
Mockito.when(distanceSpeedData.getDistanceOverall()).thenReturn(distance);
|
||||
Mockito.when(distanceSpeedData.getSpeed()).thenReturn(Speed.of(speed));
|
||||
SensorDataCycling.DistanceSpeed distanceSpeed = Mockito.mock(SensorDataCycling.DistanceSpeed.class);
|
||||
Mockito.when(distanceSpeed.hasValue()).thenReturn(true);
|
||||
Mockito.when(distanceSpeed.getValue()).thenReturn(distanceSpeedData);
|
||||
sensorDataSet.set(distanceSpeed);
|
||||
}
|
||||
SensorDataCyclingCadence cyclingCadence = Mockito.mock(SensorDataCyclingCadence.class);
|
||||
Mockito.when(cyclingCadence.hasValue()).thenReturn(true);
|
||||
Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence));
|
||||
sensorDataSet.set(cyclingCadence);
|
||||
|
||||
sensorDataSet.fillTrackPoint(trackPoint);
|
||||
return sensorDataSet;
|
||||
if (distance != null && speed != null) {
|
||||
SensorDataCyclingDistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCyclingDistanceSpeed.Data.class);
|
||||
Mockito.when(distanceSpeedData.getDistanceOverall()).thenReturn(distance);
|
||||
Mockito.when(distanceSpeedData.getSpeed()).thenReturn(Speed.of(speed));
|
||||
SensorDataCyclingDistanceSpeed distanceSpeed = Mockito.mock(SensorDataCyclingDistanceSpeed.class);
|
||||
Mockito.when(distanceSpeed.hasValue()).thenReturn(true);
|
||||
Mockito.when(distanceSpeed.getValue()).thenReturn(distanceSpeedData);
|
||||
sensorDataSet.set(distanceSpeed);
|
||||
}
|
||||
|
||||
sensorDataSet.fillTrackPoint(trackPoint);
|
||||
return sensorDataSet;
|
||||
});
|
||||
trackPointCreator.getSensorManager().setBluetoothSensorManager(remoteSensorManager);
|
||||
trackPointCreator.onChange(new SensorDataSet());
|
||||
}
|
||||
|
||||
private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) {
|
||||
AltitudeSumManager altitudeSumManager = trackPointCreator.getAltitudeSumManager();
|
||||
AltitudeSumManager altitudeSumManager = trackPointCreator.getSensorManager().getAltitudeSumManager();
|
||||
altitudeSumManager.setAltitudeGain_m(altitudeGain);
|
||||
altitudeSumManager.setAltitudeLoss_m(altitudeGain);
|
||||
}
|
||||
|
||||
+39
-5
@@ -15,11 +15,16 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
@@ -27,13 +32,15 @@ import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
/**
|
||||
* Test that legacy KML/GPX formats can still be imported.
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
public class GPXImportTest {
|
||||
public class GPXTrackImporterTest {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
||||
@@ -62,7 +69,7 @@ public class GPXImportTest {
|
||||
public void gpx_with_pause_resume() throws IOException {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new GpxTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.legacy_gpx_pause_resume);
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.legacy_gpx_pause_resume);
|
||||
|
||||
// when
|
||||
// 1. import
|
||||
@@ -116,7 +123,7 @@ public class GPXImportTest {
|
||||
public void gpx_without_speed() throws IOException {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new GpxTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.gpx11_without_speed);
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx11_without_speed);
|
||||
|
||||
// when
|
||||
// 1. import
|
||||
@@ -164,7 +171,7 @@ public class GPXImportTest {
|
||||
public void gpx_speed_no_namespace() throws IOException {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new GpxTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.gpx11_with_speed_no_namespace);
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx11_with_speed_no_namespace);
|
||||
|
||||
// when
|
||||
// 1. import
|
||||
@@ -181,7 +188,7 @@ public class GPXImportTest {
|
||||
|
||||
// 3. trackstatistics
|
||||
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
|
||||
assertEquals(4, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(4.0, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(Duration.ofSeconds(101), trackStatistics.getMovingTime());
|
||||
|
||||
// 4. trackpoints
|
||||
@@ -208,4 +215,31 @@ public class GPXImportTest {
|
||||
.setSpeed(Speed.of(3))
|
||||
), importedTrackPoints);
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void importExportTest_timezone() throws IOException {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new GpxTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx_timezone);
|
||||
InputStream inputStreamExpected = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx_timezone);
|
||||
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
// when
|
||||
// 1. import
|
||||
importTrackId = importer.importFile(inputStream).get(0);
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context, contentProviderUtils);
|
||||
trackExporter.writeTrack(importedTrack, outputStream);
|
||||
|
||||
// then
|
||||
String expected = new BufferedReader(
|
||||
new InputStreamReader(inputStreamExpected, StandardCharsets.UTF_8))
|
||||
.lines()
|
||||
.collect(Collectors.joining("\n")) + "\n";
|
||||
|
||||
assertEquals(expected, outputStream.toString()); //TODO inputStream.readAllBytes() ?
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -31,7 +31,7 @@ import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
* Test that legacy KML/GPX formats can still be imported.
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
public class KMLImportTest {
|
||||
public class KMLTrackImporterTest {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
||||
@@ -60,7 +60,7 @@ public class KMLImportTest {
|
||||
public void kml22_order_location_and_when() throws IOException {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new KmlTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.kml22_order_location_and_when);
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.kml22_order_location_and_when);
|
||||
|
||||
// when
|
||||
// 1. import
|
||||
@@ -109,7 +109,7 @@ public class KMLImportTest {
|
||||
public void kml22_time_decreases() throws IOException {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new KmlTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.kml22_time_decreases);
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.kml22_time_decreases);
|
||||
|
||||
// when
|
||||
// 1. import
|
||||
@@ -149,7 +149,7 @@ public class KMLImportTest {
|
||||
public void kml22_with_statistics_marker() throws IOException {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new KmlTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.legacy_kml22_statistics_marker);
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.legacy_kml22_statistics_marker);
|
||||
|
||||
// when
|
||||
// 1. import
|
||||
@@ -223,7 +223,7 @@ public class KMLImportTest {
|
||||
public void kml_without_locations() throws IOException {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new KmlTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.kml22_without_locations);
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.kml22_without_locations);
|
||||
|
||||
// when
|
||||
importTrackId = importer.importFile(inputStream).get(0);
|
||||
@@ -234,7 +234,7 @@ public class KMLImportTest {
|
||||
public void kml_when_locations_different() throws IOException {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new KmlTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.kml22_when_locations_different);
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.kml22_when_locations_different);
|
||||
|
||||
// when
|
||||
importTrackId = importer.importFile(inputStream).get(0);
|
||||
@@ -9,8 +9,10 @@ import org.junit.Test;
|
||||
|
||||
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.Speed;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning;
|
||||
|
||||
public class BluetoothUtilsTest {
|
||||
@@ -18,36 +20,36 @@ public class BluetoothUtilsTest {
|
||||
@Test
|
||||
public void parseHeartRate_uint8() {
|
||||
// given
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEART_RATE_SERVICE_UUID, 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.getServiceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x02, 0x3C});
|
||||
|
||||
// when
|
||||
int heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
||||
HeartRate heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(60, heartRate);
|
||||
assertEquals(HeartRate.of(60), heartRate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseHeartRate_uint16() {
|
||||
// given
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEART_RATE_SERVICE_UUID, 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.getServiceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x01, 0x01, 0x01});
|
||||
|
||||
// when
|
||||
int heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
||||
HeartRate heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(257, heartRate);
|
||||
assertEquals(HeartRate.of(257), heartRate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCyclingSpeedCadence_crankOnly() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID, 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x02, (byte) 0xC8, 0x00, 0x00, 0x00, 0x06, (byte) 0x99});
|
||||
|
||||
// when
|
||||
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
|
||||
SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
|
||||
|
||||
// then
|
||||
assertNull(sensor.getDistanceSpeed());
|
||||
@@ -56,11 +58,11 @@ public class BluetoothUtilsTest {
|
||||
|
||||
@Test
|
||||
public void parseCyclingSpeedCadence_wheelOnly() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID, 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x01, (byte) 0xFF, (byte) 0xFF, 0, 1, 0x45, (byte) 0x99});
|
||||
|
||||
// when
|
||||
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
|
||||
SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(65535 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount());
|
||||
@@ -69,11 +71,11 @@ public class BluetoothUtilsTest {
|
||||
|
||||
@Test
|
||||
public void parseCyclingSpeedCadence_crankWheel() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID, 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x03, (byte) 0xC8, 0x00, 0x00, 0x01, 0x06, (byte) 0x99, (byte) 0xE1, 0x00, 0x45, (byte) 0x99});
|
||||
|
||||
// when
|
||||
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
|
||||
SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(200 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount());
|
||||
@@ -82,19 +84,34 @@ public class BluetoothUtilsTest {
|
||||
|
||||
@Test
|
||||
public void parseCyclingPower_power() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER_MEASUREMENT_CHAR_UUID, 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.getServiceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0, 0, 40, 0});
|
||||
|
||||
// when
|
||||
int power_w = BluetoothUtils.parseCyclingPower(characteristic);
|
||||
SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(40, power_w);
|
||||
assertEquals(40, powerCadence.getPower().getValue().getW(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCyclingPower_power_with_cadence() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.getServiceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x2C, 0x00, 0x00, 0x00, (byte) 0x9F, 0x00, 0x0C, 0x00, (byte) 0xE5, 0x42});
|
||||
|
||||
// when
|
||||
SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(0, powerCadence.getPower().getValue().getW(), 0.01);
|
||||
|
||||
assertEquals(12, powerCadence.getCadence().getCrankRevolutionsCount());
|
||||
assertEquals(17125, powerCadence.getCadence().getCrankRevolutionsTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseRunningSpeedAndCadence_with_distance() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.RUNNING_RUNNING_SPEED_CADENCE_CHAR_UUID, 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.RUNNING_SPEED_CADENCE.getServiceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{2, 0, 5, 80, (byte) 0xFF, (byte) 0xFF, 0, 1});
|
||||
|
||||
// when
|
||||
|
||||
+24
-24
@@ -21,8 +21,8 @@ public class SensorDataCyclingTest {
|
||||
@Test
|
||||
public void compute_cadence_1() {
|
||||
// given
|
||||
SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 1024); // 1s
|
||||
SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 2, 2048); // 2s
|
||||
SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 1024); // 1s
|
||||
SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 2048); // 2s
|
||||
|
||||
// when
|
||||
current.compute(previous);
|
||||
@@ -34,8 +34,8 @@ public class SensorDataCyclingTest {
|
||||
@Test
|
||||
public void compute_cadence_2() {
|
||||
// given
|
||||
SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 6184);
|
||||
SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 2, 8016);
|
||||
SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 6184);
|
||||
SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 8016);
|
||||
|
||||
// when
|
||||
current.compute(previous);
|
||||
@@ -47,8 +47,8 @@ public class SensorDataCyclingTest {
|
||||
@Test
|
||||
public void compute_cadence_sameCount() {
|
||||
// given
|
||||
SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 1024);
|
||||
SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 2048);
|
||||
SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 1024);
|
||||
SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 2048);
|
||||
|
||||
// when
|
||||
current.compute(previous);
|
||||
@@ -61,8 +61,8 @@ public class SensorDataCyclingTest {
|
||||
@Test
|
||||
public void compute_cadence_sameTime() {
|
||||
// given
|
||||
SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 1024);
|
||||
SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 2, 1024);
|
||||
SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 1024);
|
||||
SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 1024);
|
||||
|
||||
// when
|
||||
current.compute(previous);
|
||||
@@ -74,8 +74,8 @@ public class SensorDataCyclingTest {
|
||||
@Test
|
||||
public void compute_cadence_rollOverTime() {
|
||||
// given
|
||||
SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, UintUtils.UINT16_MAX - 1024);
|
||||
SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 2, 0);
|
||||
SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, UintUtils.UINT16_MAX - 1024);
|
||||
SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 0);
|
||||
|
||||
// when
|
||||
current.compute(previous);
|
||||
@@ -89,8 +89,8 @@ public class SensorDataCyclingTest {
|
||||
@Deprecated
|
||||
public void compute_cadence_rollOverCount() {
|
||||
// given
|
||||
SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
|
||||
SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 0, 2048);
|
||||
SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
|
||||
SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 0, 2048);
|
||||
|
||||
// when
|
||||
current.compute(previous);
|
||||
@@ -102,8 +102,8 @@ public class SensorDataCyclingTest {
|
||||
@Test
|
||||
public void compute_cadence_overflow() {
|
||||
// given
|
||||
SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
|
||||
SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 0, 2048);
|
||||
SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
|
||||
SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 0, 2048);
|
||||
|
||||
// when
|
||||
current.compute(previous);
|
||||
@@ -115,8 +115,8 @@ public class SensorDataCyclingTest {
|
||||
@Test
|
||||
public void compute_speed() {
|
||||
// given
|
||||
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 1, 6184);
|
||||
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 2, 8016);
|
||||
SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 1, 6184);
|
||||
SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 2, 8016);
|
||||
|
||||
// when
|
||||
current.compute(previous, Distance.ofMM(2150));
|
||||
@@ -131,8 +131,8 @@ public class SensorDataCyclingTest {
|
||||
@Deprecated
|
||||
public void compute_speed_rollOverCount() {
|
||||
// given
|
||||
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
|
||||
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048);
|
||||
SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
|
||||
SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 0, 2048);
|
||||
|
||||
// when
|
||||
current.compute(previous, Distance.ofMM(2000));
|
||||
@@ -145,8 +145,8 @@ public class SensorDataCyclingTest {
|
||||
@Test
|
||||
public void compute_speed_overflow() {
|
||||
// given
|
||||
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
|
||||
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048);
|
||||
SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
|
||||
SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 0, 2048);
|
||||
|
||||
// when
|
||||
current.compute(previous, Distance.ofMM(2000));
|
||||
@@ -158,8 +158,8 @@ public class SensorDataCyclingTest {
|
||||
@Test
|
||||
public void equals_speed_with_no_data() {
|
||||
// given
|
||||
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress");
|
||||
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048);
|
||||
SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress");
|
||||
SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 0, 2048);
|
||||
|
||||
// when
|
||||
previous.toString();
|
||||
@@ -172,8 +172,8 @@ public class SensorDataCyclingTest {
|
||||
@Test
|
||||
public void equals_cadence_with_no_data() {
|
||||
// given
|
||||
SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress");
|
||||
SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 0, 2048);
|
||||
SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress");
|
||||
SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 0, 2048);
|
||||
|
||||
// when
|
||||
previous.toString();
|
||||
|
||||
-5
@@ -29,7 +29,6 @@ import android.os.Looper;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.MediumTest;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import org.junit.After;
|
||||
@@ -57,9 +56,6 @@ public class TrackRecordingServiceTestMarker {
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
@@ -128,7 +124,6 @@ public class TrackRecordingServiceTestMarker {
|
||||
public void recording_GPSfix_createsMarker() {
|
||||
// given
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.stopGPS();
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:02Z");
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
|
||||
+62
-27
@@ -11,7 +11,6 @@ import androidx.annotation.NonNull;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.MediumTest;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import org.junit.After;
|
||||
@@ -31,12 +30,14 @@ import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.io.file.importer.TrackPointAssert;
|
||||
import de.dennisguse.opentracks.sensors.AltitudeSumManager;
|
||||
import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
@@ -44,6 +45,8 @@ import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
/**
|
||||
* Tests insert location.
|
||||
* Note: on API23, the TrackRecordingService may not be stopped properly before the next test.
|
||||
* So, if something fails, subsequent tests may be affected.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TrackRecordingServiceTestRecording {
|
||||
@@ -51,9 +54,6 @@ public class TrackRecordingServiceTestRecording {
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
@@ -93,7 +93,6 @@ public class TrackRecordingServiceTestRecording {
|
||||
PreferencesUtils.setString(R.string.idle_speed_key, R.string.idle_speed_default);
|
||||
|
||||
service = startService();
|
||||
service.getTrackPointCreator().stopGPS();
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -115,8 +114,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
String startTime = "2020-02-02T02:02:02Z";
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.stopGPS();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, startTime, 0, 0, 0, 0, null, null)
|
||||
@@ -153,13 +151,12 @@ public class TrackRecordingServiceTestRecording {
|
||||
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.stopGPS();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String pauseTime = "2020-02-02T02:02:03Z";
|
||||
trackPointCreator.setClock(pauseTime);
|
||||
service.pauseCurrentTrack();
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, pauseTime, 0, 1, 0, 0, 0f, 0f)
|
||||
@@ -200,12 +197,11 @@ public class TrackRecordingServiceTestRecording {
|
||||
String starTime = "2020-02-02T02:02:02Z";
|
||||
trackPointCreator.setClock(starTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.stopGPS();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
String pauseTime = "2020-02-02T02:02:03Z";
|
||||
trackPointCreator.setClock(pauseTime);
|
||||
service.pauseCurrentTrack();
|
||||
service.endCurrentTrack();
|
||||
|
||||
// when
|
||||
trackPointCreator.setClock("2020-02-02T02:02:04Z");
|
||||
@@ -233,8 +229,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.stopGPS();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
String stopTime = "2020-02-02T02:02:03Z";
|
||||
trackPointCreator.setClock(stopTime);
|
||||
@@ -244,8 +239,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
String resumeTime = "2020-02-02T02:02:04Z";
|
||||
trackPointCreator.setClock(resumeTime);
|
||||
service.resumeTrack(trackId);
|
||||
trackPointCreator.stopGPS();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// then
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
@@ -257,6 +251,47 @@ public class TrackRecordingServiceTestRecording {
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_blesensor_only_no_distance() {
|
||||
// given
|
||||
String startTime = "2020-02-02T02:02:02Z";
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
BluetoothRemoteSensorManager remoteSensorManager = trackPointCreator.getSensorManager().getBluetoothSensorManager();
|
||||
|
||||
// when
|
||||
String sensor1 = "2020-02-02T02:02:03Z";
|
||||
trackPointCreator.setClock(sensor1);
|
||||
|
||||
remoteSensorManager.onChanged(new SensorDataHeartRate("", "", HeartRate.of(5))); //Should be ignored
|
||||
|
||||
String sensor3 = "2020-02-02T02:02:13Z";
|
||||
trackPointCreator.setClock(sensor3);
|
||||
remoteSensorManager.onChanged(new SensorDataHeartRate("", "", HeartRate.of(7)));
|
||||
|
||||
String stopTime = "2020-02-02T02:02:15Z";
|
||||
trackPointCreator.setClock(stopTime);
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.SENSORPOINT, Instant.parse(sensor3))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setHeartRate(HeartRate.of(7)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setHeartRate(HeartRate.of(7))
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_gpsOnly_recordingDistance_above() {
|
||||
@@ -265,7 +300,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
|
||||
// when
|
||||
@@ -339,7 +374,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
@@ -404,7 +439,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
@@ -468,7 +503,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
@@ -532,7 +567,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
@@ -620,7 +655,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
@@ -670,7 +705,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
@@ -716,7 +751,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
@@ -780,8 +815,8 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
BluetoothRemoteSensorManager remoteSensorManager = trackPointCreator.getRemoteSensorManager();
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
BluetoothRemoteSensorManager remoteSensorManager = trackPointCreator.getSensorManager().getBluetoothSensorManager();
|
||||
|
||||
// when
|
||||
String sensor1 = "2020-02-02T02:02:03Z";
|
||||
|
||||
+19
-32
@@ -143,7 +143,7 @@ public class TrackRecordingServiceTestStateMachine {
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void recording_startPauseStopResume_no_data() throws InterruptedException {
|
||||
public void recording_startStopResume_no_data() throws InterruptedException {
|
||||
// given
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
@@ -153,31 +153,19 @@ public class TrackRecordingServiceTestStateMachine {
|
||||
|
||||
// then
|
||||
assertTrue(service.isRecording());
|
||||
assertEquals(new RecordingStatus(trackId, false), service.getRecordingStatusObservable().getValue());
|
||||
assertEquals(new RecordingStatus(trackId), service.getRecordingStatusObservable().getValue());
|
||||
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
||||
|
||||
service.endCurrentTrack();
|
||||
|
||||
// when
|
||||
service.pauseCurrentTrack();
|
||||
service.resumeTrack(trackId);
|
||||
Thread.sleep(1000);
|
||||
|
||||
// then
|
||||
assertTrue(service.isRecording());
|
||||
assertTrue(service.isPaused());
|
||||
assertEquals(new RecordingStatus(trackId, true), service.getRecordingStatusObservable().getValue());
|
||||
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue()); //TODO The GPS is kept in pause, right? Might be a bug.
|
||||
|
||||
|
||||
// when
|
||||
service.resumeCurrentTrack();
|
||||
Thread.sleep(1000);
|
||||
|
||||
// then
|
||||
assertTrue(service.isRecording());
|
||||
assertFalse(service.isPaused());
|
||||
assertEquals(new RecordingStatus(trackId, false), service.getRecordingStatusObservable().getValue());
|
||||
assertEquals(new RecordingStatus(trackId), service.getRecordingStatusObservable().getValue());
|
||||
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
||||
|
||||
@@ -188,7 +176,6 @@ public class TrackRecordingServiceTestStateMachine {
|
||||
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
assertFalse(service.isPaused());
|
||||
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
||||
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
||||
@@ -200,8 +187,7 @@ public class TrackRecordingServiceTestStateMachine {
|
||||
|
||||
// then
|
||||
assertTrue(service.isRecording());
|
||||
assertFalse(service.isPaused());
|
||||
assertEquals(new RecordingStatus(trackId, false), service.getRecordingStatusObservable().getValue());
|
||||
assertEquals(new RecordingStatus(trackId), service.getRecordingStatusObservable().getValue());
|
||||
Thread.sleep(1000); //TODO Figure out why we need to wait here until the update is happening
|
||||
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
||||
@@ -209,29 +195,31 @@ public class TrackRecordingServiceTestStateMachine {
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void invalidStateChanges() throws InterruptedException {
|
||||
public void cannotResume_non_existing_track() {
|
||||
// given
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
// when
|
||||
service.pauseCurrentTrack();
|
||||
Thread.sleep(2000);
|
||||
|
||||
// then
|
||||
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
||||
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
||||
|
||||
|
||||
// when
|
||||
service.resumeCurrentTrack();
|
||||
Thread.sleep(1000);
|
||||
service.resumeTrack(new Track.Id(-1));
|
||||
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
||||
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void cannotEnd_without_starting() throws InterruptedException {
|
||||
// given
|
||||
assertFalse(service.isRecording());
|
||||
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
||||
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
||||
|
||||
// when
|
||||
service.endCurrentTrack();
|
||||
@@ -257,8 +245,7 @@ public class TrackRecordingServiceTestStateMachine {
|
||||
|
||||
// then
|
||||
assertTrue(service.isRecording());
|
||||
assertFalse(service.isPaused());
|
||||
assertEquals(new RecordingStatus(trackId, false), service.getRecordingStatusObservable().getValue());
|
||||
assertEquals(new RecordingStatus(trackId), service.getRecordingStatusObservable().getValue());
|
||||
Thread.sleep(1000); //TODO Figure out why we need to wait here until the update is happening
|
||||
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
||||
|
||||
-11
@@ -1,15 +1,12 @@
|
||||
package de.dennisguse.opentracks.services;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.location.Location;
|
||||
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
@@ -22,14 +19,6 @@ public class TrackRecordingServiceTestUtils {
|
||||
public static void resetService(ServiceTestRule mServiceRule, Context context) throws TimeoutException {
|
||||
// Let's use default values.
|
||||
PreferencesUtils.clear();
|
||||
|
||||
// Reset service (if some previous test failed)
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)))
|
||||
.getService();
|
||||
|
||||
service.getTrackPointCreator().setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, service.getTrackPointCreator()));
|
||||
service.getTrackPointCreator().setClock(Clock.systemUTC());
|
||||
service.endCurrentTrack();
|
||||
}
|
||||
|
||||
static void sendGPSLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, long speed) {
|
||||
|
||||
-4
@@ -8,7 +8,6 @@ import android.os.Looper;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
@@ -31,9 +30,6 @@ public class VoiceAnnouncementManagerTest {
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
@BeforeClass
|
||||
public static void preSetUp() {
|
||||
// Prepare looper for Android's message queue
|
||||
|
||||
+96
-11
@@ -42,7 +42,13 @@ public class VoiceAnnouncementUtilsTest {
|
||||
@Before
|
||||
public void setUp() {
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
PreferencesUtils.setVoiceAnnounceHeartRate(false);
|
||||
|
||||
PreferencesUtils.setVoiceAnnounceLapHeartRate(false);
|
||||
PreferencesUtils.setVoiceAnnounceAverageHeartRate(false);
|
||||
PreferencesUtils.setVoiceAnnounceTotalDistance(true);
|
||||
PreferencesUtils.setVoiceAnnounceMovingTime(true);
|
||||
PreferencesUtils.setVoiceAnnounceAverageSpeedPace(true);
|
||||
PreferencesUtils.setVoiceAnnounceLapSpeedPace(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -58,7 +64,55 @@ public class VoiceAnnouncementUtilsTest {
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 20.00 kilometers. 1 hour 5 minutes 10 seconds. Speed 18.4 kilometers per hour.", announcement);
|
||||
assertEquals("Total distance 20.0 kilometers. 1 hour 5 minutes 10 seconds. Average moving speed 18.4 kilometers per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_metric_speed_rounding_check() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setTotalDistance(Distance.of(20000));
|
||||
stats.setTotalTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1).plusSeconds(1));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 20.0 kilometers. 1 hour 1 second. Average moving speed 20.0 kilometers per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_metric_distance_rounding_check() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setTotalDistance(Distance.of(19999));
|
||||
stats.setTotalTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 20.0 kilometers. 1 hour. Average moving speed 20.0 kilometers per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_metric_distance_rounding_check_two() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setTotalDistance(Distance.of(19900));
|
||||
stats.setTotalTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 19.9 kilometers. 1 hour. Average moving speed 19.9 kilometers per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -80,7 +134,7 @@ public class VoiceAnnouncementUtilsTest {
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour.", announcement);
|
||||
assertEquals("Total distance 14.2 kilometers. 16 minutes 39 seconds. Average moving speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -96,7 +150,7 @@ public class VoiceAnnouncementUtilsTest {
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, false, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 20.00 kilometers. 1 hour 5 minutes 10 seconds. Pace 3 minutes 15 seconds per kilometer.", announcement);
|
||||
assertEquals("Total distance 20.0 kilometers. 1 hour 5 minutes 10 seconds. Pace 3 minutes 15 seconds per kilometer.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -118,7 +172,7 @@ public class VoiceAnnouncementUtilsTest {
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, false, lastInterval, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Pace 1 minute 10 seconds per kilometer. Lap time 1 minute 10 seconds per kilometer.", announcement);
|
||||
assertEquals("Total distance 14.2 kilometers. 16 minutes 39 seconds. Pace 1 minute 10 seconds per kilometer. Lap time 1 minute 10 seconds per kilometer.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -134,7 +188,7 @@ public class VoiceAnnouncementUtilsTest {
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 12.43 miles. 1 hour 5 minutes 10 seconds. Speed 11.4 miles per hour.", announcement);
|
||||
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Average moving speed 11.4 miles per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -156,7 +210,7 @@ public class VoiceAnnouncementUtilsTest {
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, true, lastInterval, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 8.83 miles. 16 minutes 39 seconds. Speed 31.8 miles per hour. Lap speed 31.8 miles per hour.", announcement);
|
||||
assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Average moving speed 31.8 miles per hour. Lap speed 31.8 miles per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -172,7 +226,7 @@ public class VoiceAnnouncementUtilsTest {
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, false, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 12.43 miles. 1 hour 5 minutes 10 seconds. Pace 5 minutes 15 seconds per mile.", announcement);
|
||||
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Pace 5 minutes 15 seconds per mile.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -194,12 +248,13 @@ public class VoiceAnnouncementUtilsTest {
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, false, lastInterval, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 8.83 miles. 16 minutes 39 seconds. Pace 1 minute 53 seconds per mile. Lap time 1 minute 53 seconds per mile.", announcement);
|
||||
assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Pace 1 minute 53 seconds per mile. Lap time 1 minute 53 seconds per mile.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_heart_rate_and_sensor_statistics() {
|
||||
PreferencesUtils.setVoiceAnnounceHeartRate(true);
|
||||
PreferencesUtils.setVoiceAnnounceAverageHeartRate(true);
|
||||
PreferencesUtils.setVoiceAnnounceLapHeartRate(true);
|
||||
|
||||
int numberOfPoints = 1000;
|
||||
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
|
||||
@@ -219,6 +274,36 @@ public class VoiceAnnouncementUtilsTest {
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour. Average heart rate 180 bpm. Current heart rate 133 bpm.", announcement);
|
||||
assertEquals("Total distance 14.2 kilometers. 16 minutes 39 seconds. Average moving speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour. Average heart rate 180 bpm. Current heart rate 133 bpm.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_only_lap_heart_rate() {
|
||||
PreferencesUtils.setVoiceAnnounceLapHeartRate(true);
|
||||
PreferencesUtils.setVoiceAnnounceAverageHeartRate(false);
|
||||
PreferencesUtils.setVoiceAnnounceTotalDistance(false);
|
||||
PreferencesUtils.setVoiceAnnounceMovingTime(false);
|
||||
PreferencesUtils.setVoiceAnnounceAverageSpeedPace(false);
|
||||
PreferencesUtils.setVoiceAnnounceLapSpeedPace(false);
|
||||
|
||||
int numberOfPoints = 1000;
|
||||
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
|
||||
Track.Id trackId = trackWithStats.first;
|
||||
TrackStatistics stats = trackWithStats.second;
|
||||
IntervalStatistics.Interval lastInterval;
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
assertEquals(trackPointIterator.getCount(), numberOfPoints);
|
||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||
}
|
||||
|
||||
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
|
||||
|
||||
// then
|
||||
assertEquals(" Current heart rate 133 bpm.", announcement);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -6,6 +6,7 @@ import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
@@ -25,7 +26,7 @@ import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class GPSHandlerTest {
|
||||
public class GPSManagerTest {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@@ -33,7 +34,7 @@ public class GPSHandlerTest {
|
||||
private TrackPointCreator trackPointCreator;
|
||||
|
||||
@InjectMocks
|
||||
private GPSHandler locationHandler;
|
||||
private GPSManager locationHandler;
|
||||
|
||||
@BeforeClass
|
||||
public static void preSetUp() {
|
||||
@@ -49,7 +50,7 @@ public class GPSHandlerTest {
|
||||
Mockito.when(trackPointCreator.createNow())
|
||||
.thenReturn(Instant.now());
|
||||
|
||||
locationHandler.onStart(context);
|
||||
locationHandler.start(context, new Handler());
|
||||
}
|
||||
|
||||
/**
|
||||
+1
-1
@@ -25,7 +25,7 @@ public class TrackPointCreatorTest {
|
||||
private TrackPointCreator.Callback server;
|
||||
|
||||
@Mock
|
||||
private GPSHandler locationHandler;
|
||||
private GPSManager locationHandler;
|
||||
|
||||
private TrackPointCreator subject;
|
||||
|
||||
|
||||
+18
-17
@@ -17,7 +17,8 @@ import org.junit.runner.RunWith;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayoutIO;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PreferencesOpenHelperTest {
|
||||
@@ -38,10 +39,10 @@ public class PreferencesOpenHelperTest {
|
||||
PreferencesOpenHelper.newInstance(1).check();
|
||||
|
||||
// then there should be one layout with old custom layout that has the new CSV value.
|
||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
||||
assertNotNull(layouts);
|
||||
assertEquals(layouts.size(), 1);
|
||||
assertEquals(layouts.get(0).toCsv(), PreferencesUtils.getCustomLayout().toCsv());
|
||||
List<RecordingLayout> recordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||
assertNotNull(recordingLayouts);
|
||||
assertEquals(recordingLayouts.size(), 1);
|
||||
assertEquals(recordingLayouts.get(0).toCsv(), PreferencesUtils.getCustomLayout().toCsv());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -63,10 +64,10 @@ public class PreferencesOpenHelperTest {
|
||||
+ PreferencesUtils.getLayoutColumnsByDefault() + ";distance,1,1,0;speed,1,1,0;";
|
||||
|
||||
// then there should be one layout with old custom layout that has the new CSV value.
|
||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
||||
assertNotNull(layouts);
|
||||
assertEquals(layouts.size(), 1);
|
||||
assertEquals(layouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
||||
List<RecordingLayout> recordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||
assertNotNull(recordingLayouts);
|
||||
assertEquals(recordingLayouts.size(), 1);
|
||||
assertEquals(recordingLayouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -87,10 +88,10 @@ public class PreferencesOpenHelperTest {
|
||||
String updatedOldCustomLayoutCsv = "whatever;3;distance,1,1,0;speed,1,1,0;";
|
||||
|
||||
// then there should be one layout with old custom layout that has the new CSV value.
|
||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
||||
assertNotNull(layouts);
|
||||
assertEquals(layouts.size(), 1);
|
||||
assertEquals(layouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
||||
List<RecordingLayout> recordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||
assertNotNull(recordingLayouts);
|
||||
assertEquals(recordingLayouts.size(), 1);
|
||||
assertEquals(recordingLayouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -109,10 +110,10 @@ public class PreferencesOpenHelperTest {
|
||||
PreferencesOpenHelper.newInstance(1).check();
|
||||
|
||||
// then custom layout should be equals to default layout.
|
||||
Layout defaultLayout = Layout.fromCsv(PreferencesUtils.buildDefaultLayout(), resources);
|
||||
List<Layout> customLayout = PreferencesUtils.getAllCustomLayouts();
|
||||
RecordingLayout defaultRecordingLayout = RecordingLayoutIO.fromCsv(PreferencesUtils.buildDefaultLayout(), resources);
|
||||
List<RecordingLayout> customRecordingLayout = PreferencesUtils.getAllCustomLayouts();
|
||||
|
||||
assertEquals(customLayout.size(), 1);
|
||||
assertEquals(defaultLayout.toCsv(), customLayout.get(0).toCsv());
|
||||
assertEquals(customRecordingLayout.size(), 1);
|
||||
assertEquals(defaultRecordingLayout.toCsv(), customRecordingLayout.get(0).toCsv());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ import java.util.List;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayoutIO;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PreferencesUtilsTest {
|
||||
@@ -85,13 +86,13 @@ public class PreferencesUtilsTest {
|
||||
editor.commit();
|
||||
|
||||
// when
|
||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
||||
List<RecordingLayout> recordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||
|
||||
// then
|
||||
assertEquals(layouts.size(), 1);
|
||||
assertTrue(layouts.get(0).getFields().size() > 0);
|
||||
assertEquals(layouts.get(0).getName(), context.getString(R.string.stats_custom_layout_default_layout));
|
||||
assertTrue(layouts.get(0).getFields().stream().anyMatch(DataField::isVisible));
|
||||
assertEquals(recordingLayouts.size(), 1);
|
||||
assertTrue(recordingLayouts.get(0).getFields().size() > 0);
|
||||
assertEquals(recordingLayouts.get(0).getName(), context.getString(R.string.stats_custom_layout_default_layout));
|
||||
assertTrue(recordingLayouts.get(0).getFields().stream().anyMatch(DataField::isVisible));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -103,12 +104,12 @@ public class PreferencesUtilsTest {
|
||||
editor.commit();
|
||||
|
||||
// when
|
||||
Layout layout = PreferencesUtils.getCustomLayout();
|
||||
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
|
||||
|
||||
// then
|
||||
assertTrue(layout.getFields().size() > 0);
|
||||
assertEquals(layout.getName(), context.getString(R.string.stats_custom_layout_default_layout));
|
||||
assertTrue(layout.getFields().stream().anyMatch(DataField::isVisible));
|
||||
assertTrue(recordingLayout.getFields().size() > 0);
|
||||
assertEquals(recordingLayout.getName(), context.getString(R.string.stats_custom_layout_default_layout));
|
||||
assertTrue(recordingLayout.getFields().stream().anyMatch(DataField::isVisible));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -126,28 +127,28 @@ public class PreferencesUtilsTest {
|
||||
editor.apply();
|
||||
|
||||
// when
|
||||
Layout layout = PreferencesUtils.getCustomLayout();
|
||||
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
|
||||
|
||||
// then
|
||||
assertEquals(layout.getFields().size(), 4);
|
||||
assertEquals(layout.getName(), "run");
|
||||
assertEquals(layout.getColumnsPerRow(), 2);
|
||||
assertEquals(recordingLayout.getFields().size(), 4);
|
||||
assertEquals(recordingLayout.getName(), "run");
|
||||
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||
|
||||
assertEquals(layout.getFields().get(0).getKey(), context.getString(R.string.stats_custom_layout_moving_time_key));
|
||||
assertTrue(layout.getFields().get(0).isVisible());
|
||||
assertTrue(layout.getFields().get(0).isPrimary());
|
||||
assertEquals(recordingLayout.getFields().get(0).getKey(), context.getString(R.string.stats_custom_layout_moving_time_key));
|
||||
assertTrue(recordingLayout.getFields().get(0).isVisible());
|
||||
assertTrue(recordingLayout.getFields().get(0).isPrimary());
|
||||
|
||||
assertEquals(layout.getFields().get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
||||
assertTrue(layout.getFields().get(1).isVisible());
|
||||
assertFalse(layout.getFields().get(1).isPrimary());
|
||||
assertEquals(recordingLayout.getFields().get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
||||
assertTrue(recordingLayout.getFields().get(1).isVisible());
|
||||
assertFalse(recordingLayout.getFields().get(1).isPrimary());
|
||||
|
||||
assertEquals(layout.getFields().get(2).getKey(), context.getString(R.string.stats_custom_layout_average_moving_speed_key));
|
||||
assertFalse(layout.getFields().get(2).isVisible());
|
||||
assertTrue(layout.getFields().get(2).isPrimary());
|
||||
assertEquals(recordingLayout.getFields().get(2).getKey(), context.getString(R.string.stats_custom_layout_average_moving_speed_key));
|
||||
assertFalse(recordingLayout.getFields().get(2).isVisible());
|
||||
assertTrue(recordingLayout.getFields().get(2).isPrimary());
|
||||
|
||||
assertEquals(layout.getFields().get(3).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
||||
assertFalse(layout.getFields().get(3).isVisible());
|
||||
assertFalse(layout.getFields().get(3).isPrimary());
|
||||
assertEquals(recordingLayout.getFields().get(3).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
||||
assertFalse(recordingLayout.getFields().get(3).isVisible());
|
||||
assertFalse(recordingLayout.getFields().get(3).isPrimary());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -165,44 +166,44 @@ public class PreferencesUtilsTest {
|
||||
editor.apply();
|
||||
|
||||
// when
|
||||
Layout layout = PreferencesUtils.getCustomLayout();
|
||||
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
|
||||
|
||||
// then
|
||||
assertEquals(layout.getFields().size(), 4);
|
||||
assertEquals(layout.getName(), "walking");
|
||||
assertEquals(layout.getColumnsPerRow(), 2);
|
||||
assertEquals(recordingLayout.getFields().size(), 4);
|
||||
assertEquals(recordingLayout.getName(), "walking");
|
||||
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||
|
||||
assertEquals(layout.getFields().get(0).getKey(), context.getString(R.string.stats_custom_layout_moving_time_key));
|
||||
assertTrue(layout.getFields().get(0).isVisible());
|
||||
assertTrue(layout.getFields().get(0).isPrimary());
|
||||
assertEquals(recordingLayout.getFields().get(0).getKey(), context.getString(R.string.stats_custom_layout_moving_time_key));
|
||||
assertTrue(recordingLayout.getFields().get(0).isVisible());
|
||||
assertTrue(recordingLayout.getFields().get(0).isPrimary());
|
||||
|
||||
assertEquals(layout.getFields().get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
||||
assertTrue(layout.getFields().get(1).isVisible());
|
||||
assertFalse(layout.getFields().get(1).isPrimary());
|
||||
assertEquals(recordingLayout.getFields().get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
||||
assertTrue(recordingLayout.getFields().get(1).isVisible());
|
||||
assertFalse(recordingLayout.getFields().get(1).isPrimary());
|
||||
|
||||
assertEquals(layout.getFields().get(2).getKey(), context.getString(R.string.stats_custom_layout_coordinates_key));
|
||||
assertFalse(layout.getFields().get(2).isVisible());
|
||||
assertTrue(layout.getFields().get(2).isPrimary());
|
||||
assertTrue(layout.getFields().get(2).isWide());
|
||||
assertEquals(recordingLayout.getFields().get(2).getKey(), context.getString(R.string.stats_custom_layout_coordinates_key));
|
||||
assertFalse(recordingLayout.getFields().get(2).isVisible());
|
||||
assertTrue(recordingLayout.getFields().get(2).isPrimary());
|
||||
assertTrue(recordingLayout.getFields().get(2).isWide());
|
||||
|
||||
assertEquals(layout.getFields().get(3).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
||||
assertFalse(layout.getFields().get(3).isVisible());
|
||||
assertFalse(layout.getFields().get(3).isPrimary());
|
||||
assertEquals(recordingLayout.getFields().get(3).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
||||
assertFalse(recordingLayout.getFields().get(3).isVisible());
|
||||
assertFalse(recordingLayout.getFields().get(3).isPrimary());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetCustomLayout() {
|
||||
// given
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
Layout layoutSrc = new Layout("road cycling");
|
||||
layoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_moving_time_key), context.getString(R.string.stats_moving_time), true, true, false));
|
||||
layoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_distance_key), context.getString(R.string.stats_distance), true, false, false));
|
||||
layoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_average_moving_speed_key), context.getString(R.string.stats_average_moving_speed), false, true, false));
|
||||
layoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_speed_key), context.getString(R.string.stats_speed), false, false, false));
|
||||
RecordingLayout recordingLayoutSrc = new RecordingLayout("road cycling");
|
||||
recordingLayoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_moving_time_key), true, true, false));
|
||||
recordingLayoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_distance_key), true, false, false));
|
||||
recordingLayoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_average_moving_speed_key), false, true, false));
|
||||
recordingLayoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_speed_key), false, false, false));
|
||||
|
||||
// when
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString(context.getString(R.string.stats_custom_layouts_key), layoutSrc.toCsv());
|
||||
editor.putString(context.getString(R.string.stats_custom_layouts_key), recordingLayoutSrc.toCsv());
|
||||
editor.commit();
|
||||
|
||||
// then
|
||||
@@ -215,13 +216,13 @@ public class PreferencesUtilsTest {
|
||||
+ context.getString(R.string.stats_custom_layout_average_moving_speed_key) + ",0,1,0;"
|
||||
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0,0;");
|
||||
|
||||
Layout layoutDst = PreferencesUtils.getCustomLayout();
|
||||
assertEquals(layoutSrc.getName(), layoutDst.getName());
|
||||
assertEquals(layoutSrc.getFields().size(), layoutDst.getFields().size());
|
||||
for (int i = 0; i < layoutSrc.getFields().size(); i++) {
|
||||
assertEquals(layoutSrc.getFields().get(i).getKey(), layoutDst.getFields().get(i).getKey());
|
||||
assertEquals(layoutSrc.getFields().get(i).isVisible(), layoutDst.getFields().get(i).isVisible());
|
||||
assertEquals(layoutSrc.getFields().get(i).isPrimary(), layoutDst.getFields().get(i).isPrimary());
|
||||
RecordingLayout recordingLayoutDst = PreferencesUtils.getCustomLayout();
|
||||
assertEquals(recordingLayoutSrc.getName(), recordingLayoutDst.getName());
|
||||
assertEquals(recordingLayoutSrc.getFields().size(), recordingLayoutDst.getFields().size());
|
||||
for (int i = 0; i < recordingLayoutSrc.getFields().size(); i++) {
|
||||
assertEquals(recordingLayoutSrc.getFields().get(i).getKey(), recordingLayoutDst.getFields().get(i).getKey());
|
||||
assertEquals(recordingLayoutSrc.getFields().get(i).isVisible(), recordingLayoutDst.getFields().get(i).isVisible());
|
||||
assertEquals(recordingLayoutSrc.getFields().get(i).isPrimary(), recordingLayoutDst.getFields().get(i).isPrimary());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +248,7 @@ public class PreferencesUtilsTest {
|
||||
editor.putString(context.getString(R.string.stats_custom_layouts_key), cyclingProfile + "\n" + runningProfile);
|
||||
editor.apply();
|
||||
|
||||
List<Layout> layoutsBefore = PreferencesUtils.getAllCustomLayouts();
|
||||
List<RecordingLayout> layoutsBefore = PreferencesUtils.getAllCustomLayouts();
|
||||
|
||||
// when cyling profile is updated
|
||||
String cyclingProfileUpdated = "cycling;2;"
|
||||
@@ -256,14 +257,14 @@ public class PreferencesUtilsTest {
|
||||
+ context.getString(R.string.stats_custom_layout_average_moving_speed_key) + ",0,0;"
|
||||
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0;";
|
||||
|
||||
List<Layout> layoutsToBeUpdated = new ArrayList<>();
|
||||
layoutsToBeUpdated.add(Layout.fromCsv(cyclingProfileUpdated, resources));
|
||||
layoutsToBeUpdated.add(Layout.fromCsv(runningProfile, resources));
|
||||
List<RecordingLayout> layoutsToBeUpdated = new ArrayList<>();
|
||||
layoutsToBeUpdated.add(RecordingLayoutIO.fromCsv(cyclingProfileUpdated, resources));
|
||||
layoutsToBeUpdated.add(RecordingLayoutIO.fromCsv(runningProfile, resources));
|
||||
|
||||
PreferencesUtils.updateCustomLayouts(layoutsToBeUpdated);
|
||||
|
||||
// then only updated profile is modified in the custom layouts
|
||||
List<Layout> layoutsAfter = PreferencesUtils.getAllCustomLayouts();
|
||||
List<RecordingLayout> layoutsAfter = PreferencesUtils.getAllCustomLayouts();
|
||||
|
||||
assertEquals(layoutsBefore.size(), 2);
|
||||
assertEquals(layoutsAfter.size(), 2);
|
||||
@@ -294,7 +295,7 @@ public class PreferencesUtilsTest {
|
||||
editor.putString(context.getString(R.string.stats_custom_layouts_key), cyclingProfile + "\n" + runningProfile);
|
||||
editor.apply();
|
||||
|
||||
List<Layout> layoutsBefore = PreferencesUtils.getAllCustomLayouts();
|
||||
List<RecordingLayout> layoutsBefore = PreferencesUtils.getAllCustomLayouts();
|
||||
|
||||
// when cyling profile is updated
|
||||
String cyclingProfileUpdated = "cycling;2;"
|
||||
@@ -302,11 +303,11 @@ public class PreferencesUtilsTest {
|
||||
+ context.getString(R.string.stats_custom_layout_distance_key) + ",0,0;"
|
||||
+ context.getString(R.string.stats_custom_layout_average_moving_speed_key) + ",0,0;"
|
||||
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0;";
|
||||
Layout layoutToBeUpdated = Layout.fromCsv(cyclingProfileUpdated, resources);
|
||||
PreferencesUtils.updateCustomLayout(layoutToBeUpdated);
|
||||
RecordingLayout recordingLayoutToBeUpdated = RecordingLayoutIO.fromCsv(cyclingProfileUpdated, resources);
|
||||
PreferencesUtils.updateCustomLayout(recordingLayoutToBeUpdated);
|
||||
|
||||
// then only updated profile is modified in the custom layouts
|
||||
List<Layout> layoutsAfter = PreferencesUtils.getAllCustomLayouts();
|
||||
List<RecordingLayout> layoutsAfter = PreferencesUtils.getAllCustomLayouts();
|
||||
|
||||
assertEquals(layoutsBefore.size(), 2);
|
||||
assertEquals(layoutsAfter.size(), 2);
|
||||
@@ -337,9 +338,9 @@ public class PreferencesUtilsTest {
|
||||
editor.apply();
|
||||
|
||||
// when it gets the custom layout
|
||||
Layout layout = PreferencesUtils.getCustomLayout();
|
||||
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
|
||||
|
||||
// then the first one was returned
|
||||
assertEquals(layout.getName(), "cycling");
|
||||
assertEquals(recordingLayout.getName(), "cycling");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ public class DescriptionGeneratorTest {
|
||||
+ "Min elevation: -500 m (-1640 ft)<br>"
|
||||
+ "Elevation gain: 6000 m (19685 ft)<br>"
|
||||
+ "Elevation loss: 6000 m (19685 ft)<br>"
|
||||
+ "Recorded: " + StringUtils.formatDateTime(OffsetDateTime.ofInstant(START_TIME, ZoneId.systemDefault())) + "<br>";
|
||||
+ "Recorded: " + StringUtils.formatDateTimeWithOffset(OffsetDateTime.ofInstant(START_TIME, ZoneId.systemDefault())) + "<br>";
|
||||
|
||||
assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
|
||||
}
|
||||
@@ -135,7 +135,7 @@ public class DescriptionGeneratorTest {
|
||||
+ "Fastest pace: 0:10 min/km (0:16 min/mi)<br>"
|
||||
+ "Elevation gain: 6000 m (19685 ft)<br>"
|
||||
+ "Elevation loss: 6000 m (19685 ft)<br>"
|
||||
+ "Recorded: " + StringUtils.formatDateTime(OffsetDateTime.ofInstant(START_TIME, ZoneId.systemDefault())) + "<br>";
|
||||
+ "Recorded: " + StringUtils.formatDateTimeWithOffset(OffsetDateTime.ofInstant(START_TIME, ZoneId.systemDefault())) + "<br>";
|
||||
|
||||
assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
|
||||
}
|
||||
|
||||
+67
-2
@@ -11,6 +11,7 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
@@ -77,7 +78,7 @@ public class TrackStatisticsUpdaterTest {
|
||||
|
||||
// when
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||
data.trackPoints.forEach(it -> subject.addTrackPoint(it));
|
||||
data.trackPoints.forEach(subject::addTrackPoint);
|
||||
|
||||
// then
|
||||
TrackStatistics statistics = subject.getTrackStatistics();
|
||||
@@ -86,7 +87,7 @@ public class TrackStatisticsUpdaterTest {
|
||||
assertEquals(Duration.ofSeconds(9), statistics.getMovingTime());
|
||||
|
||||
assertEquals(2.5, statistics.getMinAltitude(), 0.01);
|
||||
assertEquals(28.75, statistics.getMaxAltitude(), 0.01);
|
||||
assertEquals(32.5, statistics.getMaxAltitude(), 0.01);
|
||||
assertEquals(36, statistics.getTotalAltitudeGain(), 0.01);
|
||||
assertEquals(36, statistics.getTotalAltitudeLoss(), 0.01);
|
||||
|
||||
@@ -245,6 +246,70 @@ public class TrackStatisticsUpdaterTest {
|
||||
public void addTrackPoint_speed_from_GPS_moving_and_sensor_speed() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addTrackPoint_maxSpeed_ignore_above_acceleration() {
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||
assertEquals(Speed.of(0f), subject.getTrackStatistics().getMaxSpeed());
|
||||
|
||||
subject.addTrackPoint(new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0)));
|
||||
assertEquals(Speed.of(0f), subject.getTrackStatistics().getMaxSpeed());
|
||||
|
||||
// Ignore as we set max speed if two consecutive trackpoints were considered moving
|
||||
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(1))
|
||||
.setSpeed(Speed.of(1f)));
|
||||
assertEquals(Speed.of(0f), subject.getTrackStatistics().getMaxSpeed());
|
||||
|
||||
// Update max speed
|
||||
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(2))
|
||||
.setSpeed(Speed.of(1f)));
|
||||
assertEquals(Speed.of(1f), subject.getTrackStatistics().getMaxSpeed());
|
||||
|
||||
// Update max speed
|
||||
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(12))
|
||||
.setSpeed(Speed.of(50f)));
|
||||
assertEquals(Speed.of(50f), subject.getTrackStatistics().getMaxSpeed());
|
||||
|
||||
// Ignore; we were getting slower
|
||||
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(13))
|
||||
.setSpeed(Speed.of(5f)));
|
||||
assertEquals(Speed.of(50f), subject.getTrackStatistics().getMaxSpeed());
|
||||
|
||||
// Ignore acceleration above 2g
|
||||
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(14))
|
||||
.setSpeed(Speed.of(500f)));
|
||||
assertEquals(Speed.of(50f), subject.getTrackStatistics().getMaxSpeed());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addTrackPoint_maxSpeed_multiple_segments() {
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||
assertEquals(Speed.of(0f), subject.getTrackStatistics().getMaxSpeed());
|
||||
|
||||
subject.addTrackPoints(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0)),
|
||||
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(1))
|
||||
.setSpeed(Speed.of(2f)),
|
||||
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(2))
|
||||
.setSpeed(Speed.of(2f)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochSecond(4))
|
||||
));
|
||||
assertEquals(Speed.of(2f), subject.getTrackStatistics().getMaxSpeed());
|
||||
|
||||
// when
|
||||
subject.addTrackPoints(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(5)),
|
||||
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(6))
|
||||
.setSpeed(Speed.of(1f)),
|
||||
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(7))
|
||||
.setSpeed(Speed.of(1f)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochSecond(8))
|
||||
));
|
||||
|
||||
// then
|
||||
assertEquals(Speed.of(2f), subject.getTrackStatistics().getMaxSpeed());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copy_constructor() {
|
||||
// given
|
||||
|
||||
+1
-2
@@ -12,7 +12,6 @@ import static org.hamcrest.Matchers.allOf;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.espresso.ViewInteraction;
|
||||
@@ -72,7 +71,7 @@ public class EspressoAggregatedFilterTest {
|
||||
ViewInteraction checkBox = onView(
|
||||
allOf(withId(R.id.filter_dialog_check_button), withText(CATEGORY),
|
||||
withParent(allOf(withId(R.id.filter_items),
|
||||
withParent(IsInstanceOf.<View>instanceOf(android.view.ViewGroup.class)))),
|
||||
withParent(IsInstanceOf.instanceOf(android.view.ViewGroup.class)))),
|
||||
isDisplayed()));
|
||||
checkBox.perform(waitFor(2000));
|
||||
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import org.junit.runner.RunWith;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class CsvLayoutUtilsTest extends TestCase {
|
||||
public class CsvRecordingLayoutUtilsTest extends TestCase {
|
||||
|
||||
@Test
|
||||
public void testGetCsvLineParts() {
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
package de.dennisguse.opentracks.ui.customRecordingLayout;
|
||||
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.assertThat;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.veryLongTouch;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.test.espresso.NoMatchingViewException;
|
||||
import androidx.test.espresso.ViewAssertion;
|
||||
import androidx.test.espresso.ViewInteraction;
|
||||
import androidx.test.espresso.matcher.ViewMatchers;
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TrackListActivity;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class EspressoCustomLayoutTest {
|
||||
|
||||
@Rule
|
||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
@Test
|
||||
public void customLayoutTest() {
|
||||
// TrackListActivity: start recording
|
||||
ViewInteraction trackControllerRecordButton = onView(ViewMatchers.withId(R.id.track_list_fab_action));
|
||||
trackControllerRecordButton.perform(click());
|
||||
|
||||
// Get custom layout preferences and check all data fields are showed.
|
||||
Layout layout = PreferencesUtils.getCustomLayout();
|
||||
|
||||
onView(withId(R.id.stats_recycler_view)).check(new RecyclerViewItemCountAssertion((int) layout.getFields().stream().filter(DataField::isVisible).count()));
|
||||
|
||||
// stop recording
|
||||
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
|
||||
trackControllerStopButton.perform(veryLongTouch(1600));
|
||||
}
|
||||
|
||||
private static class RecyclerViewItemCountAssertion implements ViewAssertion {
|
||||
private final int expectedCount;
|
||||
|
||||
public RecyclerViewItemCountAssertion(int expectedCount) {
|
||||
this.expectedCount = expectedCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(View view, NoMatchingViewException noViewFoundException) {
|
||||
if (noViewFoundException != null) {
|
||||
throw noViewFoundException;
|
||||
}
|
||||
|
||||
RecyclerView recyclerView = (RecyclerView) view;
|
||||
RecyclerView.Adapter<?> adapter = recyclerView.getAdapter();
|
||||
assertThat(adapter.getItemCount(), is(expectedCount));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+39
-43
@@ -17,7 +17,7 @@ import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class LayoutTest extends TestCase {
|
||||
public class RecordingLayoutTest extends TestCase {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private final Resources resources = ApplicationProvider.getApplicationContext().getResources();
|
||||
@@ -28,21 +28,17 @@ public class LayoutTest extends TestCase {
|
||||
String csv = "running;2;" + context.getString(R.string.stats_custom_layout_speed_key) + ",1,1,0;" + context.getString(R.string.stats_custom_layout_distance_key) + ",1,0,0;" + context.getString(R.string.stats_custom_layout_altitude_key) + ",0,1,0;" + context.getString(R.string.stats_custom_layout_gain_key) + ",0,0,0;";
|
||||
|
||||
// when create a layout from CSV line
|
||||
Layout layout = Layout.fromCsv(csv, resources);
|
||||
List<DataField> dataFieldList = layout.getFields();
|
||||
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv, resources);
|
||||
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||
|
||||
// then layout and data fields are built correctly
|
||||
assertEquals(layout.getName(), "running");
|
||||
assertEquals(layout.getColumnsPerRow(), 2);
|
||||
assertEquals(recordingLayout.getName(), "running");
|
||||
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||
assertEquals(dataFieldList.size(), 4);
|
||||
assertEquals(dataFieldList.get(0).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
||||
assertEquals(dataFieldList.get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
||||
assertEquals(dataFieldList.get(2).getKey(), context.getString(R.string.stats_custom_layout_altitude_key));
|
||||
assertEquals(dataFieldList.get(3).getKey(), context.getString(R.string.stats_custom_layout_gain_key));
|
||||
assertEquals(dataFieldList.get(0).getTitle(), context.getString(R.string.stats_speed));
|
||||
assertEquals(dataFieldList.get(1).getTitle(), context.getString(R.string.stats_distance));
|
||||
assertEquals(dataFieldList.get(2).getTitle(), context.getString(R.string.stats_altitude));
|
||||
assertEquals(dataFieldList.get(3).getTitle(), context.getString(R.string.stats_gain));
|
||||
assertTrue(dataFieldList.get(0).isVisible());
|
||||
assertTrue(dataFieldList.get(0).isPrimary());
|
||||
assertFalse(dataFieldList.get(0).isWide());
|
||||
@@ -64,13 +60,13 @@ public class LayoutTest extends TestCase {
|
||||
String csv1 = "Layout Name;speed,1,1;distance,0,0;";
|
||||
|
||||
// when create a layout from CSV line
|
||||
Layout layout = Layout.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = layout.getFields();
|
||||
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||
|
||||
// then layout and data fields are built correctly
|
||||
assertEquals(layout.getName(), PreferencesUtils.getDefaultLayoutName());
|
||||
assertEquals(layout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
||||
assertEquals(layout.getFields().size(), 0);
|
||||
assertEquals(recordingLayout.getName(), PreferencesUtils.getDefaultLayoutName());
|
||||
assertEquals(recordingLayout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
||||
assertEquals(recordingLayout.getFields().size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -79,13 +75,13 @@ public class LayoutTest extends TestCase {
|
||||
String csv1 = "Layout Name;2;";
|
||||
|
||||
// when create a layout from CSV line
|
||||
Layout layout = Layout.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = layout.getFields();
|
||||
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||
|
||||
// then layout and data fields are built correctly
|
||||
assertEquals(layout.getName(), PreferencesUtils.getDefaultLayoutName());
|
||||
assertEquals(layout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
||||
assertEquals(layout.getFields().size(), 0);
|
||||
assertEquals(recordingLayout.getName(), PreferencesUtils.getDefaultLayoutName());
|
||||
assertEquals(recordingLayout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
||||
assertEquals(recordingLayout.getFields().size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -94,13 +90,13 @@ public class LayoutTest extends TestCase {
|
||||
String csv1 = "Layout Name;2;speed,distance,total time;";
|
||||
|
||||
// when create a layout from CSV line
|
||||
Layout layout = Layout.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = layout.getFields();
|
||||
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||
|
||||
// then layout and data fields are built correctly
|
||||
assertEquals(layout.getName(), "Layout Name");
|
||||
assertEquals(layout.getColumnsPerRow(), 2);
|
||||
assertEquals(layout.getFields().size(), 0);
|
||||
assertEquals(recordingLayout.getName(), "Layout Name");
|
||||
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||
assertEquals(recordingLayout.getFields().size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -109,27 +105,27 @@ public class LayoutTest extends TestCase {
|
||||
String csv1 = "Layout Name;2;speed,1,0;distance;";
|
||||
|
||||
// when create a layout from CSV line
|
||||
Layout layout = Layout.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = layout.getFields();
|
||||
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||
|
||||
// then layout and data fields are built correctly
|
||||
assertEquals(layout.getName(), "Layout Name");
|
||||
assertEquals(layout.getColumnsPerRow(), 2);
|
||||
assertEquals(layout.getFields().size(), 1);
|
||||
assertEquals(recordingLayout.getName(), "Layout Name");
|
||||
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||
assertEquals(recordingLayout.getFields().size(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToCsv() {
|
||||
// given a layout's object
|
||||
Layout layout = new Layout("Test Layout", 2);
|
||||
layout.addField("key1", "Title 1", false, false, false);
|
||||
layout.addField("key2", "Title 2", false, true, false);
|
||||
layout.addField("key3", "Title 3", true, false, false);
|
||||
layout.addField("key4", "Title 4", true, true, false);
|
||||
layout.addField("key5", "Title 5", true, true, true);
|
||||
RecordingLayout recordingLayout = new RecordingLayout("Test Layout", 2);
|
||||
recordingLayout.addField(new DataField("key1", false, false, false));
|
||||
recordingLayout.addField(new DataField("key2", false, true, false));
|
||||
recordingLayout.addField(new DataField("key3", true, false, false));
|
||||
recordingLayout.addField(new DataField("key4", true, true, false));
|
||||
recordingLayout.addField(new DataField("key5", true, true, true));
|
||||
|
||||
// when converts it to CSV
|
||||
String csv = layout.toCsv();
|
||||
String csv = recordingLayout.toCsv();
|
||||
|
||||
// then csv is well built
|
||||
assertEquals(csv, "Test Layout;2;key1,0,0,0;key2,0,1,0;key3,1,0,0;key4,1,1,0;key5,1,1,1;");
|
||||
@@ -138,15 +134,15 @@ public class LayoutTest extends TestCase {
|
||||
@Test
|
||||
public void testToCsv_columnsByDefault() {
|
||||
// given a layout's object
|
||||
Layout layout = new Layout("Test Layout");
|
||||
layout.addField("key1", "Title 1", false, false, false);
|
||||
layout.addField("key2", "Title 2", false, true, false);
|
||||
layout.addField("key3", "Title 3", true, false, false);
|
||||
layout.addField("key4", "Title 4", true, true, false);
|
||||
layout.addField("key5", "Title 5", true, true, true);
|
||||
RecordingLayout recordingLayout = new RecordingLayout("Test Layout");
|
||||
recordingLayout.addField(new DataField("key1", false, false, false));
|
||||
recordingLayout.addField(new DataField("key2", false, true, false));
|
||||
recordingLayout.addField(new DataField("key3", true, false, false));
|
||||
recordingLayout.addField(new DataField("key4", true, true, false));
|
||||
recordingLayout.addField(new DataField("key5", true, true, true));
|
||||
|
||||
// when converts it to CSV
|
||||
String csv = layout.toCsv();
|
||||
String csv = recordingLayout.toCsv();
|
||||
|
||||
// then csv is well built
|
||||
assertEquals(csv, "Test Layout;" + PreferencesUtils.getLayoutColumnsByDefault() + ";key1,0,0,0;key2,0,1,0;key3,1,0,0;key4,1,1,0;key5,1,1,1;");
|
||||
+1
-2
@@ -21,7 +21,6 @@ import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.data.models.UnitConversions;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||
|
||||
@@ -180,7 +179,7 @@ public class IntervalStatisticsTest {
|
||||
|
||||
// then
|
||||
assertEquals(trackStatistics.getTotalDistance().toM(), totalDistance.toM(), 0.01);
|
||||
assertEquals(trackStatistics.getTotalTime().toMillis(), totalTime * UnitConversions.S_TO_MS, 1);
|
||||
assertEquals(trackStatistics.getTotalTime().toSeconds(), totalTime, 0.01);
|
||||
assertEquals(intervalList.size(), (int) Math.ceil(trackStatistics.getTotalDistance().toM() / distanceInterval));
|
||||
if (totalGain != null) {
|
||||
assertEquals(totalGain, numberOfPoints * TestDataUtil.ALTITUDE_GAIN, 0.1);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user