forked from upstream-mirrors/OpenTracks
Compare commits
450 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ab8791a620 | |||
| 7c03085761 | |||
| 12a8686896 | |||
| e12bb967e6 | |||
| d56b018676 | |||
| a6fd701004 | |||
| ce26e20571 | |||
| d3ff2061c6 | |||
| aa63474114 | |||
| cf545dc17b | |||
| a542f45661 | |||
| 1eeb706330 | |||
| 01ea65742b | |||
| e9456919cb | |||
| 2a8e1c99b6 | |||
| 245854581b | |||
| e372a780f0 | |||
| 00be0b565c | |||
| 8b1e1cf294 | |||
| ce313ba4fd | |||
| df501bde0d | |||
| 30ed8d8ba3 | |||
| ea2127846d | |||
| ac0da5e9d1 | |||
| 47fea690c9 | |||
| a1fa95094f | |||
| c4a1fb0d98 | |||
| 57cd4b3780 | |||
| 5c645712ff | |||
| e724baaafa | |||
| f55dcd7162 | |||
| 74169cb819 | |||
| 1821f47cf1 | |||
| 925a253bbb | |||
| e89ae1c2df | |||
| 75058118e0 | |||
| 96559230f8 | |||
| f16f2cfc64 | |||
| 7f353a5d13 | |||
| 2453edaa65 | |||
| 1b1568511a | |||
| 29a36c3ea7 | |||
| 71d0c7cc92 | |||
| b09d640844 | |||
| a039f1a00c | |||
| 3df5c06c20 | |||
| 2904c65a01 | |||
| 4977789004 | |||
| 95e6448cba | |||
| a66c1b9407 | |||
| 9d55ac261f | |||
| dbd3e2fc5c | |||
| 713e454e44 | |||
| f0616fc043 | |||
| 75ed3120bd | |||
| b22e41cf66 | |||
| d1f003c0ce | |||
| 6ef80ade98 | |||
| e49d9fbd83 | |||
| c7738b1cb2 | |||
| 713f5c7708 | |||
| b9a36d1b3c | |||
| 34e89ea3f9 | |||
| 50a0e37086 | |||
| 11ac6b7279 | |||
| aadb79bf7d | |||
| da6b26644e | |||
| b264655d75 | |||
| 1ea9c0b216 | |||
| 8040edac7a | |||
| 037c79a1c5 | |||
| bc46f7447e | |||
| 6e88a448b1 | |||
| 2dac8d3ea6 | |||
| 0844c48d25 | |||
| 9607fda962 | |||
| 77ed28a8e2 | |||
| 4283e6d316 | |||
| ee030f32f5 | |||
| fb25055f3e | |||
| 9cdf87b998 | |||
| e9217b5327 | |||
| b4cb606499 | |||
| d1975685bc | |||
| 8cd9dda1f5 | |||
| c072967dd1 | |||
| 94e91d2063 | |||
| 796fa216f6 | |||
| f143aabfbf | |||
| d58342233c | |||
| 91c254ba7f | |||
| 19edb47554 | |||
| 1129c96562 | |||
| 7224efa8a9 | |||
| 149f8f809e | |||
| c2f59c11bf | |||
| 8b7d4fa813 | |||
| 81081b0ee5 | |||
| def4f3a1b9 | |||
| b679f00cc5 | |||
| 40494557d4 | |||
| a94f023a17 | |||
| ee105a1610 | |||
| 3bec73e074 | |||
| 1155a56e49 | |||
| 9b7e858d1d | |||
| d1490fc343 | |||
| 3f6a4975fa | |||
| b5408eabcd | |||
| e1e34452ae | |||
| df121b40d7 | |||
| 4e737616cb | |||
| fb963781af | |||
| 96339b6a36 | |||
| d043842901 | |||
| cefb6b3abf | |||
| 3ee6d43e3f | |||
| 2edd9a1f05 | |||
| f9a4fcfa0d | |||
| 6bed9c5ddf | |||
| 4e400c60f6 | |||
| ca6f973360 | |||
| edb0e7f7bb | |||
| 876b013c2e | |||
| 7fb27cbe95 | |||
| 6d519e2fc1 | |||
| c2f3b50767 | |||
| a95c6a1857 | |||
| f45dad167b | |||
| 88c75d31a8 | |||
| d49e7749f3 | |||
| 8d3e953220 | |||
| 6566904906 | |||
| 1d0e8f59b7 | |||
| be7987f88a | |||
| a92a1e7838 | |||
| 8cba53cfe4 | |||
| fcedb9c444 | |||
| 2ec82af8f1 | |||
| 447d1bb7a3 | |||
| 2f6765e45f | |||
| 90bf2e6ab0 | |||
| d12423522e | |||
| 24f052491b | |||
| 6b4e7d7d9b | |||
| 00fcbb070a | |||
| 708cbf0b2a | |||
| 60e2fe57f9 | |||
| 14cebf3fec | |||
| b651a8ca6a | |||
| d75472ef11 | |||
| 0b849c3d55 | |||
| 1c767c41a7 | |||
| dfa80d61d7 | |||
| fa558c9ab7 | |||
| a1a363698f | |||
| b79acfb942 | |||
| 24ea718ff2 | |||
| 6d187fa66d | |||
| ea27db13fa | |||
| 29d2e7c78c | |||
| 4b7badbecc | |||
| 0dcb8010e2 | |||
| 0a9d4b207c | |||
| c6cb388ce4 | |||
| e6e009ba14 | |||
| f8aa456549 | |||
| 167d90332e | |||
| 719a052f34 | |||
| 244ca26210 | |||
| 476f3a517b | |||
| 289e93eb5d | |||
| 18ff96733e | |||
| 0f2c5adf26 | |||
| 74954df600 | |||
| 1a14fed287 | |||
| 744853ee91 | |||
| 597e1b2c7f | |||
| 8156993921 | |||
| 9890dc28bd | |||
| b65d83010d | |||
| 1a5d06f9c6 | |||
| 723c21d7ec | |||
| 8fe0c3be7d | |||
| e64792bf3f | |||
| 21bafff761 | |||
| c33c2de51d | |||
| a6b11c1c37 | |||
| 315788308b | |||
| 547c1c5c65 | |||
| 69efd6fb82 | |||
| 3a0524f96d | |||
| 05456b44ef | |||
| b449dad619 | |||
| ccefe36489 | |||
| 90e9ab6d2a | |||
| d1061fa6cd | |||
| c0c813001f | |||
| 51498bbcf9 | |||
| 4ef1fcefbc | |||
| 9a572130a7 | |||
| e931f5ab7c | |||
| 9affe28fad | |||
| 4905a37951 | |||
| 439671d0a4 | |||
| 825e8dda6e | |||
| 661c839c78 | |||
| 8eb42a7ecb | |||
| 660a55ff5a | |||
| 958697bcb3 | |||
| 6029eb0317 | |||
| 8b56d53a6a | |||
| e9deebea6c | |||
| 54844b9a9a | |||
| 738201c1c2 | |||
| 48b6be0bd1 | |||
| 6b924cd936 | |||
| 5f083de530 | |||
| 278f6323f1 | |||
| 5baa8fa21e | |||
| 707f4add71 | |||
| b583f8f23d | |||
| c847f622e4 | |||
| 70dd58f484 | |||
| 015352c989 | |||
| 42ae582cd8 | |||
| 6e0ad50397 | |||
| d43efad52a | |||
| 693904529d | |||
| 08143a546a | |||
| eb49156ee7 | |||
| 51c03d8d75 | |||
| 666cbbad68 | |||
| 1cbc93c303 | |||
| afd1232940 | |||
| a3407ccbea | |||
| c259f9b8bd | |||
| b1a556bf91 | |||
| ab70874d59 | |||
| e6367b1dfa | |||
| 8db381c448 | |||
| ef111b7959 | |||
| 56c6cb1f19 | |||
| 874c5eb8ae | |||
| 1a087b1117 | |||
| 3120cd8da0 | |||
| 4263c000c0 | |||
| f4073ead63 | |||
| 2f90784442 | |||
| 2cab394d03 | |||
| 55f1bcb30b | |||
| 33a17784e6 | |||
| 7cb97b0e6b | |||
| 1cb3db496a | |||
| d49d6c9857 | |||
| 12f0c3313f | |||
| a78e059612 | |||
| 0774ee512f | |||
| 4c55468571 | |||
| 201f2d1b27 | |||
| 595ad7f7b5 | |||
| d518507193 | |||
| 888f59133d | |||
| 5bb5b3b344 | |||
| b584a95c9c | |||
| 9ba152c469 | |||
| 1191f2e1e2 | |||
| e5aed794ba | |||
| 7956063598 | |||
| 0f356de2ca | |||
| 5b4b54694c | |||
| 9c271972d2 | |||
| 744f11eaa0 | |||
| 3c16f67882 | |||
| 12d1a50995 | |||
| 21404fed50 | |||
| 990c91f978 | |||
| 6a262426a5 | |||
| 1b2998f423 | |||
| d24dd2b005 | |||
| 341fd02f9a | |||
| 6833ea7282 | |||
| a1da53864e | |||
| 28d49de047 | |||
| 4ae9d24960 | |||
| 0b0b1a0d8d | |||
| 090cdbf712 | |||
| 8258d607c3 | |||
| 686a2a9b8f | |||
| 7ddb6b32bb | |||
| 5cd639c852 | |||
| ae9906fb18 | |||
| 96bcac397b | |||
| d7b207afac | |||
| 1672ba99ad | |||
| a6d4a05839 | |||
| 860b90da37 | |||
| 46d7385d25 | |||
| 25fbd9490c | |||
| 6d33adffce | |||
| 92cecb6f3a | |||
| e6bdc9a41d | |||
| e234e9c493 | |||
| 73d6ca6ca6 | |||
| 1ef7a6ecc4 | |||
| 94a0b57362 | |||
| 4ebaf366d6 | |||
| 911f2c525f | |||
| a310f773a1 | |||
| 8a247e5b38 | |||
| 1045ade71a | |||
| 4307dceb70 | |||
| 59ea50b96f | |||
| 8d015fee6c | |||
| 653352255e | |||
| ebcdd87481 | |||
| 8d1af8f2cf | |||
| 766739c222 | |||
| 54fa6aa3e2 | |||
| 1dc737410b | |||
| 4093899a82 | |||
| 9236eb6944 | |||
| 9f00b0606b | |||
| 20a18b86dc | |||
| 34cfc235c3 | |||
| f733d18a60 | |||
| 221b61c7f1 | |||
| ca64f2c749 | |||
| 3d083928eb | |||
| f47ab142f2 | |||
| 6e4d46a14d | |||
| fffeb40200 | |||
| a5e5d0ca51 | |||
| 7791ff8659 | |||
| 3db0a53377 | |||
| 58dac8b706 | |||
| f29b6d4088 | |||
| 80fb4f9998 | |||
| 6340e55fff | |||
| 625549d381 | |||
| 7d9878b32e | |||
| a5553211af | |||
| 044e84154c | |||
| f61d718b2d | |||
| c7a54dcee8 | |||
| 3dc2ee3462 | |||
| 6817ff3234 | |||
| fb0400001a | |||
| 16591b3e80 | |||
| 28da32f573 | |||
| c687308d97 | |||
| 65c8e8542e | |||
| ca4cb648e7 | |||
| 53240c3d1a | |||
| 95b27f66fb | |||
| f31e146cda | |||
| 696939112a | |||
| 73ce97a63e | |||
| 5988ae3d3f | |||
| 58655814b2 | |||
| 11f1b6e72d | |||
| 8f537f33a7 | |||
| 4db72230ff | |||
| fd92793c2a | |||
| 05e45b6870 | |||
| 7521f906ed | |||
| 3222a0c497 | |||
| caf3d957cd | |||
| 005d00cfaa | |||
| c227d54f26 | |||
| bc62320b9f | |||
| 0ad32f676c | |||
| fff5f2d406 | |||
| be653b73ee | |||
| 4580f2a7bb | |||
| 5dd56892fb | |||
| e3460d94ed | |||
| 920ad5129a | |||
| fba08fe8dd | |||
| 5746a41905 | |||
| 361ae909e7 | |||
| 4b59660ea1 | |||
| cabf5d238e | |||
| 9c902b89ed | |||
| c31b8a5802 | |||
| 901c548d13 | |||
| b77de1e10d | |||
| b8c7fba7d8 | |||
| 466719fbc8 | |||
| 4f0ab71503 | |||
| daa4689f44 | |||
| 023a5aa221 | |||
| 291633c1d5 | |||
| 00990076fb | |||
| e672df7a60 | |||
| 82eb237117 | |||
| 832005565a | |||
| 894699f009 | |||
| f45dba258b | |||
| 17be49af70 | |||
| e91d36fc16 | |||
| 56f333180b | |||
| 9af8b0d353 | |||
| 57c0750980 | |||
| 8e36ee2e7d | |||
| ec2e527fd1 | |||
| d968e5d0c1 | |||
| 4257f21cae | |||
| 26e7059af2 | |||
| 94595f2a1f | |||
| 1fa76301e2 | |||
| ce2c608cc9 | |||
| 916f26dfda | |||
| a108edc125 | |||
| 1a1a8a851b | |||
| ddd1efeb58 | |||
| 6c30dd8509 | |||
| 21fe59f32c | |||
| f79b2b51c8 | |||
| b16e9ad964 | |||
| 65f8ba16b3 | |||
| 9b48993cd0 | |||
| 54cec3e821 | |||
| daeb972c8a | |||
| 73872e8926 | |||
| c15fa21a56 | |||
| e9d1080e5d | |||
| d0b3f45151 | |||
| 32b0b02d28 | |||
| d6617f61b7 | |||
| b5180f235f | |||
| e879eedbd6 | |||
| c7ca6800f7 | |||
| fed884a6ce | |||
| 02a77e5ce7 | |||
| 6f97399aeb | |||
| 1c4f0e67ab | |||
| ebed386d96 | |||
| e44f63feb7 | |||
| 34c5a3ab97 | |||
| eac47db058 | |||
| e72d2a750c | |||
| f54856c3f1 | |||
| 81f6bbe955 | |||
| c162c0a0ff | |||
| 385d05dcdb | |||
| 308ce4ac07 | |||
| f8a0241e1f | |||
| c74a5c8749 | |||
| defdd49a65 |
+1
-1
@@ -4,7 +4,7 @@ end_of_line = lf
|
|||||||
indent_size = 4
|
indent_size = 4
|
||||||
indent_style = space
|
indent_style = space
|
||||||
insert_final_newline = false
|
insert_final_newline = false
|
||||||
max_line_length = 100
|
max_line_length = 200
|
||||||
tab_width = 4
|
tab_width = 4
|
||||||
ij_continuation_indent_size = 8
|
ij_continuation_indent_size = 8
|
||||||
ij_formatter_off_tag = @formatter:off
|
ij_formatter_off_tag = @formatter:off
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
name: Android Test
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
name: Android Lint
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: 'ubuntu:android-24.04'
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
shell: bash
|
||||||
|
run: ./gradlew lint
|
||||||
|
|
||||||
|
android-check:
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: 'ubuntu:android-24.04'
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
shell: bash
|
||||||
|
run: ./gradlew testNightlyDebugUnitTest --stacktrace
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
steps:
|
|
||||||
build:
|
|
||||||
when:
|
|
||||||
event: [ push ]
|
|
||||||
image: androidsdk/android-31
|
|
||||||
commands:
|
|
||||||
- apt-get update
|
|
||||||
- apt-get install -y openjdk-17-jdk-headless
|
|
||||||
- update-java-alternatives -a
|
|
||||||
- ./gradlew lint
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
steps:
|
|
||||||
build:
|
|
||||||
when:
|
|
||||||
event: [ push ]
|
|
||||||
image: androidsdk/android-31
|
|
||||||
commands:
|
|
||||||
- apt-get update
|
|
||||||
- apt-get install -y openjdk-17-jdk-headless
|
|
||||||
- update-java-alternatives -a
|
|
||||||
- ./gradlew testNightlyDebugUnitTest --stacktrace
|
|
||||||
@@ -40,8 +40,7 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
Translations are hosted on <a href="https://translate.codeberg.org/engage/opentracks/">
|
Translations are hosted on <a href="https://translate.codeberg.org/engage/opentracks/">translate.codeberg.org/projects/opentracks/</a>.
|
||||||
translate.codeberg.org/projects/opentracks/</a>.
|
|
||||||
<a href="https://translate.codeberg.org/engage/opentracks/">
|
<a href="https://translate.codeberg.org/engage/opentracks/">
|
||||||
<img src="https://translate.codeberg.org/widget/opentracks/opentracks-src-main-res-values-strings-xml-main/horizontal-auto.svg" alt="Translation status" />
|
<img src="https://translate.codeberg.org/widget/opentracks/opentracks-src-main-res-values-strings-xml-main/horizontal-auto.svg" alt="Translation status" />
|
||||||
</a>
|
</a>
|
||||||
@@ -112,8 +111,7 @@ The API is disabled by default to protect the user's privacy, but it can easily
|
|||||||
`Classes`:
|
`Classes`:
|
||||||
* **Start a recording:** `de.dennisguse.opentracks.publicapi.StartRecording`
|
* **Start a recording:** `de.dennisguse.opentracks.publicapi.StartRecording`
|
||||||
* Set track data: `TRACK_NAME`, `TRACK_DESCRIPTION`, `TRACK_CATEGORY`, and `TRACK_ICON` (
|
* Set track data: `TRACK_NAME`, `TRACK_DESCRIPTION`, `TRACK_CATEGORY`, and `TRACK_ICON` (
|
||||||
non-localized identifier
|
see `ActivityType.id` defined in [/src/main/java/de/dennisguse/opentracks/util/TrackIconUtils.java#L38](/src/main/java/de/dennisguse/opentracks/data/model/ActivityType.java)).
|
||||||
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 (
|
NOTE: if `TRACK_ICON` is not present, `TRACK_CATEGORY` will be used to determine the icon (
|
||||||
localized).
|
localized).
|
||||||
* Send recorded data to another application via _Dashboard API_: `STATS_TARGET_PACKAGE` and
|
* Send recorded data to another application via _Dashboard API_: `STATS_TARGET_PACKAGE` and
|
||||||
@@ -134,6 +132,10 @@ adb shell am start -n "de.dennisguse.opentracks.playstore/de.dennisguse.opentrac
|
|||||||
```
|
```
|
||||||
|
|
||||||
## File formats compatibility with open-source software
|
## File formats compatibility with open-source software
|
||||||
|
|
||||||
|
KML/KMZ can be used to export indoor activities (no GPS present).
|
||||||
|
GPX exports only those points that have a GPS coordinate present (sensor data is aggregated though).
|
||||||
|
|
||||||
| | [GPX 1.1](https://www.topografix.com/GPX/1/1/) | [KML 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html) | [KMZ 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html) |
|
| | [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) |
|
| [OpenLayers 7.1.0](https://openlayers.org/) | ? | [no](https://github.com/openlayers/openlayers/issues/14104) | [no](https://github.com/openlayers/openlayers/issues/14104) |
|
||||||
@@ -144,7 +146,7 @@ adb shell am start -n "de.dennisguse.opentracks.playstore/de.dennisguse.opentrac
|
|||||||
| [SportsTracker](https://github.com/ssaring/sportstracker) | yes, [single tracks only](https://github.com/ssaring/sportstracker/issues/260) | no | no |
|
| [SportsTracker](https://github.com/ssaring/sportstracker) | yes, [single tracks only](https://github.com/ssaring/sportstracker/issues/260) | no | no |
|
||||||
| [ExifTool](https://exiftool.org) | [yes](https://exiftool.org/forum/index.php?topic=15972.0) | no | no |
|
| [ExifTool](https://exiftool.org) | [yes](https://exiftool.org/forum/index.php?topic=15972.0) | no | no |
|
||||||
| [Wanderer](https://wanderer.to/) | yes | yes | yes |
|
| [Wanderer](https://wanderer.to/) | yes | yes | yes |
|
||||||
|
| [GPX.Studio]https://gpx.studio/) | yes | no | no |
|
||||||
|
|
||||||
## Dashboard API (incl. map)
|
## Dashboard API (incl. map)
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
* Polar H10
|
* Polar H10
|
||||||
* Polar OH1
|
* Polar OH1
|
||||||
Has updatable firmware that requires a user account at polar.com
|
Has updatable firmware that requires a user account at polar.com
|
||||||
|
* Polar Loop Gen 2
|
||||||
* Wahoo Tickr (Model: WFBTHR02)
|
* Wahoo Tickr (Model: WFBTHR02)
|
||||||
* Mi Band 3, Amazfit Band 5, Amazfit Bip. These devices are manufactured by Huami, and other Huami devices will very likely work. Theoretically, this sensor must be enabled, but testing reveals that it works even without enabling. For details on how to enable see [Gadgetbridge wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Huami-Heartrate-measurement#bluetooth-heart-rate-sensor)
|
* Mi Band 3, Amazfit Band 5, Amazfit Bip. These devices are manufactured by Huami, and other Huami devices will very likely work. Theoretically, this sensor must be enabled, but testing reveals that it works even without enabling. For details on how to enable see [Gadgetbridge wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Huami-Heartrate-measurement#bluetooth-heart-rate-sensor)
|
||||||
* Amazfit Neo. But first you have to enable "Discoverable" and "Activity heart rate sharing" in the official Zepp app. (after pairing with OpenTracks you can disable them again)
|
* Amazfit Neo. But first you have to enable "Discoverable" and "Activity heart rate sharing" in the official Zepp app. (after pairing with OpenTracks you can disable them again)
|
||||||
@@ -44,7 +45,10 @@ However, often only one value is provided.
|
|||||||
We only support _Instantaneous Power_.
|
We only support _Instantaneous Power_.
|
||||||
|
|
||||||
For cycling (incl. cadence):
|
For cycling (incl. cadence):
|
||||||
* Assioma Duo Powermeter
|
|
||||||
|
* Assioma
|
||||||
|
* Duo Powermeter
|
||||||
|
* Pro MX-2
|
||||||
* Elite drivo II
|
* Elite drivo II
|
||||||
* QUARQ Red DZero Powermeter
|
* QUARQ Red DZero Powermeter
|
||||||
* Tacx Satori Smart
|
* Tacx Satori Smart
|
||||||
@@ -109,3 +113,16 @@ Here is a list of the smartphones tested:
|
|||||||
| Google | Nexus 4 | LineageOS 18.1 | Ok |
|
| Google | Nexus 4 | LineageOS 18.1 | Ok |
|
||||||
| Samsung | S4 | Android 11 | Ok |
|
| Samsung | S4 | Android 11 | Ok |
|
||||||
| Sony | Xperia XZ1 Compact | LineageOS 16.1 | Failure |
|
| Sony | Xperia XZ1 Compact | LineageOS 16.1 | Failure |
|
||||||
|
|
||||||
|
## Bosch Smart System eBike
|
||||||
|
|
||||||
|
OpenTracks also supports Bosch Smart System eBikes via Bosch's custom BLE service `00000010-eaa2-11e9-81b4-2a2ae2dbcce4`.
|
||||||
|
|
||||||
|
Tested with:
|
||||||
|
|
||||||
|
* Bosch Perfomance Line CX (SW-Version 16.9.0) / Kiox 300 (SW-Version 16.4.0)
|
||||||
|
|
||||||
|
Currently supported measurements:
|
||||||
|
|
||||||
|
* Human power
|
||||||
|
* Cadence
|
||||||
|
|||||||
+2
-5
@@ -11,9 +11,6 @@ CHANGELOG_FILE="$CHANGELOG_DIR/$VERSIONID.txt"
|
|||||||
|
|
||||||
git diff --exit-code || echo -e "\nWARNING WARNING WARNING WARNING WARNING: this branch contains uncommit changes."
|
git diff --exit-code || echo -e "\nWARNING WARNING WARNING WARNING WARNING: this branch contains uncommit changes."
|
||||||
|
|
||||||
#TODO Execute all tests: we are destroying data, running on a real device.
|
|
||||||
# ./gradlew connectedCheck
|
|
||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
echo -e "\nPrevious changelog\n------------"
|
echo -e "\nPrevious changelog\n------------"
|
||||||
PREVIOUS_CHANGELOG=$(find "$CHANGELOG_DIR" -iname "[[:digit:]]**.txt" | sort -n | tail -1)
|
PREVIOUS_CHANGELOG=$(find "$CHANGELOG_DIR" -iname "[[:digit:]]**.txt" | sort -n | tail -1)
|
||||||
@@ -34,8 +31,8 @@ gedit -w "$CHANGELOG_FILE"
|
|||||||
git add "$CHANGELOG_FILE"
|
git add "$CHANGELOG_FILE"
|
||||||
|
|
||||||
echo "Updating build.gradle"
|
echo "Updating build.gradle"
|
||||||
sed -i s/versionCode\ .*/versionCode\ "$VERSIONID"/g build.gradle
|
sed -i -e "s/versionCode\ [0-9]\{1,\}/versionCode\ $VERSIONID/g" build.gradle
|
||||||
sed -i s/versionName\ .*/versionName\ "\"$VERSIONNAME\""/g build.gradle
|
sed -i -e "s/versionName\ \"v[0-9\.]\{1,\}\"/versionName\ \"$VERSIONNAME\"/g" build.gradle
|
||||||
|
|
||||||
git diff
|
git diff
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -6,8 +6,13 @@ export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64/
|
|||||||
|
|
||||||
JAVA_VERSION=`java --version`
|
JAVA_VERSION=`java --version`
|
||||||
if [[ $JAVA_VERSION != "openjdk 21."* ]]; then
|
if [[ $JAVA_VERSION != "openjdk 21."* ]]; then
|
||||||
echo "OpenJDK version should be 21.X"
|
echo -e "OpenJDK version should be 21.X"
|
||||||
exit -1;
|
fi
|
||||||
|
|
||||||
|
git diff --exit-code || echo -e "\nWARNING WARNING WARNING WARNING WARNING: this branch contains uncommit changes."
|
||||||
|
|
||||||
|
if [[ $(git log "$(git describe --tags --abbrev=0)..HEAD" --no-merges --oneline | wc -l) -gt 0 ]]; then
|
||||||
|
echo -e "No version tag for this commit; please use RELEASE.sh";
|
||||||
fi
|
fi
|
||||||
|
|
||||||
RELEASE_STORE_PASSWORD=
|
RELEASE_STORE_PASSWORD=
|
||||||
|
|||||||
+45
-32
@@ -6,7 +6,7 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.11.1'
|
classpath 'com.android.tools.build:gradle:9.3.1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,11 +18,11 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def getVersionName = { ->
|
def getVersionName = { ->
|
||||||
return 'git describe --tags'.execute().text.trim()
|
return 'git describe --tags'.execute(null, project.rootDir).text.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
def getVersionCode = { ->
|
def getVersionCodeNightly = { ->
|
||||||
return Integer.valueOf('git rev-list HEAD --count'.execute().text.trim())
|
return Integer.valueOf('git rev-list HEAD --count'.execute(null, project.rootDir).text.trim())
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@@ -33,11 +33,12 @@ android {
|
|||||||
targetCompatibility JavaVersion.VERSION_17
|
targetCompatibility JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
|
|
||||||
compileSdk = 36
|
compileSdk = 37
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding = true
|
viewBinding = true
|
||||||
buildConfig = true
|
buildConfig = true
|
||||||
|
resValues = true
|
||||||
}
|
}
|
||||||
|
|
||||||
androidResources {
|
androidResources {
|
||||||
@@ -46,17 +47,28 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId 'de.dennisguse.opentracks'
|
applicationId 'de.dennisguse.opentracks'
|
||||||
versionCode 6291
|
versionCode 6741
|
||||||
versionName "v4.22.1"
|
versionName "v4.28.2"
|
||||||
|
|
||||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||||
|
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 36
|
targetSdk = 37
|
||||||
|
|
||||||
testInstrumentationRunner 'de.dennisguse.opentracks.TestRunner'
|
testInstrumentationRunner 'de.dennisguse.opentracks.TestRunner'
|
||||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||||
}
|
}
|
||||||
|
testOptions {
|
||||||
|
managedDevices {
|
||||||
|
localDevices {
|
||||||
|
mediumPhone {
|
||||||
|
device = "Medium Phone"
|
||||||
|
apiLevel = 35
|
||||||
|
systemImageSource = "aosp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
nightly {
|
nightly {
|
||||||
if (System.getProperty('nightly_store_file') != null) {
|
if (System.getProperty('nightly_store_file') != null) {
|
||||||
@@ -95,6 +107,11 @@ android {
|
|||||||
nightly {
|
nightly {
|
||||||
dimension 'version'
|
dimension 'version'
|
||||||
applicationId 'de.dennisguse.opentracks.nightly'
|
applicationId 'de.dennisguse.opentracks.nightly'
|
||||||
|
|
||||||
|
versionCode getVersionCodeNightly()
|
||||||
|
versionName getVersionName()
|
||||||
|
base.archivesName = "de.dennisguse.opentracks.nightly_${getVersionCodeNightly()}"
|
||||||
|
|
||||||
signingConfig = signingConfigs.nightly
|
signingConfig = signingConfigs.nightly
|
||||||
}
|
}
|
||||||
irreproducible {
|
irreproducible {
|
||||||
@@ -109,27 +126,13 @@ android {
|
|||||||
// PlayStore: https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore
|
// PlayStore: https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore
|
||||||
dimension 'version'
|
dimension 'version'
|
||||||
applicationId 'de.dennisguse.opentracks.playstore'
|
applicationId 'de.dennisguse.opentracks.playstore'
|
||||||
|
|
||||||
|
base.archivesName = "de.dennisguse.opentracks.playstore_${getVersionName()}_${android.defaultConfig.versionCode}"
|
||||||
|
|
||||||
signingConfig = signingConfigs.release
|
signingConfig = signingConfigs.release
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applicationVariants.configureEach { variant ->
|
|
||||||
variant.resValue 'string', 'applicationId', variant.applicationId
|
|
||||||
|
|
||||||
variant.outputs.configureEach {
|
|
||||||
if (variant.flavorName == 'nightly') {
|
|
||||||
setVersionCodeOverride(getVersionCode())
|
|
||||||
setVersionNameOverride(getVersionName())
|
|
||||||
}
|
|
||||||
|
|
||||||
if (variant.flavorName == 'reproducible') {
|
|
||||||
outputFileName = "${applicationId}_${variant.versionName}.apk"
|
|
||||||
} else {
|
|
||||||
outputFileName = "${applicationId}_${variant.versionCode}.apk"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependenciesInfo {
|
dependenciesInfo {
|
||||||
// Disables dependency metadata when building APKs.
|
// Disables dependency metadata when building APKs.
|
||||||
includeInApk = false
|
includeInApk = false
|
||||||
@@ -142,23 +145,33 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
|
||||||
|
|
||||||
implementation 'androidx.appcompat:appcompat:1.7.1'
|
implementation 'androidx.appcompat:appcompat:1.8.0'
|
||||||
implementation 'androidx.preference:preference:1.2.1'
|
implementation 'androidx.preference:preference:1.2.1'
|
||||||
implementation 'androidx.documentfile:documentfile:1.1.0'
|
implementation 'androidx.documentfile:documentfile:1.1.0'
|
||||||
implementation 'androidx.gridlayout:gridlayout:1.1.0'
|
implementation 'androidx.gridlayout:gridlayout:1.1.0'
|
||||||
implementation 'com.google.android.material:material:1.12.0'
|
implementation 'com.google.android.material:material:1.14.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
|
implementation 'androidx.constraintlayout:constraintlayout:2.2.2'
|
||||||
implementation 'androidx.core:core:1.17.0'
|
implementation 'androidx.core:core:1.19.0'
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
implementation 'androidx.core:core-splashscreen:1.2.0'
|
||||||
implementation 'androidx.mediarouter:mediarouter:1.8.1'
|
implementation 'androidx.mediarouter:mediarouter:1.8.1'
|
||||||
implementation 'androidx.core:core-location-altitude:1.0.0-alpha03'
|
implementation 'androidx.core:core-location-altitude:1.0.0'
|
||||||
|
implementation 'androidx.work:work-runtime:2.11.2'
|
||||||
|
implementation 'androidx.health.connect:connect-client:1.1.0'
|
||||||
|
|
||||||
androidTestImplementation 'androidx.test:core:1.7.0'
|
androidTestImplementation 'androidx.test:core:1.7.0'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
|
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
|
||||||
androidTestImplementation 'androidx.test:rules:1.7.0'
|
androidTestImplementation 'androidx.test:rules:1.7.0'
|
||||||
androidTestImplementation 'androidx.test:runner:1.7.0'
|
androidTestImplementation 'androidx.test:runner:1.7.0'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'
|
||||||
androidTestImplementation 'org.mockito:mockito-android:5.19.0'
|
|
||||||
|
|
||||||
androidTestUtil 'androidx.test:orchestrator:1.6.1'
|
androidTestUtil 'androidx.test:orchestrator:1.6.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
androidComponents {
|
||||||
|
onVariants(selector().all()) { variant ->
|
||||||
|
variant.resValues.put(
|
||||||
|
new com.android.build.api.variant.impl.ResValueKeyImpl("string", "applicationId"),
|
||||||
|
new com.android.build.api.variant.ResValue(variant.applicationId.get(), null)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
(versionName): OpenTracks
|
||||||
|
|
||||||
|
Ændringer:
|
||||||
|
-
|
||||||
|
|
||||||
|
Fejlrettelser:
|
||||||
|
-
|
||||||
|
|
||||||
|
Udvikler:
|
||||||
|
-
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
v3.0.1: OpenTracks
|
||||||
|
|
||||||
|
Änderungen:
|
||||||
|
- Markierungen teilen
|
||||||
|
- Umstrukturierte Einstellungen
|
||||||
|
- Zurück-Schaltfläche in der Aktionsleiste entfernt
|
||||||
|
- Export: Option zur Einstellung der Fotogröße entfernt
|
||||||
|
- Export: KML als Format ist jetzt auswählbar
|
||||||
|
|
||||||
|
Fehlerbehebungen:
|
||||||
|
- Exportverzeichnis ist jetzt /OpenTracks/
|
||||||
|
- Querformatmodus stürzt nicht mehr ab
|
||||||
|
- KML/KMZ-Export behoben
|
||||||
|
|
||||||
|
Entwickler:
|
||||||
|
- Tests sind ausführbar (aber viele schlagen fehl)
|
||||||
|
- Kontinuierliche Integration mit TravisCI aktiviert (keine Tests)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
v3.1.0: OpenTracks
|
||||||
|
|
||||||
|
Änderungen:
|
||||||
|
- Anzeige der Strecke in Karten-Apps hinzugefügt (getestet mit OsmAnd und Maps.ME)
|
||||||
|
- Während der Verfolgung werden Streckenstatistiken angezeigt, ohne das Gerät entsperren zu müssen
|
||||||
|
- Querformat für die Detailansicht der Strecke entfernt (war fehlerhaft)
|
||||||
|
- Verlauf der Suchvorschläge entfernt
|
||||||
|
|
||||||
|
Fehlerbehebung:
|
||||||
|
- Speicherberechtigung für „Alle importieren/Alle exportieren” anfordern
|
||||||
|
|
||||||
|
Entwickler:
|
||||||
|
- Upgrade auf Gradle 5.6
|
||||||
|
- Release-Builds nicht minimieren
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
v4.23.0: OpenTracks
|
||||||
|
|
||||||
|
Änderungen:
|
||||||
|
- Benutzeroberfläche: Durchschnittsgeschwindigkeit des aktuellen Segments hinzugefügt
|
||||||
|
(derzeit nur in neuen Layouts verfügbar)
|
||||||
|
- Durch Klicken auf die Benachrichtigung wird die eigentliche Aufzeichnungs-Benutzeroberfläche geöffnet
|
||||||
|
|
||||||
|
Entwickler:
|
||||||
|
- Veralteter JobIntentService durch WorkManager ersetzt
|
||||||
|
- Verwende .isEmpty() außer für CharSequence und Stream.toList()
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
v4.23.1: OpenTracks
|
||||||
|
|
||||||
|
Änderungen:
|
||||||
|
|
||||||
|
- Neue UI-Komponente zur Anzeige der verstrichenen Zeit im aktuellen Segment
|
||||||
|
|
||||||
|
Fehlerbehebung:
|
||||||
|
- Sofortiger Export nach Workout abgestürzt (eingeführt in v4.23.0)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
v4.23.2: OpenTracks
|
||||||
|
|
||||||
|
Änderungen:
|
||||||
|
- Vorausschauende Rückwärtsfunktion funktioniert vollständig
|
||||||
|
|
||||||
|
Fehlerbehebung:
|
||||||
|
- Absturz im Zusammenhang mit dem Dateinamen der Strecke in den Einstellungen behoben
|
||||||
|
|
||||||
|
Entwickler:
|
||||||
|
- Datenklasse für Statistiken eingeführt
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
v4.22.2: OpenTracks
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Voice annoucements setting for time and unit were linked
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
v4.23.0: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- UI: added average pace of current segment
|
||||||
|
(currently only available in new layouts)
|
||||||
|
- Clicking on notification opens actual recording UI
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- Deprecated JobIntentService replaced with WorkManager
|
||||||
|
- Use .isEmpty() except for CharSequence and Stream.toList()
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
v4.23.1: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- New UI component to show elapsed time in the current segment
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Instant post-workout export crashed (introduced in v4.23.0)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
v4.23.2: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Predictive back works completely
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Fixed in crash related to the track filename in the settings
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- Introduced data class for Statistics
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
v4.24.0: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- PublicAPI: start track only sets field if data is present
|
||||||
|
- Marker photos can be shared again
|
||||||
|
- Removed KMZ without photos
|
||||||
|
- Heart rate of 0.0 is treated as invalid (except for SensorStatistics)
|
||||||
|
- Removed import workaround for special coordinates (100/200)
|
||||||
|
(was used by MyTracks to export/import special trackpoints)
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- Track and Marker are records
|
||||||
|
- AggregatedStatistics are computed in database
|
||||||
|
- Split into TrackStatisticsUpdater and SegmentStatisticUpdater
|
||||||
|
- Introduced Iterator classes (instead of passing Cursor or List)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
v4.24.1: OpenTracks
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Restore compatibility with OSM Dashboard v6.4.1 and earlier (introduced in v4.24.0)
|
||||||
|
- Crash when using default track name by number (introduced in v4.24.0)
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
v4.24.2: OpenTracks
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Crash when select all (introduced in v4.24.0)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
v4.25.0: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Record button is centered
|
||||||
|
- Support for BLE thermometer (Environmental Sensing)
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- On import: get type from filename (not URI)
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- TrackPoint is a record
|
||||||
|
- Update to Android Gradle Plugin 9.0
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
v4.25.1: OpenTracks
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Since v4.24.0, the API for transferring data to OSM Dashboard was broken.
|
||||||
|
Sadly, the changes in OpenTracks could not be reverted.
|
||||||
|
Thus, the protocol version was bumped to 3.
|
||||||
|
This requires updating OSM Dashboard to a version AFTER v6.5.7.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
v4.25.2: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Restored compatibility with OSMDashboard (Dashboard API protocol version 2)
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Update UI immediately after resuming a track
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
v4.26.0: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- GPS is optional: configurable in the settings
|
||||||
|
- BLE sensor address/name is only shown after successful connection
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Altitude is now shown again in EGM2008 (introduced in v4.25.0)
|
||||||
|
- Altitude and speed were shown as 0.0 briefly when data was stored
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- Refactored sensor subsystem
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
v4.26.1: OpenTracks
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Crash when deleting a UI layout
|
||||||
|
- Crash when heartrate sensor data could not be decoded
|
||||||
|
- Crash when exporting zero tracks
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
v4.26.2: OpenTracks
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- GPS via Bluetooth is not (yet) supported
|
||||||
|
- Altitude type (EGM2008) wasn't shown
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
v4.27.0: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Sensor: power from Bosch eBike
|
||||||
|
- Sensors: include already bonded Bluetooth devices (like Bosch eBike)
|
||||||
|
- Barometer: enforce sampling frequency
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Dashboard API: did not provide update
|
||||||
|
- GPS could not be enabled with Bluetooth disabled
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- Removed Mockito
|
||||||
|
- UI tests: less flacky
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
v4.27.1: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Support for Bosch Smart System eBikes (cadence, human power)
|
||||||
|
- Add activity "gravel biking"
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- While recording: TrackRecordingService would not restart correctly if killed by Android
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- TargetSDK 37
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
v4.27.2: OpenTracks
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Not all valid timezone offsets could be set
|
||||||
|
- Fixed race condition for IDLE trackpoints
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
v4.28.0: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Support for exporting to Health Connect
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Dashboard API: when sharing multiple tracks, the currently recorded one wasn't updated
|
||||||
|
- Configuration setting for announcement of lap power wasn't stored correctly (only some languages affected)
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
v4.28.1: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- HealthConnect: sending total distance, speed and elevation gain
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Gravel activity was not selectable
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- Cleanup ContentProvider URLs
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
v4.28.2: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Import: ignore GPX waypoints without time
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Only start GPS once
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
v3.0.1: OpenTracks
|
||||||
|
|
||||||
|
Cambios:
|
||||||
|
- Compartir marcadores
|
||||||
|
- Configuración reestructurada
|
||||||
|
- Se ha eliminado el botón «Atrás» de la barra de acciones
|
||||||
|
- Exportación: se ha eliminado la opción de configuración del tamaño de las fotos
|
||||||
|
- Exportación: ahora se puede seleccionar el formato KML
|
||||||
|
|
||||||
|
Correcciones de errores:
|
||||||
|
- El directorio de exportación es ahora /OpenTracks/
|
||||||
|
- El modo horizontal ya no provoca fallos
|
||||||
|
- Se ha corregido la exportación a KML/KMZ
|
||||||
|
|
||||||
|
Desarrollador:
|
||||||
|
- Las pruebas son ejecutables (pero muchas fallan)
|
||||||
|
- Se ha habilitado la integración continua con TravisCI (sin pruebas)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
v3.1.0: OpenTracks
|
||||||
|
|
||||||
|
Cambios:
|
||||||
|
- Se agregó la capacidad de mostrar rutas en la aplicación de mapas (probado con OsmAnd y Maps.ME).
|
||||||
|
- Se ha añadido la posibilidad de visualizar las estadísticas de seguimiento sin desbloquear el dispositivo durante el mismo.
|
||||||
|
- Se ha eliminado el modo horizontal de la vista de detalles de la pista (debido a un error).
|
||||||
|
- El historial de sugerencias de búsqueda ha sido eliminado.
|
||||||
|
|
||||||
|
Corrección de errores:
|
||||||
|
- Solicitar permisos de almacenamiento para todas las importaciones/todas las exportaciones
|
||||||
|
|
||||||
|
Desarrolladores:
|
||||||
|
- Actualizar a Gradle 5.6
|
||||||
|
- No minimice la versión de lanzamiento
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
v3.2.0: OpenTracks
|
||||||
|
|
||||||
|
Cambios:
|
||||||
|
- Se ha eliminado el widget
|
||||||
|
- Se han incorporado iconos de Material Design
|
||||||
|
- Se han eliminado el snowboard y el esquí (se necesitan nuevos iconos)
|
||||||
|
|
||||||
|
Corrección de errores:
|
||||||
|
- Se ha corregido la funcionalidad de búsqueda
|
||||||
|
|
||||||
|
Desarrollador:
|
||||||
|
- Se han corregido (algunos) casos de prueba
|
||||||
|
- Se ha eliminado la funcionalidad «parcelable» en desuso
|
||||||
|
- Limpieza y reformateo a fondo
|
||||||
|
- No se utiliza la difusión para la comunicación entre la aplicación y el widget
|
||||||
|
- El archivo stats_content.xml no incluye otros archivos
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
v4.27.0: OpenTracks
|
||||||
|
|
||||||
|
Cambios:
|
||||||
|
- Sensor: potencia de la bicicleta eléctrica Bosch
|
||||||
|
- Sensores: se incluyen los dispositivos Bluetooth ya emparejados (como la bicicleta eléctrica Bosch)
|
||||||
|
- Barómetro: se aplica la frecuencia de muestreo
|
||||||
|
|
||||||
|
Corrección de errores:
|
||||||
|
- API del panel de control: no se proporcionaba la actualización
|
||||||
|
- No se podía activar el GPS con el Bluetooth desactivado
|
||||||
|
|
||||||
|
Desarrollador:
|
||||||
|
- Se ha eliminado Mockito
|
||||||
|
- Pruebas de la interfaz de usuario: menos inestables
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
v4.23.2: OpenTracks
|
||||||
|
|
||||||
|
Cambios:
|
||||||
|
- a volta predictiva funciona
|
||||||
|
|
||||||
|
Arranxo:
|
||||||
|
- do fallo que daba relacionado co nome do ficheiro nos axustes
|
||||||
|
|
||||||
|
Desenvolvemento:
|
||||||
|
- Engádese data class para as Estatísticas
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
v4.26.2: OpenTracks
|
||||||
|
|
||||||
|
Arranxos:
|
||||||
|
- (aínda) non hai compatibilidade co GPS vía Bluetooth
|
||||||
|
- non se mostraba a altitude tipo (EGM2008)
|
||||||
@@ -4,7 +4,7 @@ Modifiche:
|
|||||||
- Velocità degli annunci vocali configurabile
|
- Velocità degli annunci vocali configurabile
|
||||||
- API <26: L'icona del lanciatore omette lo sfondo
|
- API <26: L'icona del lanciatore omette lo sfondo
|
||||||
|
|
||||||
Bugfix:
|
Correzioni:
|
||||||
- UI di registrazione: mostra i valori corretti
|
- UI di registrazione: mostra i valori corretti
|
||||||
- Registrazione UI: mostra (di nuovo) l'altitudine in EGM2008
|
- Registrazione UI: mostra (di nuovo) l'altitudine in EGM2008
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
v4.22.0: OpenTracks
|
||||||
|
|
||||||
|
Modifiche:
|
||||||
|
- Migrazione a https://codeberg.org/OpenTracksApp/OpenTracks
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
v4.22.1: OpenTracks
|
||||||
|
|
||||||
|
Modifiche:
|
||||||
|
- Primo rilascio da Codeberg
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
v4.22.2: OpenTracks
|
||||||
|
|
||||||
|
Correzioni:
|
||||||
|
- L'impostazione degli annunci vocali per l'ora e l'unità di misura è stata collegata
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
v4.26.2: OpenTracks
|
||||||
|
|
||||||
|
Correzioni:
|
||||||
|
- Il GPS via Bluetooth non è (ancora) supportato
|
||||||
|
- Il tipo di altitudine (EGM2008) non viene mostrato
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
(versionName): OpenTracks
|
||||||
|
|
||||||
|
Modifiche:
|
||||||
|
-
|
||||||
|
|
||||||
|
Correzioni:
|
||||||
|
-
|
||||||
|
|
||||||
|
Sviluppatore:
|
||||||
|
-
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
Un tracker sportivo che rispetta la tua privacy.
|
||||||
|
|
||||||
|
Sport e attività all'aria aperta portano a felicità, allenamento e fiducia
|
||||||
|
|
||||||
|
Valorizza la tua salute tenendo traccia dei tuoi progressi.
|
||||||
|
Registra quando stai correndo o passeggiando e regalati un monitor più grande quando fai ciclismo.
|
||||||
|
Segna con fotografie i posti interessanti incrociati durante il tragitto.
|
||||||
|
Tieni traccia delle statistiche in dettaglio per analizzarle.
|
||||||
|
Condividi solo i dati che vuoi.
|
||||||
|
|
||||||
|
* Annunci vocali.
|
||||||
|
* Supporto a sensori Bluetooth LE: battito cardiaco, velocità e distanza (ciclismo), cadenza (ciclismo), rilevamento potenza (ciclismo).
|
||||||
|
* Altitudine positiva o negativa tramite sensore barometrico.
|
||||||
|
* Altitudine mostrata in EGM2008 (sopra il livello del mare) ed esportata come WGS84.
|
||||||
|
* Esporta tracce come KMZ (con foto incluse), KML o GPX.
|
||||||
|
* Non richiede accesso a internet o altri permessi.
|
||||||
|
* Temi chiaro e scuro, o seguendo la impostazioni di sistema.
|
||||||
|
* Nessuna pubblicità.
|
||||||
|
|
||||||
|
Libre software / Free Software / Open Source
|
||||||
|
cioè puoi usare, studiare, cambiare e condividere il codice sorgente.
|
||||||
|
Licenza: Apache 2.0
|
||||||
@@ -1 +1 @@
|
|||||||
Un tracker sportivo che rispetta completamente la tua privacy.
|
Un tracker sportivo che rispetta la tua privacy.
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
OpenTracks é um aplicativo de rastreamento de esportes que respeita totalmente a sua privacidade.
|
Um companheiro de rastreamento esportivo que respeita sua privacidade.
|
||||||
|
|
||||||
Características:
|
Esportes e atividades ao ar livre garantem felicidade, habilidades e confiança.
|
||||||
* Rastreamento de esportes: monitore seu esporte e atividades ao ar livre
|
|
||||||
* Fotos e marcadores: marque locais interessantes durante o rastreamento
|
Valoriza sua saúde acompanhando os seus treinos.
|
||||||
* Ganho e perda de altitude: via sensor barométrico
|
Grava enquanto você corre ou caminha e te entrega um computador de bicicleta com uma tela maior para o ciclismo.
|
||||||
* Altitude mostrada no EGM2008 (acima do nível médio do mar); exportado como WGS84
|
Marca locais interessantes durante o percurso com fotos.
|
||||||
* Anúncios de voz
|
Deixa estatísticas registradas em grande detalhe para analises.
|
||||||
* Suporta sensores Bluetooth LE: frequência cardíaca, velocidade e distância (ciclismo), cadência (ciclismo) e medidor de potência (ciclismo)
|
Compartilha apenas os dados que você quer que outros tenham.
|
||||||
* Exportar dados: exporte trilhas como KMZ (incluindo fotos), KML, ou GPX
|
|
||||||
* Sem acesso à Internet: a Internet não é usada
|
* Anúncios por voz.
|
||||||
|
* Suporte para sensores Bluetooth LE: frequência cardíaca, velocidade e distância (ciclismo), cadência (ciclismo) e medição de potência (ciclismo).
|
||||||
|
* Ganhos e perdas de altitude: via sensor barométrico.
|
||||||
|
* Altitudes exibidas em EGM2008 (acima do nível médio do mar); exportado como WGS84.
|
||||||
|
* Exporta os dados de rastreio como KMZ (inclusive fotos), KML ou GPX.
|
||||||
|
* Sem acesso à internet ou permissões extra.
|
||||||
|
* Tema claro e escuro, respeitando as configurações do sistema.
|
||||||
|
* Sem propagandas.
|
||||||
|
|
||||||
|
Software Livre / Código Aberto
|
||||||
|
Significa que você pode usar, estudar, mudar e compartilhar o código.
|
||||||
|
Licença Apache 2.0
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
v3.0.0: Opentracks
|
v3.0.0: திறந்தபாதைகள்
|
||||||
|
|
||||||
ஓபன்ட்ராக்சின் முதல் வெளியீடு (அதாவது, மைட்ராக்சிலிருந்து ஆர்ட் ஃபோர்க்)
|
திறந்தபாதைகளின் முதல் வெளியீடு (அதாவது, மைட்ராக்சிலிருந்து ஆர்ட் ஃபோர்க்)
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- மைட்ராக்சிலிருந்து ஓபன்ட்ராக்சுக்கு மறுபெயரிடப்பட்டது
|
- மைட்ராக்சிலிருந்து ஓபன்ட்ராக்சுக்கு மறுபெயரிடப்பட்டது
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
v3.8.0: Opentracks
|
v3.8.0: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றத்தை உடைத்தல்:
|
மாற்றத்தை உடைத்தல்:
|
||||||
- சிபிஎக்ச் இறக்குமதி/ஏற்றுமதி விரைவு, கேடென்ச் மற்றும் ஆர்ட்ரேட் டிராக் பாயிண்ட் எக்ச்டென்சன்/வி 2 வழியாக
|
- சிபிஎக்ச் இறக்குமதி/ஏற்றுமதி விரைவு, கேடென்ச் மற்றும் ஆர்ட்ரேட் டிராக் பாயிண்ட் எக்ச்டென்சன்/வி 2 வழியாக
|
||||||
@@ -6,7 +6,7 @@ v3.8.0: Opentracks
|
|||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- டாச்போர்டு ஏபிஐ: குறிப்பான்களுக்கான அணுகலை வழங்குதல் (ஓபன்ட்ராக்சில் காண்பிக்க திரும்பப்பெறுதல்)
|
- டாச்போர்டு ஏபிஐ: குறிப்பான்களுக்கான அணுகலை வழங்குதல் (ஓபன்ட்ராக்சில் காண்பிக்க திரும்பப்பெறுதல்)
|
||||||
- புதிய மார்க்கர் படவுரு (ஆரஞ்சு புச் முள், எச்.வி.சி)
|
- புதிய மார்க்கர் படவுரு (ஆரஞ்சு புச் முள், எச்.வி.சி)
|
||||||
- இறக்குமதிக்கான கோப்புகளை கோப்பு மேலாளர் வழியாக தேர்ந்தெடுக்கலாம்
|
- இறக்குமதிக்கான கோப்புகளைக் கோப்பு மேலாளர் வழியாகத் தேர்ந்தெடுக்கலாம்
|
||||||
|
|
||||||
பிழைத்திருத்தங்கள்:
|
பிழைத்திருத்தங்கள்:
|
||||||
- ஒருங்கிணைந்த புள்ளிவிவரங்கள் இடைமுகம் விருப்பமான விகிதத்தை மதிக்கிறது
|
- ஒருங்கிணைந்த புள்ளிவிவரங்கள் இடைமுகம் விருப்பமான விகிதத்தை மதிக்கிறது
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
v3.9.0: Opentracks
|
v3.9.0: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- தடங்களை அவற்றின் UUID உடன் ஏற்றுமதி செய்யுங்கள்
|
- தடங்களை அவற்றின் UUID உடன் ஏற்றுமதி செய்யுங்கள்
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
v3.12.0: ஓபன்ட்ராக்ச்
|
v3.12.0: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- ஊடலை சென்சார் முகவரிகள்
|
- ஊடலை சென்சார் முகவரிகள்
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
v3.15.0: ஓபன்ட்ராக்ச்
|
v3.15.0: திறந்தபாதைகள்
|
||||||
|
|
||||||
எச்சரிக்கை:
|
எச்சரிக்கை:
|
||||||
இந்த வெளியீடு மாற்ற முடியாத தரவுத்தள மாற்றங்களைப் பயன்படுத்துகிறது.
|
இந்த வெளியீடு மாற்ற முடியாத தரவுத்தள மாற்றங்களைப் பயன்படுத்துகிறது.
|
||||||
@@ -14,7 +14,7 @@ v3.15.0: ஓபன்ட்ராக்ச்
|
|||||||
(தயவுசெய்து OSMDashboard 2.10.0 ஐ மேம்படுத்தவும்)
|
(தயவுசெய்து OSMDashboard 2.10.0 ஐ மேம்படுத்தவும்)
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- KML/KMZ சரியாக காப்புப் பிரதி எடுக்கவும் மீட்டமைக்கவும் பயன்படுத்தலாம் (புள்ளிவிவரங்கள் உட்பட)
|
- KML/KMZ சரியாகக் காப்புப் பிரதி எடுக்கவும் மீட்டமைக்கவும் பயன்படுத்தலாம் (புள்ளிவிவரங்கள் உட்பட)
|
||||||
|
|
||||||
டெவலப்பர்:
|
டெவலப்பர்:
|
||||||
- மறுவேலை செய்யப்பட்ட தட பிரிவு மற்றும் அது சேமிப்பு (முன்னர் இடைநிறுத்தம்/மீண்டும் மார்க்கர்)
|
- மறுவேலை செய்யப்பட்ட தட பிரிவு மற்றும் அது சேமிப்பு (முன்னர் இடைநிறுத்தம்/மீண்டும் மார்க்கர்)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
v3.16.0: ஓபன்ட்ராக்ச்
|
v3.16.0: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- சைக்கிள் ஓட்டுதல்: பி.எல்.இ வேக சென்சாரிலிருந்து தூரத்தைப் பெறுங்கள்
|
- சைக்கிள் ஓட்டுதல்: பி.எல்.இ வேக சென்சாரிலிருந்து தூரத்தைப் பெறுங்கள்
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
v3.17.0: Opentracks
|
v3.17.0: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- கயாக்கிங்கிற்கு ஐகானைச் சேர்க்கவும்
|
- கயாக்கிங்கிற்கு ஐகானைச் சேர்க்கவும்
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
v3.18.0: ஓபன்ட்ராக்ச்
|
v3.18.0: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- உயரம் EGM2008 இல் காட்டப்பட்டுள்ளது (WGS84 எனச் சேமிக்கப்படுகிறது/ஏற்றுமதி செய்யப்படுகிறது)
|
- உயரம் EGM2008 இல் காட்டப்பட்டுள்ளது (WGS84 எனச் சேமிக்கப்படுகிறது/ஏற்றுமதி செய்யப்படுகிறது)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
v3.18.3: ஓபன்ட்ராக்ச்
|
v3.18.3: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- KML பாகுபடுத்தலுக்கு/GX: KOURS இன் ஆர்டர் தேவையில்லை
|
- KML பாகுபடுத்தலுக்கு/GX: KOURS இன் ஆர்டர் தேவையில்லை
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
v3.18.6: Opentracks
|
v3.18.6: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- "வரைபடத்தில் காட்டு"- KMZ க்கு பதிலாக KML வழியாகச் செயல்பாடு
|
- "வரைபடத்தில் காட்டு"- KMZ க்கு பதிலாக KML வழியாகச் செயல்பாடு
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
v3.19.0: Opentracks
|
v3.19.0: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- இயங்கும் விரைவு மற்றும் கேடென்ச் (ஆர்.சி.எச்) சென்சார்களுக்கான உதவி செயல்படுத்தப்பட்டது
|
- இயங்கும் விரைவு மற்றும் கேடென்ச் (ஆர்.சி.எச்) சென்சார்களுக்கான உதவி செயல்படுத்தப்பட்டது
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
v3.20.2: ஓபன்ட்ராக்ச்
|
v3.20.2: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- மிதவைகளுக்குப் பதிலாக உயரத்தை ஆதாயம் மற்றும் இழப்பு என்பது எண்கள்
|
- மிதவைகளுக்குப் பதிலாக உயரத்தை ஆதாயம் மற்றும் இழப்பு என்பது எண்கள்
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
v3.21.0: Opentracks
|
v3.21.0: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- பதிவு செய்யும்போது: டிராக் பாயிண்டுகளின் செயலற்ற அல்லாத உருவாக்கத்தை அகற்று
|
- பதிவு செய்யும்போது: டிராக் பாயிண்டுகளின் செயலற்ற அல்லாத உருவாக்கத்தை அகற்று
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
v3.24.0: Opentracks
|
v3.24.0: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- சி.பி.எச் விருப்பமானது (உட்புற பயன்பாட்டிற்கும்): தூரத்தை வழங்கும் பி.எல்.இ சென்சார்களைப் பயன்படுத்தினால் பயன்படுத்தப்பட்டால்
|
- சி.பி.எச் விருப்பமானது (உட்புற பயன்பாட்டிற்கும்): தூரத்தை வழங்கும் பி.எல்.இ சென்சார்களைப் பயன்படுத்தினால் பயன்படுத்தப்பட்டால்
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
v3.24.1: Opentracks
|
v3.24.1: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- பிற பயன்பாடுகளிலிருந்து பதிவுகளைத் தொடங்க பநிஇ சேர்க்கப்பட்டது (இயல்பாக முடக்கப்பட்டுள்ளது)
|
- பிற பயன்பாடுகளிலிருந்து பதிவுகளைத் தொடங்க பநிஇ சேர்க்கப்பட்டது (இயல்பாக முடக்கப்பட்டுள்ளது)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
V3.27.0: Opentracks
|
V3.27.0: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- குரல் அன்னியூசென்ச்: 100 மீ, 200 மீ மற்றும் 500 மீ
|
- குரல் அன்னியூசென்ச்: 100 மீ, 200 மீ மற்றும் 500 மீ
|
||||||
- இடைவெளி: 100 மீ மற்றும் 500 மீ சேர்க்கவும்
|
- இடைவெளி: 100 மீ மற்றும் 500 மீ சேர்
|
||||||
- கடிகாரம் இப்போது விநாடிகளையும் காட்டுகிறது
|
- கடிகாரம் இப்போது விநாடிகளையும் காட்டுகிறது
|
||||||
- சி.பி.எக்ச், கே.எம்.எல், கே.எம்.இசட்: கிடைமட்ட துல்லியத்தை ஏற்றுமதி செய்து இறக்குமதி செய்க
|
- சி.பி.எக்ச், கே.எம்.எல், கே.எம்.இசட்: கிடைமட்ட துல்லியத்தை ஏற்றுமதி செய்து இறக்குமதி செய்க
|
||||||
- சி.பி.எக்ச்: கிளச்ட்ரச்ட் நீட்டிப்பு வழியாக ஒட்டுமொத்த தூரத்தை ஏற்றுமதி செய்யுங்கள்
|
- சி.பி.எக்ச்: கிளச்ட்ரச்ட் நீட்டிப்பு வழியாக ஒட்டுமொத்த தூரத்தை ஏற்றுமதி செய்யுங்கள்
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
v4.0.0: Opentracks
|
v4.0.0: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- கீழ் பயன்பாட்டு பட்டியுடன் இடைமுகம் மறுசீரமைப்பு.
|
- கீழ் பயன்பாட்டு பட்டியுடன் இடைமுகம் மறுசீரமைப்பு.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
V4.0.1: Opentracks
|
V4.0.1: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- குரல் அறிவிப்புகள் இப்போது கட்டமைக்கப்படுகின்றன
|
- குரல் அறிவிப்புகள் இப்போது கட்டமைக்கப்படுகின்றன
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
V4.9.0: Opentracks
|
V4.9.0: திறந்தபாதைகள்
|
||||||
|
|
||||||
குறிப்பு: மாற்றத்தை உடைத்தல்
|
குறிப்பு: மாற்றத்தை உடைத்தல்
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
V4.10.0: Opentracks
|
V4.10.0: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- BLE சுற்றுச்சூழல் உணர்திறன் (காற்றழுத்தமானி) வழியாக உதவி ஆதாயம்/இழப்பு
|
- BLE சுற்றுச்சூழல் உணர்திறன் (காற்றழுத்தமானி) வழியாக உதவி ஆதாயம்/இழப்பு
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
v4.13.0: ஓபன்ட்ராக்ச்
|
v4.13.0: திறந்தபாதைகள்
|
||||||
|
|
||||||
மாற்றங்கள்:
|
மாற்றங்கள்:
|
||||||
- பொது பநிஇ வழியாகக் குறிப்பான்களை உருவாக்க முடியும்
|
- பொது பநிஇ வழியாகக் குறிப்பான்களை உருவாக்க முடியும்
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
v4.22.0: OpenTracks
|
||||||
|
|
||||||
|
மாற்றங்கள்:
|
||||||
|
- https://codeberg.org/OpenTracksApp/OpenTracks க்கு இடம்பெயர்தல்
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
v4.22.1: OpenTracks
|
||||||
|
|
||||||
|
மாற்றங்கள்:
|
||||||
|
- கோட்பெர்க் வழியாக முதல் வெளியீடு
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
v4.22.2: திறந்தபாதைகள்
|
||||||
|
பிழைத்திருத்தம்:
|
||||||
|
- நேரம் மற்றும் அலகுக்கான குரல் அறிவிப்பு அமைப்பு இணைக்கப்பட்டது
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
v4.23.0: OpenTracks
|
||||||
|
|
||||||
|
மாற்றங்கள்:
|
||||||
|
- UI: தற்போதைய பிரிவின் சராசரி வேகத்தைச் சேர்த்தது
|
||||||
|
(தற்போது புதிய தளவமைப்புகளில் மட்டுமே கிடைக்கிறது)
|
||||||
|
- அறிவிப்பைக் சொடுக்கு செய்வதன் மூலம் உண்மையான பதிவு இடைமுகம் திறக்கும்
|
||||||
|
|
||||||
|
டெவலப்பர்:
|
||||||
|
- நிராகரிக்கப்பட்ட JobIntentService, WorkManager உடன் மாற்றப்பட்டது
|
||||||
|
- CharSequence மற்றும் Stream.toList() தவிர .isEmpty() ஐப் பயன்படுத்தவும்
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
v4.23.1: OpenTracks
|
||||||
|
|
||||||
|
மாற்றங்கள்:
|
||||||
|
- தற்போதைய பிரிவில் கழிந்த நேரத்தைக் காட்ட புதிய இடைமுகம் கூறு
|
||||||
|
|
||||||
|
பிழைத்திருத்தம்:
|
||||||
|
- உடற்பயிற்சிக்குப் பிந்தைய உடனடி ஏற்றுமதி செயலிழந்தது (v4.23.0 இல் அறிமுகப்படுத்தப்பட்டது)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
v4.23.2: OpenTracks
|
||||||
|
|
||||||
|
மாற்றங்கள்:
|
||||||
|
- முன்கணிப்பு முற்றிலும் வேலை செய்கிறது
|
||||||
|
|
||||||
|
பிழைத்திருத்தம்:
|
||||||
|
- அமைப்புகளில் டிராக் கோப்புப் பெயருடன் தொடர்புடைய செயலிழப்பில் சரி செய்யப்பட்டது
|
||||||
|
|
||||||
|
டெவலப்பர்:
|
||||||
|
- புள்ளிவிவரங்களுக்கான தரவு வகுப்பு அறிமுகப்படுத்தப்பட்டது
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
v4.24.0: OpenTracks
|
||||||
|
|
||||||
|
மாற்றங்கள்:
|
||||||
|
- PublicAPI: தரவு இருந்தால் ஸ்டார்ட் டிராக் மட்டுமே புலத்தை அமைக்கும்
|
||||||
|
- மார்க்கர் புகைப்படங்களை மீண்டும் பகிரலாம்
|
||||||
|
- புகைப்படங்கள் இல்லாமல் KMZ அகற்றப்பட்டது
|
||||||
|
- இதயத் துடிப்பு 0.0 தவறானதாகக் கருதப்படுகிறது (சென்சார் ஸ்டாடிஸ்டிக்ஸ் தவிர)
|
||||||
|
- சிறப்பு ஒருங்கிணைப்புகளுக்கான இறக்குமதி தீர்வு நீக்கப்பட்டது (100/200)
|
||||||
|
(சிறப்பு டிராக் பாயிண்ட்களை ஏற்றுமதி/இறக்குமதி செய்ய MyTracks பயன்படுத்தியது)
|
||||||
|
|
||||||
|
டெவலப்பர்:
|
||||||
|
- ட்ராக் மற்றும் மார்க்கர் பதிவுகள்
|
||||||
|
- திரட்டப்பட்ட புள்ளிவிவரங்கள் தரவுத்தளத்தில் கணக்கிடப்படுகின்றன
|
||||||
|
- TrackStatisticsUpdater மற்றும் SegmentStatisticUpdater எனப் பிரிக்கவும்
|
||||||
|
- அறிமுகப்படுத்தப்பட்ட இட்டரேட்டர் வகுப்புகள் (கர்சர் அல்லது பட்டியலைக் கடந்து செல்வதற்குப் பதிலாக)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
v4.24.1: OpenTracks
|
||||||
|
|
||||||
|
பிழைத்திருத்தம்:
|
||||||
|
- OSM டாஷ்போர்டு v6.4.1 மற்றும் அதற்கு முந்தையவற்றுடன் இணக்கத்தன்மையை மீட்டெடுக்கவும் (v4.24.0 இல் அறிமுகப்படுத்தப்பட்டது)
|
||||||
|
- எண்ணின்படி இயல்புநிலை ட்ராக் பெயரைப் பயன்படுத்தும்போது செயலிழக்கிறது (v4.24.0 இல் அறிமுகப்படுத்தப்பட்டது)
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
v4.24.2: OpenTracks
|
||||||
|
|
||||||
|
பிழைத்திருத்தம்:
|
||||||
|
- அனைத்தையும் தேர்ந்தெடுக்கும்போது செயலிழக்க (v4.24.0 இல் அறிமுகப்படுத்தப்பட்டது)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
v4.25.0: OpenTracks
|
||||||
|
|
||||||
|
மாற்றங்கள்:
|
||||||
|
- பதிவுப் பொத்தான் மையமாக உள்ளது
|
||||||
|
- BLE தெர்மோமீட்டருக்கான ஆதரவு (சுற்றுச்சூழல் உணர்தல்)
|
||||||
|
|
||||||
|
பிழைத்திருத்தம்:
|
||||||
|
- இறக்குமதியில்: கோப்பு பெயரிலிருந்து வகையைப் பெறுங்கள் (URI அல்ல)
|
||||||
|
|
||||||
|
டெவலப்பர்:
|
||||||
|
- TrackPoint ஒரு பதிவு
|
||||||
|
- Android Gradle செருகுநிரல் 9.0 க்கு புதுப்பிக்கவும்
|
||||||
@@ -1,22 +1,22 @@
|
|||||||
一款尊重您的隐私的运动追踪应用。
|
尊重您隐私的运动追踪伙伴。
|
||||||
|
|
||||||
健康幸福的生活总少不了各种运动和户外活动。
|
运动与户外活动能带来快乐、提升技能、增强自信。
|
||||||
|
|
||||||
使用本应用,您可以保持对您的训练数据的有效追踪,为您的健康助力。
|
通过跟踪训练数据,珍视您的健康。
|
||||||
本应用可以在您跑步或散步时记录数据,也可以在您骑车时作为一个大屏幕自行车电脑。
|
记录您的跑步或步行数据,并提供大屏的专用骑行码表。
|
||||||
您可以为路上的有趣位置创建标记,还可以用图片作为标记。
|
用照片标记沿途有趣的地点。
|
||||||
您的数据会被详尽地记录下来,供您查阅分析。
|
保留详细记录的统计数据以供分析。
|
||||||
您的数据只会分享给您想要分享给的人。
|
仅分享您愿意公开的数据。
|
||||||
|
|
||||||
* 语音播报
|
* 语音播报。
|
||||||
* 支持蓝牙LE传感器:心率、速度和距离(骑车)、踏频(骑车)和功率计(骑车)。
|
* 支持蓝牙低功耗传感器:心率、速度和距离(骑行)、踏频(骑行)、功率计(骑行)。
|
||||||
* 海拔增减:通过气压传感器。
|
* 海拔上升和下降:通过气压传感器。
|
||||||
* EGM2008 海拔显示(平均海平面以上);导出为 WGS84。
|
* 采用 EGM2008 显示海拔(平均海平面以上);导出为 WGS84。
|
||||||
* 导出路线数据,支持 KMZ(包括照片)、KML 和 GPX 格式。
|
* 以 KMZ(含照片)、KML 或 GPX 格式导出轨迹数据。
|
||||||
* 不需要联网,不需要额外权限。
|
* 无需互联网访问或额外权限。
|
||||||
* 深色和浅色主题,跟随系统设置。
|
* 深色和浅色主题,跟随系统设置。
|
||||||
* 没有广告。
|
* 无广告。
|
||||||
|
|
||||||
本应用是开源软件
|
自由软件/开放源代码
|
||||||
意味着您可以使用、学习、修改和分发本应用的源代码。
|
意味着您可以使用、研究、修改和分享源代码。
|
||||||
本应用使用 Apache 2.0 协议发布。
|
采用 Apache 2.0 许可
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
一款尊重您的隐私的运动追踪应用。
|
尊重您隐私的运动追踪伙伴。
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
@@ -114,7 +114,6 @@ case "$( uname )" in #(
|
|||||||
NONSTOP* ) nonstop=true ;;
|
NONSTOP* ) nonstop=true ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH="\\\"\\\""
|
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
@@ -172,7 +171,6 @@ fi
|
|||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
if "$cygwin" || "$msys" ; then
|
if "$cygwin" || "$msys" ; then
|
||||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
|
||||||
|
|
||||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
@@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
|||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
-classpath "$CLASSPATH" \
|
|
||||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||||
"$@"
|
"$@"
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
-2
@@ -70,11 +70,10 @@ goto fail
|
|||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|
||||||
set CLASSPATH=
|
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h1>OpenTracks: Privacy Policy</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
OpenTracks does only store data on the local device that is relevant for tracking your sport exercise.
|
||||||
|
Stored data is not transmitted from the app itself to a third party.
|
||||||
|
|
||||||
|
Data is stored by OpenTracks until:
|
||||||
|
1. the user deletes the recorded track or
|
||||||
|
2. the user uninstalls OpenTracks
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
||||||
|
}
|
||||||
@@ -2,72 +2,117 @@ package de.dennisguse.opentracks;
|
|||||||
|
|
||||||
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
import static androidx.test.espresso.Espresso.onView;
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||||
import static androidx.test.espresso.action.ViewActions.longClick;
|
import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withParentIndex;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||||
|
import static androidx.test.espresso.matcher.RootMatchers.isDialog;
|
||||||
|
import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.performClick;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.performClickOnAncestor;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.performLongClickOnAncestor;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.waitUntilDisplayed;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.waitUntilRecyclerViewItemCount;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.withRecyclerViewItemCount;
|
||||||
|
import static de.dennisguse.opentracks.util.TestEnvironmentUtils.resetTrackRecordingServiceAndDeleteTracks;
|
||||||
|
import static de.dennisguse.opentracks.util.TrackUiTestUtils.renameStoppedTrack;
|
||||||
|
import static de.dennisguse.opentracks.util.TrackUiTestUtils.startRecordingFromTrackList;
|
||||||
|
import static de.dennisguse.opentracks.util.TrackUiTestUtils.stopRecordingToTrackStopped;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
import static de.dennisguse.opentracks.util.EspressoUtils.childAtPosition;
|
|
||||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
|
||||||
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.LargeTest;
|
import androidx.test.filters.LargeTest;
|
||||||
import androidx.test.rule.GrantPermissionRule;
|
import androidx.test.rule.GrantPermissionRule;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class EspressoDeleteTrackTest {
|
public class EspressoDeleteTrackTest {
|
||||||
|
|
||||||
|
private static final Duration UI_TIMEOUT = Duration.ofSeconds(10);
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
resetTrackRecordingServiceAndDeleteTracks();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void espressoDeleteTrackTest() {
|
public void deleteTrackFromTrackStoppedActivity() {
|
||||||
// TrackListActivity: start recording
|
recordTrackAndOpenTrackStoppedActivity();
|
||||||
onView(withId(R.id.track_list_fab_action)).perform(click());
|
|
||||||
|
|
||||||
// TrackRecordingActivity
|
// TrackStoppedActivity: discard the just-recorded track directly
|
||||||
onView(withId(R.id.track_recording_fab_action))
|
onView(withId(R.id.discard_button))
|
||||||
// wait; stay recording
|
.perform(performClick());
|
||||||
.perform(waitFor(5000))
|
onView(withText(android.R.string.ok))
|
||||||
// stop;
|
.inRoot(isDialog())
|
||||||
.perform(longClick());
|
.perform(performClick());
|
||||||
|
waitUntilDisplayed(withId(R.id.track_list), UI_TIMEOUT);
|
||||||
|
waitUntilRecyclerViewItemCount(withId(R.id.track_list), 0, UI_TIMEOUT);
|
||||||
|
|
||||||
// TrackStoppedActivity
|
// the recycler is empty again after discarding the only recorded track
|
||||||
onView(withId(R.id.finish_button)).perform(click());
|
onView(withId(R.id.track_list))
|
||||||
|
.check(matches(withRecyclerViewItemCount(0)));
|
||||||
|
}
|
||||||
|
|
||||||
// select track
|
@Test
|
||||||
onView(allOf(withParent(withId(R.id.track_list)), withParentIndex(0))).perform(longClick());
|
public void deleteTrackFromSelectedTrackInTrackList() {
|
||||||
|
String uniqueTrackName = "DeleteFromTrackList-" + System.currentTimeMillis();
|
||||||
|
|
||||||
// open menu and delete selected track
|
recordTrackAndOpenTrackStoppedActivity();
|
||||||
//TODO openActionBarOverflowOrOptionsMenu(); doesn't work
|
renameStoppedTrack(uniqueTrackName, UI_TIMEOUT);
|
||||||
onView(
|
|
||||||
allOf(withContentDescription("More options"),
|
|
||||||
childAtPosition(
|
|
||||||
childAtPosition(
|
|
||||||
withId(androidx.appcompat.R.id.action_mode_bar),
|
|
||||||
1),
|
|
||||||
2),
|
|
||||||
isDisplayed()))
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
onView(withText("Delete")).perform(click());
|
onView(withId(R.id.finish_button))
|
||||||
|
.perform(performClick());
|
||||||
|
waitUntilDisplayed(withId(R.id.track_list), UI_TIMEOUT);
|
||||||
|
waitUntilDisplayed(allOf(withId(R.id.track_list_item_name), withText(uniqueTrackName)), UI_TIMEOUT);
|
||||||
|
waitUntilRecyclerViewItemCount(withId(R.id.track_list), 1, UI_TIMEOUT);
|
||||||
|
|
||||||
onView(withText("OK")).perform(click());
|
onView(allOf(withId(R.id.track_list_item_name), withText(uniqueTrackName)))
|
||||||
|
.perform(performLongClickOnAncestor());
|
||||||
|
|
||||||
// tracklist is empty now
|
clickDeleteSelectedTrackAction();
|
||||||
onView(allOf(withText("Start recording your next adventure here"), isDisplayed()));
|
onView(withText(android.R.string.ok))
|
||||||
|
.inRoot(isDialog())
|
||||||
|
.perform(performClick());
|
||||||
|
waitUntilRecyclerViewItemCount(withId(R.id.track_list), 0, UI_TIMEOUT);
|
||||||
|
|
||||||
|
onView(withId(R.id.track_list))
|
||||||
|
.check(matches(withRecyclerViewItemCount(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recordTrackAndOpenTrackStoppedActivity() {
|
||||||
|
startRecordingFromTrackList(UI_TIMEOUT);
|
||||||
|
stopRecordingToTrackStopped(UI_TIMEOUT, R.id.discard_button, R.id.finish_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clickDeleteSelectedTrackAction() {
|
||||||
|
try {
|
||||||
|
waitUntilDisplayed(withId(R.id.list_context_menu_delete), UI_TIMEOUT);
|
||||||
|
onView(withId(R.id.list_context_menu_delete))
|
||||||
|
.perform(performClick());
|
||||||
|
} catch (AssertionError ignored) {
|
||||||
|
onView(allOf(
|
||||||
|
withContentDescription(androidx.appcompat.R.string.abc_action_menu_overflow_description),
|
||||||
|
isDescendantOfA(withId(androidx.appcompat.R.id.action_mode_bar))))
|
||||||
|
.perform(performClick());
|
||||||
|
onView(withText(R.string.menu_delete))
|
||||||
|
.inRoot(isPlatformPopup())
|
||||||
|
.perform(performClickOnAncestor());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,35 +2,43 @@ package de.dennisguse.opentracks;
|
|||||||
|
|
||||||
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
import static androidx.test.espresso.Espresso.onView;
|
||||||
import static androidx.test.espresso.Espresso.openContextualActionModeOverflowMenu;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
|
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
|
||||||
import static androidx.test.espresso.action.ViewActions.longClick;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.replaceText;
|
import static androidx.test.espresso.action.ViewActions.replaceText;
|
||||||
import static androidx.test.espresso.action.ViewActions.scrollTo;
|
import static androidx.test.espresso.action.ViewActions.scrollTo;
|
||||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||||
|
import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.performClick;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.performClickOnAncestor;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.waitUntilDisplayed;
|
||||||
|
import static de.dennisguse.opentracks.util.TestEnvironmentUtils.resetTrackRecordingServiceAndDeleteTracks;
|
||||||
|
import static de.dennisguse.opentracks.util.TrackUiTestUtils.startRecordingFromTrackList;
|
||||||
|
import static de.dennisguse.opentracks.util.TrackUiTestUtils.stopRecordingToTrackStopped;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
|
||||||
|
|
||||||
import androidx.test.espresso.ViewInteraction;
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.LargeTest;
|
import androidx.test.filters.LargeTest;
|
||||||
import androidx.test.rule.GrantPermissionRule;
|
import androidx.test.rule.GrantPermissionRule;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class EspressoEditTrackRecordingTest {
|
public class EspressoEditTrackRecordingTest {
|
||||||
|
|
||||||
|
private static final Duration UI_TIMEOUT = Duration.ofSeconds(10);
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public final LocaleRule mLocaleRule = new LocaleRule(Locale.ENGLISH);
|
public final LocaleRule mLocaleRule = new LocaleRule(Locale.ENGLISH);
|
||||||
|
|
||||||
@@ -40,46 +48,48 @@ public class EspressoEditTrackRecordingTest {
|
|||||||
@Rule
|
@Rule
|
||||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
resetTrackRecordingServiceAndDeleteTracks();
|
||||||
|
}
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test
|
@Test
|
||||||
public void espressoEditTrackRecordingTest() {
|
public void espressoEditTrackRecordingTest() {
|
||||||
{
|
startRecordingFromTrackList(UI_TIMEOUT);
|
||||||
// TrackListActivity: start recording
|
|
||||||
ViewInteraction fabRecordButton = onView(withId(R.id.track_list_fab_action));
|
|
||||||
fabRecordButton.perform(click());
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
// TrackRecordingActivity
|
// TrackRecordingActivity
|
||||||
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
|
|
||||||
|
|
||||||
// wait; stay recording
|
|
||||||
trackControllerStopButton.perform(waitFor(4000));
|
|
||||||
|
|
||||||
// open menu
|
// open menu
|
||||||
openContextualActionModeOverflowMenu();
|
onView(allOf(
|
||||||
|
withContentDescription(androidx.appcompat.R.string.abc_action_menu_overflow_description),
|
||||||
// Click the item.
|
isDescendantOfA(withId(R.id.bottom_app_bar))))
|
||||||
onView(withText(R.string.menu_edit)).perform(click());
|
.perform(performClick());
|
||||||
|
onView(withText(R.string.menu_edit))
|
||||||
|
.inRoot(isPlatformPopup())
|
||||||
|
.perform(performClickOnAncestor());
|
||||||
|
waitUntilDisplayed(withId(R.id.track_edit_name), UI_TIMEOUT);
|
||||||
|
|
||||||
// change name for "New Name"
|
// change name for "New Name"
|
||||||
ViewInteraction textInputEditText = onView(withId(R.id.track_edit_name));
|
onView(withId(R.id.track_edit_name))
|
||||||
textInputEditText.perform(scrollTo(), replaceText("New Name"));
|
.perform(scrollTo(), replaceText("New Name"));
|
||||||
|
|
||||||
ViewInteraction textInputEditText2 = onView(allOf(withId(R.id.track_edit_name), withText("New Name"), isDisplayed()));
|
onView(allOf(withId(R.id.track_edit_name), withText("New Name"), isDisplayed()))
|
||||||
textInputEditText2.perform(closeSoftKeyboard());
|
.perform(closeSoftKeyboard());
|
||||||
|
|
||||||
// save edition
|
// save edition
|
||||||
ViewInteraction appCompatButton = onView(withId(R.id.track_edit_save));
|
onView(withId(R.id.track_edit_save))
|
||||||
appCompatButton.perform(click());
|
.perform(performClick());
|
||||||
|
waitUntilDisplayed(withId(R.id.track_recording_fab_action), UI_TIMEOUT);
|
||||||
|
|
||||||
// stop;
|
// stop;
|
||||||
trackControllerStopButton.perform(longClick());
|
stopRecordingToTrackStopped(UI_TIMEOUT, R.id.resume_button, R.id.finish_button);
|
||||||
|
|
||||||
// it's on track stopped activity and there are two buttons
|
// it's on track stopped activity and there are two buttons
|
||||||
ViewInteraction resumeButton = onView(withId(R.id.resume_button));
|
onView(withId(R.id.resume_button))
|
||||||
resumeButton.check(matches(isDisplayed()));
|
.check(matches(isDisplayed()));
|
||||||
ViewInteraction finishButton = onView(withId(R.id.finish_button));
|
onView(withId(R.id.finish_button))
|
||||||
finishButton.check(matches(isDisplayed()));
|
.check(matches(isDisplayed()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,100 +1,141 @@
|
|||||||
package de.dennisguse.opentracks;
|
package de.dennisguse.opentracks;
|
||||||
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
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.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.isDisplayed;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
|
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withParentIndex;
|
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.performLongClickOnAncestor;
|
||||||
import static de.dennisguse.opentracks.util.EspressoUtils.selectTabAtIndex;
|
import static de.dennisguse.opentracks.util.EspressoUtils.selectTabAtIndex;
|
||||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
import static de.dennisguse.opentracks.util.EspressoUtils.waitForResumedViewCondition;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.waitUntilDisplayed;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.withSelectedTab;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.withViewPagerCurrentItem;
|
||||||
|
import static de.dennisguse.opentracks.util.TestEnvironmentUtils.resetTrackRecordingServiceAndDeleteTracks;
|
||||||
|
import static de.dennisguse.opentracks.util.TrackUiTestUtils.finishStoppedTrack;
|
||||||
|
import static de.dennisguse.opentracks.util.TrackUiTestUtils.resumeStoppedTrack;
|
||||||
|
import static de.dennisguse.opentracks.util.TrackUiTestUtils.startRecordingFromTrackList;
|
||||||
|
import static de.dennisguse.opentracks.util.TrackUiTestUtils.stopRecordingToTrackStopped;
|
||||||
|
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.espresso.ViewInteraction;
|
import androidx.test.espresso.ViewInteraction;
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.LargeTest;
|
import androidx.test.filters.LargeTest;
|
||||||
import androidx.test.rule.GrantPermissionRule;
|
import androidx.test.rule.GrantPermissionRule;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.RuleChain;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||||
|
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||||
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
|
import de.dennisguse.opentracks.data.models.TrackBuilder;
|
||||||
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class EspressoUITest {
|
public class EspressoUITest {
|
||||||
|
|
||||||
@Rule
|
private static final Duration UI_TIMEOUT = Duration.ofSeconds(10);
|
||||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
|
||||||
|
private final ActivityScenarioRule<TrackListActivity> activityRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||||
|
private final GrantPermissionRule grantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
public RuleChain rules = RuleChain.outerRule(grantPermissionRule)
|
||||||
|
.around(activityRule);
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
resetTrackRecordingServiceAndDeleteTracks();
|
||||||
|
}
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test
|
@Test
|
||||||
public void record_stop_resume_stop_finish() {
|
public void record_stop_resume_stop_finish() {
|
||||||
|
startRecordingFromTrackList(UI_TIMEOUT);
|
||||||
{
|
{
|
||||||
// TrackListActivity: start recording
|
stopRecordingToTrackStopped(UI_TIMEOUT, R.id.resume_button);
|
||||||
ViewInteraction trackControllerRecordButton = onView(withId(R.id.track_list_fab_action));
|
resumeStoppedTrack(UI_TIMEOUT);
|
||||||
trackControllerRecordButton.perform(click());
|
stopRecordingToTrackStopped(UI_TIMEOUT, R.id.finish_button);
|
||||||
}
|
finishStoppedTrack(UI_TIMEOUT);
|
||||||
{
|
|
||||||
// TrackRecordingActivity: wait to record some time and then stop
|
|
||||||
onView(withId(R.id.track_recording_fab_action))
|
|
||||||
.perform(waitFor(5000))
|
|
||||||
.perform(longClick());
|
|
||||||
|
|
||||||
// TrackStoppedActivity: resume
|
onView(withId(R.id.track_list))
|
||||||
onView(allOf(withId(R.id.resume_button), isClickable()))
|
.check(matches(isDisplayed()));
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
// TrackRecordingActivity: wait and then stop
|
|
||||||
onView(withId(R.id.track_recording_fab_action))
|
|
||||||
.perform(waitFor(5000))
|
|
||||||
.perform(longClick());
|
|
||||||
|
|
||||||
// TrackStoppedActivity
|
|
||||||
onView(withId(R.id.finish_button))
|
|
||||||
.perform(click());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test
|
@Test
|
||||||
public void record_move_through_tabs() {
|
public void record_move_through_tabs() {
|
||||||
{
|
startRecordingFromTrackList(UI_TIMEOUT);
|
||||||
// TrackListActivity: start recording
|
waitUntilDisplayed(withId(R.id.track_detail_activity_tablayout), UI_TIMEOUT);
|
||||||
ViewInteraction trackControllerRecordButton = onView(withId(R.id.track_list_fab_action));
|
waitUntilDisplayed(withId(R.id.track_detail_activity_view_pager), UI_TIMEOUT);
|
||||||
trackControllerRecordButton.perform(click());
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
// TrackRecordingActivity
|
// TrackRecordingActivity
|
||||||
ViewInteraction tabLayout = onView(withId(R.id.track_detail_activity_tablayout));
|
ViewInteraction tabLayout = onView(withId(R.id.track_detail_activity_tablayout));
|
||||||
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
|
|
||||||
|
|
||||||
tabLayout.perform(selectTabAtIndex(1));
|
tabLayout.perform(selectTabAtIndex(1));
|
||||||
tabLayout.perform(waitFor(1000));
|
waitUntilTabSelectionSettlesAt(1);
|
||||||
|
|
||||||
tabLayout.perform(selectTabAtIndex(2));
|
tabLayout.perform(selectTabAtIndex(2));
|
||||||
tabLayout.perform(waitFor(1000));
|
waitUntilTabSelectionSettlesAt(2);
|
||||||
|
|
||||||
tabLayout.perform(selectTabAtIndex(3));
|
tabLayout.perform(selectTabAtIndex(3));
|
||||||
tabLayout.perform(waitFor(1000));
|
waitUntilTabSelectionSettlesAt(3);
|
||||||
|
|
||||||
tabLayout.perform(selectTabAtIndex(0));
|
tabLayout.perform(selectTabAtIndex(0));
|
||||||
tabLayout.perform(waitFor(1000));
|
waitUntilTabSelectionSettlesAt(0);
|
||||||
|
|
||||||
// stop
|
// stop
|
||||||
trackControllerStopButton.perform(longClick());
|
stopRecordingToTrackStopped(UI_TIMEOUT, R.id.resume_button, R.id.finish_button);
|
||||||
|
|
||||||
|
onView(withId(R.id.resume_button))
|
||||||
|
.check(matches(isDisplayed()));
|
||||||
|
onView(withId(R.id.finish_button))
|
||||||
|
.check(matches(isDisplayed()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test
|
@Test
|
||||||
public void selectAndDeleteTrack() {
|
public void selectAndDeleteTrack() {
|
||||||
|
Pair<Track, List<TrackPoint>> pair = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 5);
|
||||||
|
String uniqueTrackName = "SelectAndDeleteTrack-" + System.currentTimeMillis();
|
||||||
|
TrackBuilder trackBuilder = new TrackBuilder(pair.first);
|
||||||
|
trackBuilder.setName(uniqueTrackName);
|
||||||
|
TestDataUtil.insertTrackWithLocations(new ContentProviderUtils(ApplicationProvider.getApplicationContext()), trackBuilder.getTrack(), pair.second);
|
||||||
|
activityRule.getScenario().recreate();
|
||||||
|
waitUntilDisplayed(allOf(withId(R.id.track_list_item_name), withText(uniqueTrackName)), UI_TIMEOUT);
|
||||||
|
|
||||||
onView(withId(R.id.track_list)).check(matches(isDisplayed()));
|
onView(withId(R.id.track_list)).check(matches(isDisplayed()));
|
||||||
onView(allOf(withParent(withId(R.id.track_list)), withParentIndex(0))).perform(longClick());
|
onView(allOf(withId(R.id.track_list_item_name), withText(uniqueTrackName)))
|
||||||
|
.perform(performLongClickOnAncestor());
|
||||||
|
|
||||||
|
onView(withId(androidx.appcompat.R.id.action_mode_bar))
|
||||||
|
.check(matches(isDisplayed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitUntilTabSelectionSettlesAt(int index) {
|
||||||
|
waitForResumedViewCondition(
|
||||||
|
withId(R.id.track_detail_activity_tablayout),
|
||||||
|
view -> withSelectedTab(index).matches(view),
|
||||||
|
"selected tab to be " + index,
|
||||||
|
UI_TIMEOUT);
|
||||||
|
waitForResumedViewCondition(
|
||||||
|
withId(R.id.track_detail_activity_view_pager),
|
||||||
|
view -> withViewPagerCurrentItem(index).matches(view),
|
||||||
|
"ViewPager current item to be " + index,
|
||||||
|
UI_TIMEOUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.chart;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Altitude;
|
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
|
||||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class ChartPointTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void create_by_time() {
|
|
||||||
// given
|
|
||||||
TrackStatistics statistics = new TrackStatistics();
|
|
||||||
statistics.setTotalTime(Duration.ofSeconds(1000));
|
|
||||||
|
|
||||||
// when
|
|
||||||
ChartPoint point = ChartPoint.create(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), false, UnitSystem.IMPERIAL_FEET);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(1000000, (long) point.timeOrDistance());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void create_by_distance() {
|
|
||||||
// given
|
|
||||||
TrackStatistics statistics = new TrackStatistics();
|
|
||||||
statistics.setTotalDistance(Distance.of(1000));
|
|
||||||
|
|
||||||
// when
|
|
||||||
ChartPoint point = ChartPoint.create(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), true, UnitSystem.METRIC);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(1, (long) point.timeOrDistance());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void create_get_altitude_speed_and_pace() {
|
|
||||||
// given
|
|
||||||
TrackStatistics statistics = new TrackStatistics();
|
|
||||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
|
|
||||||
.setAltitude(Altitude.EGM2008.of(50));
|
|
||||||
|
|
||||||
// when
|
|
||||||
ChartPoint point = ChartPoint.create(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(50, point.altitude(), 0.01);
|
|
||||||
assertEquals(36, point.speed(), 0.01);
|
|
||||||
assertEquals(1.66, point.pace(), 0.01);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void create_sensorNotAvailable() {
|
|
||||||
// given
|
|
||||||
TrackStatistics statistics = new TrackStatistics();
|
|
||||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
|
|
||||||
.setAltitude(Altitude.EGM2008.of(50));
|
|
||||||
// when
|
|
||||||
ChartPoint point = ChartPoint.create(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertNull(point.heartRate());
|
|
||||||
assertNull(point.cadence());
|
|
||||||
assertNull(point.power());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void create_sensorAvailable() {
|
|
||||||
// given
|
|
||||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
|
|
||||||
.setAltitude(Altitude.EGM2008.of(50))
|
|
||||||
.setHeartRate(100f)
|
|
||||||
.setCadence(101f)
|
|
||||||
.setPower(102f);
|
|
||||||
|
|
||||||
TrackStatistics statistics = new TrackStatistics();
|
|
||||||
|
|
||||||
// when
|
|
||||||
ChartPoint point = ChartPoint.create(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(100.0, point.heartRate(), 0.01);
|
|
||||||
assertEquals(101.0, point.cadence(), 0.01);
|
|
||||||
assertEquals(102.0, point.power(), 0.01);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
||||||
* use this file except in compliance with the License. You may obtain a copy of
|
|
||||||
* the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.dennisguse.opentracks.chart;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Altitude;
|
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
|
||||||
import de.dennisguse.opentracks.data.models.Position;
|
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Commons utilities for creating stubs of track, location.
|
|
||||||
*
|
|
||||||
* @author Youtao Liu
|
|
||||||
*/
|
|
||||||
//TODO Move to ChartPointTest
|
|
||||||
class TrackStubUtils {
|
|
||||||
|
|
||||||
public static final double INITIAL_ALTITUDE = 22;
|
|
||||||
public static final long INITIAL_TIME = 1000L;
|
|
||||||
private static final double INITIAL_LATITUDE = 22;
|
|
||||||
private static final double INITIAL_LONGITUDE = 22;
|
|
||||||
private static final Distance INITIAL_ACCURACY = Distance.of(5);
|
|
||||||
private static final Speed INITIAL_SPEED = Speed.of(10);
|
|
||||||
private static final float INITIAL_BEARING = 3.0f;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a MyTracks location with default values.
|
|
||||||
*
|
|
||||||
* @return a track stub.
|
|
||||||
*/
|
|
||||||
public static TrackPoint createDefaultTrackPoint() {
|
|
||||||
return createDefaultTrackPoint(INITIAL_LATITUDE, INITIAL_LONGITUDE, INITIAL_ALTITUDE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@link TrackPoint} stub with specified values.
|
|
||||||
*
|
|
||||||
* @return a SensorDataSetLocation stub.
|
|
||||||
*/
|
|
||||||
private static TrackPoint createDefaultTrackPoint(double latitude, double longitude, double altitude) {
|
|
||||||
return new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
|
||||||
new Position(
|
|
||||||
Instant.ofEpochMilli(INITIAL_TIME),
|
|
||||||
latitude,
|
|
||||||
longitude,
|
|
||||||
INITIAL_ACCURACY,
|
|
||||||
Altitude.WGS84.of(altitude),
|
|
||||||
INITIAL_ACCURACY,
|
|
||||||
INITIAL_BEARING,
|
|
||||||
INITIAL_SPEED
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,22 +6,28 @@ import android.util.Pair;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.Duration;
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||||
|
import de.dennisguse.opentracks.data.MarkerIterator;
|
||||||
import de.dennisguse.opentracks.data.TrackPointIterator;
|
import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||||
|
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||||
import de.dennisguse.opentracks.data.models.Altitude;
|
import de.dennisguse.opentracks.data.models.Altitude;
|
||||||
|
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||||
|
import de.dennisguse.opentracks.data.models.Cadence;
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
|
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||||
import de.dennisguse.opentracks.data.models.Marker;
|
import de.dennisguse.opentracks.data.models.Marker;
|
||||||
import de.dennisguse.opentracks.data.models.Position;
|
import de.dennisguse.opentracks.data.models.Position;
|
||||||
|
import de.dennisguse.opentracks.data.models.Power;
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
import de.dennisguse.opentracks.data.models.Speed;
|
||||||
|
import de.dennisguse.opentracks.data.models.Statistics;
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
|
||||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
|
||||||
import de.dennisguse.opentracks.ui.markers.MarkerUtils;
|
import de.dennisguse.opentracks.ui.markers.MarkerUtils;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
|
|
||||||
@@ -37,11 +43,15 @@ public class TestDataUtil {
|
|||||||
* Create a track without any trackPoints.
|
* Create a track without any trackPoints.
|
||||||
*/
|
*/
|
||||||
public static Track createTrack(Track.Id trackId) {
|
public static Track createTrack(Track.Id trackId) {
|
||||||
Track track = new Track();
|
return new Track(
|
||||||
track.setId(trackId);
|
trackId,
|
||||||
track.setName("Test: " + trackId.id());
|
UUID.randomUUID(),
|
||||||
|
"Test: " + trackId.id(),
|
||||||
return track;
|
"",
|
||||||
|
"",
|
||||||
|
ActivityType.UNKNOWN,
|
||||||
|
ZoneOffset.UTC,
|
||||||
|
Statistics.DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -85,14 +95,10 @@ public class TestDataUtil {
|
|||||||
createTrackPoint(i, TrackPoint.Type.SEGMENT_END_MANUAL)
|
createTrackPoint(i, TrackPoint.Type.SEGMENT_END_MANUAL)
|
||||||
);
|
);
|
||||||
|
|
||||||
//TODO Use TrackStatisticsUpdater
|
|
||||||
TrackStatistics stats = new TrackStatistics();
|
|
||||||
stats.setTotalDistance(Distance.of(0));
|
|
||||||
stats.setTotalTime(Duration.ofMillis(0));
|
|
||||||
List<Marker> markers = List.of(
|
List<Marker> markers = List.of(
|
||||||
new Marker(trackId, trackPoints.get(1), "Marker 1", "Marker description 1", "Marker category 3", "", null),
|
new Marker(null, trackId, "Marker 1", "Marker description 1", "Marker typeLocalized 3", trackPoints.get(1).position(), null),
|
||||||
new Marker(trackId, trackPoints.get(4), "Marker 2", "Marker description 2", "Marker category 3", "", null),
|
new Marker(null, trackId, "Marker 2", "Marker description 2", "Marker typeLocalized 3", trackPoints.get(4).position(), null),
|
||||||
new Marker(trackId, trackPoints.get(5), "Marker 3", "Marker description 3", "Marker category 3", "", null)
|
new Marker(null, trackId, "Marker 3", "Marker description 3", "Marker typeLocalized 3", trackPoints.get(5).position(), null)
|
||||||
);
|
);
|
||||||
|
|
||||||
return new TrackData(track, trackPoints, markers);
|
return new TrackData(track, trackPoints, markers);
|
||||||
@@ -110,8 +116,10 @@ public class TestDataUtil {
|
|||||||
return pair.first;
|
return pair.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TrackPoint createTrackPoint(int i) {
|
public static TrackPoint createTrackPoint(int i, TrackPoint.Type type, AltitudeGainLoss altitudeGainLoss) {
|
||||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
return new TrackPoint(
|
||||||
|
null,
|
||||||
|
type,
|
||||||
new Position(
|
new Position(
|
||||||
Instant.ofEpochSecond(i),
|
Instant.ofEpochSecond(i),
|
||||||
INITIAL_LATITUDE + (double) i / 10000.0,
|
INITIAL_LATITUDE + (double) i / 10000.0,
|
||||||
@@ -121,22 +129,25 @@ public class TestDataUtil {
|
|||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Speed.of(5f + (i / 10f))
|
Speed.of(5f + (i / 10f))
|
||||||
));
|
),
|
||||||
|
null,
|
||||||
trackPoint.setHeartRate(100f + i % 80);
|
HeartRate.of(100f + i % 80),
|
||||||
trackPoint.setCadence(300f + i);
|
null,
|
||||||
trackPoint.setPower(400f + i);
|
Cadence.of(300f + i),
|
||||||
trackPoint.setAltitudeGain(ALTITUDE_GAIN);
|
Power.of(400f + i),
|
||||||
trackPoint.setAltitudeLoss(ALTITUDE_LOSS);
|
altitudeGainLoss
|
||||||
return trackPoint;
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TrackPoint createTrackPoint(int i, TrackPoint.Type type) {
|
public static TrackPoint createTrackPoint(int i, TrackPoint.Type type) {
|
||||||
TrackPoint trackPoint = createTrackPoint(i);
|
return createTrackPoint(i, TrackPoint.Type.TRACKPOINT, new AltitudeGainLoss(ALTITUDE_GAIN, ALTITUDE_LOSS));
|
||||||
trackPoint.setType(type);
|
|
||||||
return trackPoint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static TrackPoint createTrackPoint(int i) {
|
||||||
|
return createTrackPoint(i, TrackPoint.Type.TRACKPOINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts a track with locations into the database.
|
* Inserts a track with locations into the database.
|
||||||
*
|
*
|
||||||
@@ -145,45 +156,41 @@ public class TestDataUtil {
|
|||||||
*/
|
*/
|
||||||
public static void insertTrackWithLocations(ContentProviderUtils contentProviderUtils, Track track, List<TrackPoint> trackPoints) {
|
public static void insertTrackWithLocations(ContentProviderUtils contentProviderUtils, Track track, List<TrackPoint> trackPoints) {
|
||||||
contentProviderUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
contentProviderUtils.bulkInsertTrackPoint(trackPoints, track.getId());
|
contentProviderUtils.bulkInsertTrackPoint(trackPoints, track.id());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Marker createMarkerWithPhoto(Context context, Track.Id trackId, TrackPoint trackPoint) throws IOException {
|
public static Marker createMarkerWithPhoto(Context context, Track.Id trackId, TrackPoint trackPoint) throws IOException {
|
||||||
|
return createMarkerWithPhoto(context, trackId, trackPoint, "Marker description");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Marker createMarkerWithPhoto(Context context, Track.Id trackId, TrackPoint trackPoint, String description) throws IOException {
|
||||||
File dstFile = new File(MarkerUtils.getImageUrl(context, trackId));
|
File dstFile = new File(MarkerUtils.getImageUrl(context, trackId));
|
||||||
dstFile.createNewFile();
|
dstFile.createNewFile();
|
||||||
Uri photoUri = FileUtils.getUriForFile(context, dstFile);
|
Uri photoUri = FileUtils.getUriForFile(context, dstFile);
|
||||||
|
|
||||||
//TODO Use TrackStatisticsUpdater
|
return new Marker(
|
||||||
TrackStatistics stats = new TrackStatistics();
|
null,
|
||||||
stats.setTotalDistance(Distance.of(0));
|
trackId,
|
||||||
stats.setTotalTime(Duration.ofMillis(0));
|
"Marker name",
|
||||||
|
description,
|
||||||
return new Marker(trackId, trackPoint, "Marker name", "Marker description", "Marker category", "", photoUri);
|
"Marker typeLocalized",
|
||||||
|
trackPoint.position(),
|
||||||
|
photoUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<TrackPoint> getTrackPoints(ContentProviderUtils contentProviderUtils, Track.Id trackId) {
|
public static List<TrackPoint> getTrackPoints(ContentProviderUtils contentProviderUtils, Track.Id trackId) {
|
||||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointIterator(trackId, null)) {
|
||||||
ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
||||||
while (trackPointIterator.hasNext()) {
|
trackPointIterator.forEachRemaining(trackPoints::add);
|
||||||
trackPoints.add(trackPointIterator.next());
|
|
||||||
}
|
|
||||||
return trackPoints;
|
return trackPoints;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Pair<Track.Id, TrackStatistics> buildTrackWithTrackPoints(ContentProviderUtils contentProviderUtils, int numberOfPoints) {
|
public static List<Marker> getMarkers(ContentProviderUtils contentProviderUtils, Track.Id trackId) {
|
||||||
Track dummyTrack = new Track();
|
try (MarkerIterator trackPointIterator = contentProviderUtils.getMarkerIterator(trackId)) {
|
||||||
dummyTrack.setId(new Track.Id(System.currentTimeMillis()));
|
ArrayList<Marker> markers = new ArrayList<>();
|
||||||
dummyTrack.setName("Dummy Track");
|
trackPointIterator.forEachRemaining(markers::add);
|
||||||
contentProviderUtils.insertTrack(dummyTrack);
|
return markers;
|
||||||
TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
|
}
|
||||||
for (int i = 0; i < numberOfPoints; i++) {
|
|
||||||
TrackPoint tp = TestDataUtil.createTrackPoint(i);
|
|
||||||
contentProviderUtils.insertTrackPoint(tp, dummyTrack.getId());
|
|
||||||
trackStatisticsUpdater.addTrackPoint(tp);
|
|
||||||
}
|
|
||||||
dummyTrack.setTrackStatistics(trackStatisticsUpdater.getTrackStatistics());
|
|
||||||
contentProviderUtils.updateTrack(dummyTrack);
|
|
||||||
return new Pair<>(dummyTrack.getId(), trackStatisticsUpdater.getTrackStatistics());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-13
@@ -6,8 +6,12 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Altitude;
|
import de.dennisguse.opentracks.data.models.Altitude;
|
||||||
|
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||||
|
import de.dennisguse.opentracks.data.models.Cadence;
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
|
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||||
import de.dennisguse.opentracks.data.models.Position;
|
import de.dennisguse.opentracks.data.models.Position;
|
||||||
|
import de.dennisguse.opentracks.data.models.Power;
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
import de.dennisguse.opentracks.data.models.Speed;
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
|
|
||||||
@@ -20,7 +24,9 @@ public class TestSensorDataUtil {
|
|||||||
sensorDataList.add(new TestSensorDataUtil.SensorData(time, hr, cadence, power, type));
|
sensorDataList.add(new TestSensorDataUtil.SensorData(time, hr, cadence, power, type));
|
||||||
int i = trackPointList.size() + 1;
|
int i = trackPointList.size() + 1;
|
||||||
|
|
||||||
TrackPoint tp = new TrackPoint(type,
|
TrackPoint tp = new TrackPoint(
|
||||||
|
null,
|
||||||
|
type,
|
||||||
new Position(
|
new Position(
|
||||||
time,
|
time,
|
||||||
TestDataUtil.INITIAL_LATITUDE + (double) i / 10000.0,
|
TestDataUtil.INITIAL_LATITUDE + (double) i / 10000.0,
|
||||||
@@ -30,19 +36,15 @@ public class TestSensorDataUtil {
|
|||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Speed.of(5f + (i / 10f))
|
Speed.of(5f + (i / 10f))
|
||||||
)
|
),
|
||||||
|
null,
|
||||||
|
HeartRate.ofOrNull(hr),
|
||||||
|
null,
|
||||||
|
cadence != null ? Cadence.of(cadence) : null,
|
||||||
|
power != null ? Power.of(power) : null,
|
||||||
|
new AltitudeGainLoss(3, 3)
|
||||||
);
|
);
|
||||||
if (hr != null) {
|
|
||||||
tp.setHeartRate(hr);
|
|
||||||
}
|
|
||||||
if (cadence != null) {
|
|
||||||
tp.setCadence(cadence);
|
|
||||||
}
|
|
||||||
if (power != null) {
|
|
||||||
tp.setPower(power);
|
|
||||||
}
|
|
||||||
tp.setAltitudeGain(3f);
|
|
||||||
tp.setAltitudeLoss(3f);
|
|
||||||
trackPointList.add(tp);
|
trackPointList.add(tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
||||||
* use this file except in compliance with the License. You may obtain a copy of
|
|
||||||
* the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.dennisguse.opentracks.data;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import android.content.ContentUris;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.tables.MarkerColumns;
|
|
||||||
import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
|
|
||||||
import de.dennisguse.opentracks.data.tables.TracksColumns;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link CustomContentProvider}.
|
|
||||||
*
|
|
||||||
* @author Youtao Liu
|
|
||||||
*/
|
|
||||||
public class CustomContentProviderTest {
|
|
||||||
|
|
||||||
private CustomContentProvider customContentProvider;
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
customContentProvider = new CustomContentProvider() {
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link CustomContentProvider#onCreate(android.content.Context)}.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testOnCreate() {
|
|
||||||
assertTrue(customContentProvider.onCreate(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link CustomContentProvider#getType(Uri)}.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testGetType() {
|
|
||||||
assertEquals(TracksColumns.CONTENT_TYPE, customContentProvider.getType(TracksColumns.CONTENT_URI));
|
|
||||||
assertEquals(TracksColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TracksColumns.CONTENT_URI.buildUpon(), 1).build()));
|
|
||||||
|
|
||||||
assertEquals(TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI_BY_ID));
|
|
||||||
assertEquals(TrackPointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TrackPointsColumns.CONTENT_URI_BY_TRACKID.buildUpon(), 1).build()));
|
|
||||||
|
|
||||||
assertEquals(MarkerColumns.CONTENT_TYPE, customContentProvider.getType(MarkerColumns.CONTENT_URI));
|
|
||||||
assertEquals(MarkerColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(MarkerColumns.CONTENT_URI.buildUpon(), 1).build()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+298
-263
@@ -20,50 +20,53 @@ import static org.junit.Assert.assertFalse;
|
|||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentUris;
|
import android.content.ContentUris;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.location.Location;
|
import android.database.MatrixCursor;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||||
import de.dennisguse.opentracks.content.data.TestSensorDataUtil;
|
import de.dennisguse.opentracks.content.data.TestSensorDataUtil;
|
||||||
|
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||||
import de.dennisguse.opentracks.data.models.Altitude;
|
import de.dennisguse.opentracks.data.models.Altitude;
|
||||||
|
import de.dennisguse.opentracks.data.models.AltitudeExtremities;
|
||||||
|
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||||
|
import de.dennisguse.opentracks.data.models.Cadence;
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||||
import de.dennisguse.opentracks.data.models.Marker;
|
import de.dennisguse.opentracks.data.models.Marker;
|
||||||
|
import de.dennisguse.opentracks.data.models.MarkerBuilder;
|
||||||
import de.dennisguse.opentracks.data.models.Position;
|
import de.dennisguse.opentracks.data.models.Position;
|
||||||
|
import de.dennisguse.opentracks.data.models.Power;
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
import de.dennisguse.opentracks.data.models.Speed;
|
||||||
|
import de.dennisguse.opentracks.data.models.Statistics;
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
|
import de.dennisguse.opentracks.data.models.TrackBuilder;
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
|
import de.dennisguse.opentracks.data.statistics.SensorStatistics;
|
||||||
import de.dennisguse.opentracks.data.tables.MarkerColumns;
|
import de.dennisguse.opentracks.data.tables.MarkerColumns;
|
||||||
import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
|
import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
|
||||||
import de.dennisguse.opentracks.data.tables.TracksColumns;
|
import de.dennisguse.opentracks.data.tables.TracksColumns;
|
||||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -72,7 +75,6 @@ import de.dennisguse.opentracks.util.FileUtils;
|
|||||||
* @author Bartlomiej Niechwiej
|
* @author Bartlomiej Niechwiej
|
||||||
* @author Youtao Liu
|
* @author Youtao Liu
|
||||||
*/
|
*/
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
|
||||||
public class CustomContentProviderUtilsTest {
|
public class CustomContentProviderUtilsTest {
|
||||||
|
|
||||||
private static final String NAME_PREFIX = "test name";
|
private static final String NAME_PREFIX = "test name";
|
||||||
@@ -84,12 +86,6 @@ public class CustomContentProviderUtilsTest {
|
|||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
private ContentProviderUtils contentProviderUtils;
|
private ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
@Mock
|
|
||||||
private Cursor cursorMock;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private ContentResolver contentResolverMock;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
contentProviderUtils = new ContentProviderUtils(context);
|
contentProviderUtils = new ContentProviderUtils(context);
|
||||||
@@ -98,41 +94,50 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocationIterator_noPoints() {
|
public void testLocationIterator_noPoints() {
|
||||||
testIterator(new Track.Id(1), 0);
|
assertEquals(0, testIterator(new Track.Id(1), 0).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocationIterator_noAscending() {
|
public void testLocationIterator_noAscending() {
|
||||||
testIterator(new Track.Id(1), 50);
|
assertEquals(50, testIterator(new Track.Id(1), 50).size());
|
||||||
testIterator(new Track.Id(2), 50);
|
assertEquals(50, testIterator(new Track.Id(2), 50).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocationIterator_largeTrack() {
|
public void testLocationIterator_largeTrack() {
|
||||||
testIterator(new Track.Id(1), 20000 / 2);
|
assertEquals(20000 / 2, testIterator(new Track.Id(1), 20000 / 2).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testIterator(Track.Id trackId, int numPoints) {
|
private List<TrackPoint> testIterator(Track.Id trackId, int numPoints) {
|
||||||
TrackPoint.Id lastPointId = initializeTrack(trackId, numPoints);
|
TrackPoint.Id lastPointId = initializeTrack(trackId, numPoints);
|
||||||
List<TrackPoint> locations = new ArrayList<>(numPoints);
|
List<TrackPoint> locations = new ArrayList<>(numPoints);
|
||||||
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
try (TrackPointIterator it = contentProviderUtils.getTrackPointIterator(trackId, null)) {
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
TrackPoint trackPoint = it.next();
|
TrackPoint trackPoint = it.next();
|
||||||
assertNotNull(trackPoint);
|
assertNotNull(trackPoint);
|
||||||
locations.add(trackPoint);
|
locations.add(trackPoint);
|
||||||
// Make sure the IDs are returned in the right order.
|
// Make sure the IDs are returned in the right order.
|
||||||
assertEquals(lastPointId.id() - numPoints + locations.size(), trackPoint.getId().id());
|
assertEquals(lastPointId.id() - numPoints + locations.size(), trackPoint.id().id());
|
||||||
}
|
}
|
||||||
assertEquals(numPoints, locations.size());
|
assertEquals(numPoints, locations.size());
|
||||||
}
|
}
|
||||||
|
return locations;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TrackPoint.Id initializeTrack(Track.Id id, int numPoints) {
|
private TrackPoint.Id initializeTrack(Track.Id id, int numPoints) {
|
||||||
Track track = new Track();
|
Track createdTrack = new Track(
|
||||||
track.setId(id);
|
id,
|
||||||
track.setName("Test: " + id.id());
|
UUID.randomUUID(),
|
||||||
contentProviderUtils.insertTrack(track);
|
"Test: " + id,
|
||||||
track = contentProviderUtils.getTrack(id);
|
"",
|
||||||
|
"",
|
||||||
|
ActivityType.UNKNOWN,
|
||||||
|
ZoneOffset.UTC,
|
||||||
|
Statistics.DEFAULT);
|
||||||
|
|
||||||
|
contentProviderUtils.insertTrack(createdTrack);
|
||||||
|
|
||||||
|
Track track = contentProviderUtils.getTrack(id);
|
||||||
assertNotNull(track);
|
assertNotNull(track);
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = new ArrayList<>(numPoints);
|
List<TrackPoint> trackPoints = new ArrayList<>(numPoints);
|
||||||
@@ -153,10 +158,10 @@ public class CustomContentProviderUtilsTest {
|
|||||||
// Load all inserted trackPoints.
|
// Load all inserted trackPoints.
|
||||||
TrackPoint.Id lastPointId = null;
|
TrackPoint.Id lastPointId = null;
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(id, null)) {
|
try (TrackPointIterator it = contentProviderUtils.getTrackPointIterator(id, null)) {
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
TrackPoint trackPoint = it.next();
|
TrackPoint trackPoint = it.next();
|
||||||
lastPointId = trackPoint.getId();
|
lastPointId = trackPoint.id();
|
||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,40 +178,62 @@ public class CustomContentProviderUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testCreateTrack() {
|
public void testCreateTrack() {
|
||||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||||
|
|
||||||
int columnIndex = 1;
|
|
||||||
// Id
|
|
||||||
when(cursorMock.getColumnIndexOrThrow(TracksColumns._ID)).thenReturn(columnIndex);
|
|
||||||
when(cursorMock.isNull(columnIndex)).thenReturn(false);
|
|
||||||
when(cursorMock.getLong(columnIndex)).thenReturn(trackId.id());
|
|
||||||
|
|
||||||
//Uuid
|
|
||||||
columnIndex++;
|
|
||||||
when(cursorMock.getColumnIndexOrThrow(TracksColumns.UUID)).thenReturn(columnIndex);
|
|
||||||
when(cursorMock.isNull(columnIndex)).thenReturn(false);
|
|
||||||
when(cursorMock.getBlob(columnIndex)).thenReturn(UUIDUtils.toBytes(UUID.randomUUID()));
|
|
||||||
|
|
||||||
// Name
|
|
||||||
columnIndex++;
|
|
||||||
String name = NAME_PREFIX + trackId.id();
|
String name = NAME_PREFIX + trackId.id();
|
||||||
when(cursorMock.getColumnIndexOrThrow(TracksColumns.NAME)).thenReturn(columnIndex);
|
|
||||||
when(cursorMock.isNull(columnIndex)).thenReturn(false);
|
|
||||||
when(cursorMock.getString(columnIndex)).thenReturn(name);
|
|
||||||
|
|
||||||
Track track = ContentProviderUtils.createTrack(cursorMock);
|
MatrixCursor cursor = new MatrixCursor(new String[]{
|
||||||
assertEquals(trackId, track.getId());
|
TracksColumns._ID,
|
||||||
assertEquals(name, track.getName());
|
TracksColumns.UUID,
|
||||||
|
TracksColumns.NAME,
|
||||||
|
TracksColumns.DESCRIPTION,
|
||||||
|
TracksColumns.ACTIVITY_TYPE,
|
||||||
|
TracksColumns.ACTIVITY_TYPE_LOCALIZED,
|
||||||
|
TracksColumns.STARTTIME,
|
||||||
|
TracksColumns.STARTTIME_OFFSET,
|
||||||
|
TracksColumns.STOPTIME,
|
||||||
|
TracksColumns.TOTALDISTANCE,
|
||||||
|
TracksColumns.TOTALTIME,
|
||||||
|
TracksColumns.MOVINGTIME,
|
||||||
|
TracksColumns.MAXSPEED,
|
||||||
|
TracksColumns.MIN_ALTITUDE,
|
||||||
|
TracksColumns.MAX_ALTITUDE,
|
||||||
|
TracksColumns.ALTITUDE_GAIN,
|
||||||
|
TracksColumns.ALTITUDE_LOSS,
|
||||||
|
});
|
||||||
|
cursor.addRow(new Object[]{
|
||||||
|
trackId.id(),
|
||||||
|
UUIDUtils.toBytes(UUID.randomUUID()),
|
||||||
|
name,
|
||||||
|
"",
|
||||||
|
ActivityType.UNKNOWN.getId(),
|
||||||
|
"",
|
||||||
|
1_000L,
|
||||||
|
ZoneOffset.UTC.getTotalSeconds(),
|
||||||
|
2_000L,
|
||||||
|
123.45f,
|
||||||
|
1_000L,
|
||||||
|
500L,
|
||||||
|
12.34f,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
});
|
||||||
|
assertTrue(cursor.moveToFirst());
|
||||||
|
|
||||||
|
Track track = ContentProviderUtils.createTrack(cursor);
|
||||||
|
assertEquals(trackId, track.id());
|
||||||
|
assertEquals(name, track.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertCount(int trackCount, int trackPointCount, int markerCount) {
|
private void assertCount(int trackCount, int trackPointCount, int markerCount) {
|
||||||
ContentResolver contentResolver = context.getContentResolver();
|
ContentResolver contentResolver = context.getContentResolver();
|
||||||
try (Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID)) {
|
try (Cursor tracksCursor = contentResolver.query(CustomContentProvider.UrlType.TRACKS.getUri(), null, null, null, TracksColumns._ID)) {
|
||||||
assertEquals(trackCount, tracksCursor.getCount());
|
assertEquals(trackCount, tracksCursor.getCount());
|
||||||
}
|
}
|
||||||
try (Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID)) {
|
try (Cursor tracksPointsCursor = contentResolver.query(CustomContentProvider.UrlType.TRACKPOINTS_BY_ID.getUri(), null, null, null, TrackPointsColumns._ID)) {
|
||||||
assertEquals(trackPointCount, tracksPointsCursor.getCount());
|
assertEquals(trackPointCount, tracksPointsCursor.getCount());
|
||||||
}
|
}
|
||||||
try (Cursor markerCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID)) {
|
try (Cursor markerCursor = contentResolver.query(CustomContentProvider.UrlType.MARKERS.getUri(), null, null, null, MarkerColumns._ID)) {
|
||||||
assertEquals(markerCount, markerCursor.getCount());
|
assertEquals(markerCount, markerCursor.getCount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,8 +247,15 @@ public class CustomContentProviderUtilsTest {
|
|||||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||||
|
|
||||||
Marker marker = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
contentProviderUtils.insertMarker(new Marker(
|
||||||
contentProviderUtils.insertMarker(marker);
|
null,
|
||||||
|
trackId,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
getLastValidTrackPoint(trackId).position(),
|
||||||
|
null
|
||||||
|
));
|
||||||
|
|
||||||
assertCount(1, 10, 1);
|
assertCount(1, 10, 1);
|
||||||
|
|
||||||
@@ -239,14 +273,14 @@ public class CustomContentProviderUtilsTest {
|
|||||||
public void testDeleteAllTracks_withMarkerAndPhoto() throws IOException {
|
public void testDeleteAllTracks_withMarkerAndPhoto() throws IOException {
|
||||||
// Insert track, points and marker with photo at first.
|
// Insert track, points and marker with photo at first.
|
||||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||||
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||||
|
|
||||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
TrackPoint trackPoint = getLastValidTrackPoint(trackId);
|
||||||
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
||||||
contentProviderUtils.insertMarker(marker);
|
contentProviderUtils.insertMarker(marker);
|
||||||
|
|
||||||
assertCount(1, 10, 1);
|
assertCount(1, 10, 1);
|
||||||
assertTrue(marker.hasPhoto());
|
assertNotNull(marker.photoUrl());
|
||||||
File dir = FileUtils.getPhotoDir(context, trackId);
|
File dir = FileUtils.getPhotoDir(context, trackId);
|
||||||
assertTrue(dir.isDirectory());
|
assertTrue(dir.isDirectory());
|
||||||
assertEquals(1, dir.list().length);
|
assertEquals(1, dir.list().length);
|
||||||
@@ -276,8 +310,15 @@ public class CustomContentProviderUtilsTest {
|
|||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId2, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId2, 10);
|
||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10);
|
||||||
|
|
||||||
Marker marker = new Marker(trackId1, contentProviderUtils.getLastValidTrackPoint(trackId2));
|
contentProviderUtils.insertMarker(new Marker(
|
||||||
contentProviderUtils.insertMarker(marker);
|
null,
|
||||||
|
trackId1,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
getLastValidTrackPoint(trackId1).position(),
|
||||||
|
null
|
||||||
|
));
|
||||||
|
|
||||||
assertCount(3, 30, 1);
|
assertCount(3, 30, 1);
|
||||||
|
|
||||||
@@ -304,19 +345,19 @@ public class CustomContentProviderUtilsTest {
|
|||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10);
|
||||||
|
|
||||||
// Insert a marker in tracks trackId and trackId + 1.
|
// Insert a marker in tracks trackId and trackId + 1.
|
||||||
TrackPoint trackPoint1 = contentProviderUtils.getLastValidTrackPoint(trackId1);
|
TrackPoint trackPoint1 = getLastValidTrackPoint(trackId1);
|
||||||
Marker marker1 = TestDataUtil.createMarkerWithPhoto(context, trackId1, trackPoint1);
|
Marker marker1 = TestDataUtil.createMarkerWithPhoto(context, trackId1, trackPoint1);
|
||||||
contentProviderUtils.insertMarker(marker1);
|
contentProviderUtils.insertMarker(marker1);
|
||||||
File dir1 = FileUtils.getPhotoDir(context, trackId1);
|
File dir1 = FileUtils.getPhotoDir(context, trackId1);
|
||||||
|
|
||||||
TrackPoint trackPoint2 = contentProviderUtils.getLastValidTrackPoint(trackId2);
|
TrackPoint trackPoint2 = getLastValidTrackPoint(trackId2);
|
||||||
Marker marker2 = TestDataUtil.createMarkerWithPhoto(context, trackId2, trackPoint2);
|
Marker marker2 = TestDataUtil.createMarkerWithPhoto(context, trackId2, trackPoint2);
|
||||||
contentProviderUtils.insertMarker(marker2);
|
contentProviderUtils.insertMarker(marker2);
|
||||||
File dir2 = FileUtils.getPhotoDir(context, trackId2);
|
File dir2 = FileUtils.getPhotoDir(context, trackId2);
|
||||||
|
|
||||||
// Check.
|
// Check.
|
||||||
assertCount(3, 30, 2);
|
assertCount(3, 30, 2);
|
||||||
assertTrue(marker1.hasPhoto());
|
assertNotNull(marker1.photoUrl());
|
||||||
assertTrue(dir1.isDirectory());
|
assertTrue(dir1.isDirectory());
|
||||||
assertEquals(1, dir1.list().length);
|
assertEquals(1, dir1.list().length);
|
||||||
assertTrue(dir1.exists());
|
assertTrue(dir1.exists());
|
||||||
@@ -348,7 +389,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(initialTrackNumber + 1, allTracks.size());
|
assertEquals(initialTrackNumber + 1, allTracks.size());
|
||||||
assertEquals(trackId, allTracks.get(allTracks.size() - 1).getId());
|
assertEquals(trackId, allTracks.get(allTracks.size() - 1).id());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -375,61 +416,55 @@ public class CustomContentProviderUtilsTest {
|
|||||||
contentProviderUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
|
|
||||||
// when / then
|
// when / then
|
||||||
assertNotNull(contentProviderUtils.getTrack(track.getUuid()));
|
assertNotNull(contentProviderUtils.getTrack(track.uuid()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the method {@link ContentProviderUtils#updateTrack(Track)}
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateTrack() {
|
public void testUpdateTrack() {
|
||||||
// given
|
// given
|
||||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||||
Track track = TestDataUtil.createTrack(trackId);
|
TrackBuilder track = new TrackBuilder(TestDataUtil.createTrack(trackId));
|
||||||
String nameOld = "name1";
|
String nameOld = "name1";
|
||||||
String nameNew = "name2";
|
String nameNew = "name2";
|
||||||
track.setName(nameOld);
|
track.setName(nameOld);
|
||||||
|
|
||||||
// when / then
|
// when / then
|
||||||
contentProviderUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track.getTrack());
|
||||||
assertEquals(nameOld, contentProviderUtils.getTrack(trackId).getName());
|
assertEquals(nameOld, contentProviderUtils.getTrack(trackId).name());
|
||||||
track.setName(nameNew);
|
track.setName(nameNew);
|
||||||
contentProviderUtils.updateTrack(track);
|
contentProviderUtils.updateTrack(track.getTrack());
|
||||||
assertEquals(nameNew, contentProviderUtils.getTrack(trackId).getName());
|
assertEquals(nameNew, contentProviderUtils.getTrack(trackId).name());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the method {@link ContentProviderUtils#createContentValues(Marker)}.
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateContentValues_marker() {
|
public void testCreateContentValues_marker() {
|
||||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||||
Pair<Track, List<TrackPoint>> track = TestDataUtil.createTrack(trackId, 10);
|
Pair<Track, List<TrackPoint>> track = TestDataUtil.createTrack(trackId, 10);
|
||||||
|
|
||||||
|
TrackBuilder trackBuilder = new TrackBuilder(track.first);
|
||||||
|
|
||||||
// AverageSpeed
|
// AverageSpeed
|
||||||
TrackStatistics statistics = new TrackStatistics();
|
trackBuilder.setStatistics(new Statistics(
|
||||||
statistics.setStartTime(Instant.ofEpochMilli(1000));
|
Instant.ofEpochMilli(1000),
|
||||||
statistics.setStopTime(Instant.ofEpochMilli(2500));
|
Instant.ofEpochMilli(2500),
|
||||||
statistics.setTotalTime(Duration.ofMillis(1500));
|
Duration.ofMillis(1500),
|
||||||
statistics.setMovingTime(Duration.ofMillis(700));
|
Duration.ofMillis(700),
|
||||||
statistics.setTotalDistance(Distance.of(750.0));
|
Distance.of(750),
|
||||||
statistics.setTotalAltitudeGain(50.0f);
|
Speed.of(60),
|
||||||
statistics.setMaxSpeed(Speed.of(60.0));
|
new AltitudeExtremities(1250, 1200),
|
||||||
statistics.setMaxAltitude(1250.0);
|
new AltitudeGainLoss(50, 50),
|
||||||
statistics.setMinAltitude(1200.0);
|
null,
|
||||||
|
null
|
||||||
|
));
|
||||||
|
contentProviderUtils.insertTrack(trackBuilder.getTrack());
|
||||||
|
|
||||||
track.first.setTrackStatistics(statistics);
|
MarkerBuilder marker = new MarkerBuilder(trackId, track.second.get(0));
|
||||||
contentProviderUtils.insertTrack(track.first);
|
|
||||||
|
|
||||||
Marker marker = new Marker(trackId, track.second.get(0));
|
|
||||||
marker.setDescription(TEST_DESC);
|
marker.setDescription(TEST_DESC);
|
||||||
contentProviderUtils.insertMarker(marker);
|
contentProviderUtils.insertMarker(marker.getMarker());
|
||||||
|
|
||||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(contentResolverMock);
|
|
||||||
|
|
||||||
Marker.Id markerId = new Marker.Id(System.currentTimeMillis());
|
Marker.Id markerId = new Marker.Id(System.currentTimeMillis());
|
||||||
marker.setId(markerId);
|
marker.setId(markerId);
|
||||||
ContentValues contentValues = contentProviderUtils.createContentValues(marker);
|
ContentValues contentValues = contentProviderUtils.createContentValues(marker.getMarker(), trackId);
|
||||||
assertEquals(markerId.id(), contentValues.get(MarkerColumns._ID));
|
assertEquals(markerId.id(), contentValues.get(MarkerColumns._ID));
|
||||||
assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(MarkerColumns.LONGITUDE));
|
assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(MarkerColumns.LONGITUDE));
|
||||||
assertEquals(TEST_DESC, contentValues.get(MarkerColumns.DESCRIPTION));
|
assertEquals(TEST_DESC, contentValues.get(MarkerColumns.DESCRIPTION));
|
||||||
@@ -446,15 +481,18 @@ public class CustomContentProviderUtilsTest {
|
|||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
Marker marker1 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
Marker.Id marker1Id = contentProviderUtils.insertMarker(new Marker(
|
||||||
marker1.setDescription(TEST_DESC);
|
null,
|
||||||
contentProviderUtils.insertMarker(marker1);
|
trackId,
|
||||||
|
"",
|
||||||
|
TEST_DESC,
|
||||||
|
"",
|
||||||
|
getLastValidTrackPoint(trackId).position(),
|
||||||
|
null
|
||||||
|
));
|
||||||
|
|
||||||
// Check insert was done.
|
// Check insert was done.
|
||||||
assertEquals(contentProviderUtils.getMarkers(trackId).size(), 1);
|
assertEquals(1, TestDataUtil.getMarkers(contentProviderUtils, trackId).size());
|
||||||
|
|
||||||
// Get marker id that needs to delete.
|
|
||||||
Marker.Id marker1Id = contentProviderUtils.insertMarker(marker1);
|
|
||||||
|
|
||||||
// Delete
|
// Delete
|
||||||
contentProviderUtils.deleteMarker(context, marker1Id);
|
contentProviderUtils.deleteMarker(context, marker1Id);
|
||||||
@@ -473,18 +511,18 @@ public class CustomContentProviderUtilsTest {
|
|||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
TrackPoint trackPoint = getLastValidTrackPoint(trackId);
|
||||||
Marker marker1 = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
Marker marker1 = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
||||||
contentProviderUtils.insertMarker(marker1);
|
contentProviderUtils.insertMarker(marker1);
|
||||||
|
|
||||||
// Check insert was done.
|
// Check insert was done.
|
||||||
assertEquals(contentProviderUtils.getMarkers(trackId).size(), 1);
|
assertEquals(1, TestDataUtil.getMarkers(contentProviderUtils, trackId).size());
|
||||||
|
|
||||||
// Get marker id that needs to delete.
|
// Get marker id that needs to delete.
|
||||||
Marker.Id marker1Id = contentProviderUtils.insertMarker(marker1);
|
Marker.Id marker1Id = contentProviderUtils.insertMarker(marker1);
|
||||||
|
|
||||||
// Check marker has photo and it's in the external storage.
|
// Check marker has photo and it's in the external storage.
|
||||||
assertTrue(marker1.hasPhoto());
|
assertNotNull(marker1.photoUrl());
|
||||||
File dir = FileUtils.getPhotoDir(context, trackId);
|
File dir = FileUtils.getPhotoDir(context, trackId);
|
||||||
assertTrue(dir.isDirectory());
|
assertTrue(dir.isDirectory());
|
||||||
assertEquals(1, dir.list().length);
|
assertEquals(1, dir.list().length);
|
||||||
@@ -523,20 +561,31 @@ public class CustomContentProviderUtilsTest {
|
|||||||
// TestDataUtil.insertTrackWithLocations(contentProviderUtils, track);
|
// TestDataUtil.insertTrackWithLocations(contentProviderUtils, track);
|
||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
Marker marker1 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
Marker.Id marker1Id = contentProviderUtils.insertMarker(new Marker(
|
||||||
marker1.setDescription(MOCK_DESC);
|
null,
|
||||||
Marker.Id marker1Id = contentProviderUtils.insertMarker(marker1);
|
trackId,
|
||||||
|
"",
|
||||||
Marker marker2 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
MOCK_DESC,
|
||||||
marker2.setDescription(MOCK_DESC);
|
"",
|
||||||
Marker.Id marker2Id = contentProviderUtils.insertMarker(marker2);
|
getLastValidTrackPoint(trackId).position(),
|
||||||
|
null
|
||||||
|
));
|
||||||
|
Marker.Id marker2Id = contentProviderUtils.insertMarker(new Marker(
|
||||||
|
null,
|
||||||
|
trackId,
|
||||||
|
"",
|
||||||
|
MOCK_DESC,
|
||||||
|
"",
|
||||||
|
getLastValidTrackPoint(trackId).position(),
|
||||||
|
null
|
||||||
|
));
|
||||||
|
|
||||||
// Delete
|
// Delete
|
||||||
assertNotNull(contentProviderUtils.getMarker(marker1Id));
|
assertNotNull(contentProviderUtils.getMarker(marker1Id));
|
||||||
contentProviderUtils.deleteMarker(context, marker1Id);
|
contentProviderUtils.deleteMarker(context, marker1Id);
|
||||||
assertNull(contentProviderUtils.getMarker(marker1Id));
|
assertNull(contentProviderUtils.getMarker(marker1Id));
|
||||||
|
|
||||||
assertEquals(MOCK_DESC, contentProviderUtils.getMarker(marker2Id).getDescription());
|
assertEquals(MOCK_DESC, contentProviderUtils.getMarker(marker2Id).description());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -547,14 +596,17 @@ public class CustomContentProviderUtilsTest {
|
|||||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||||
|
|
||||||
Marker marker1 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
for (int i = 0; i < 4; i++) {
|
||||||
Marker marker2 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
contentProviderUtils.insertMarker(new Marker(
|
||||||
Marker marker3 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
null,
|
||||||
Marker marker4 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
trackId,
|
||||||
contentProviderUtils.insertMarker(marker1);
|
"",
|
||||||
contentProviderUtils.insertMarker(marker2);
|
MOCK_DESC,
|
||||||
contentProviderUtils.insertMarker(marker3);
|
"",
|
||||||
contentProviderUtils.insertMarker(marker4);
|
getLastValidTrackPoint(trackId).position(),
|
||||||
|
null
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
assertEquals(Integer.valueOf(4), contentProviderUtils.getNextMarkerNumber(trackId));
|
assertEquals(Integer.valueOf(4), contentProviderUtils.getNextMarkerNumber(trackId));
|
||||||
}
|
}
|
||||||
@@ -568,11 +620,18 @@ public class CustomContentProviderUtilsTest {
|
|||||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||||
|
|
||||||
Marker marker = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
Marker.Id markerId = contentProviderUtils.insertMarker(
|
||||||
marker.setDescription(TEST_DESC);
|
new Marker(
|
||||||
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
|
null,
|
||||||
|
trackId,
|
||||||
|
"",
|
||||||
|
TEST_DESC,
|
||||||
|
"",
|
||||||
|
getLastValidTrackPoint(trackId).position(),
|
||||||
|
null
|
||||||
|
));
|
||||||
|
|
||||||
assertEquals(TEST_DESC, contentProviderUtils.getMarker(markerId).getDescription());
|
assertEquals(TEST_DESC, contentProviderUtils.getMarker(markerId).description());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -584,16 +643,29 @@ public class CustomContentProviderUtilsTest {
|
|||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
Marker marker = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
Marker.Id markerId = contentProviderUtils.insertMarker(
|
||||||
marker.setDescription(TEST_DESC);
|
new Marker(
|
||||||
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
|
null,
|
||||||
|
trackId,
|
||||||
|
"",
|
||||||
|
TEST_DESC,
|
||||||
|
"",
|
||||||
|
getLastValidTrackPoint(trackId).position(),
|
||||||
|
null
|
||||||
|
));
|
||||||
|
|
||||||
// Update
|
// Update
|
||||||
marker = contentProviderUtils.getMarker(markerId);
|
contentProviderUtils.updateMarker(context, new Marker(
|
||||||
marker.setDescription(TEST_DESC_NEW);
|
markerId,
|
||||||
contentProviderUtils.updateMarker(context, marker);
|
trackId,
|
||||||
|
"",
|
||||||
|
TEST_DESC_NEW,
|
||||||
|
"",
|
||||||
|
getLastValidTrackPoint(trackId).position(),
|
||||||
|
null
|
||||||
|
));
|
||||||
|
|
||||||
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).getDescription());
|
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).description());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -607,10 +679,8 @@ public class CustomContentProviderUtilsTest {
|
|||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
TrackPoint trackPoint = getLastValidTrackPoint(trackId);
|
||||||
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
Marker.Id markerId = contentProviderUtils.insertMarker(TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint, TEST_DESC));
|
||||||
marker.setDescription(TEST_DESC);
|
|
||||||
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
|
|
||||||
|
|
||||||
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.id());
|
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.id());
|
||||||
assertTrue(dir.exists());
|
assertTrue(dir.exists());
|
||||||
@@ -618,14 +688,13 @@ public class CustomContentProviderUtilsTest {
|
|||||||
assertEquals(1, dir.list().length);
|
assertEquals(1, dir.list().length);
|
||||||
|
|
||||||
// Update
|
// Update
|
||||||
marker = contentProviderUtils.getMarker(markerId);
|
MarkerBuilder marker = new MarkerBuilder(contentProviderUtils.getMarker(markerId));
|
||||||
marker.setName(TEST_NAME_NEW);
|
marker.setName(TEST_NAME_NEW);
|
||||||
marker.setDescription(TEST_DESC_NEW);
|
marker.setDescription(TEST_DESC_NEW);
|
||||||
contentProviderUtils.updateMarker(context, marker);
|
contentProviderUtils.updateMarker(context, marker.getMarker());
|
||||||
|
|
||||||
assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(markerId).getName());
|
assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(markerId).name());
|
||||||
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).getDescription());
|
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).description());
|
||||||
assertTrue(marker.hasPhoto());
|
|
||||||
assertTrue(dir.exists());
|
assertTrue(dir.exists());
|
||||||
assertTrue(dir.isDirectory());
|
assertTrue(dir.isDirectory());
|
||||||
assertEquals(1, dir.list().length);
|
assertEquals(1, dir.list().length);
|
||||||
@@ -642,9 +711,8 @@ public class CustomContentProviderUtilsTest {
|
|||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
TrackPoint trackPoint = getLastValidTrackPoint(trackId);
|
||||||
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint, TEST_DESC);
|
||||||
marker.setDescription(TEST_DESC);
|
|
||||||
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
|
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
|
||||||
|
|
||||||
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.id());
|
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.id());
|
||||||
@@ -653,15 +721,14 @@ public class CustomContentProviderUtilsTest {
|
|||||||
assertEquals(1, dir.list().length);
|
assertEquals(1, dir.list().length);
|
||||||
|
|
||||||
// Update
|
// Update
|
||||||
marker = contentProviderUtils.getMarker(markerId);
|
MarkerBuilder markerBuilder = new MarkerBuilder(contentProviderUtils.getMarker(markerId));
|
||||||
marker.setName(TEST_NAME_NEW);
|
markerBuilder.setName(TEST_NAME_NEW);
|
||||||
marker.setDescription(TEST_DESC_NEW);
|
markerBuilder.setDescription(TEST_DESC_NEW);
|
||||||
marker.setPhotoUrl(null);
|
markerBuilder.setPhotoUrl(null);
|
||||||
contentProviderUtils.updateMarker(context, marker);
|
contentProviderUtils.updateMarker(context, markerBuilder.getMarker());
|
||||||
|
|
||||||
assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(markerId).getName());
|
assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(markerId).name());
|
||||||
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).getDescription());
|
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).description());
|
||||||
assertFalse(marker.hasPhoto());
|
|
||||||
assertFalse(dir.exists());
|
assertFalse(dir.exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -676,11 +743,9 @@ public class CustomContentProviderUtilsTest {
|
|||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||||
|
|
||||||
// Insert two markers with photos.
|
// Insert two markers with photos.
|
||||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
TrackPoint trackPoint = getLastValidTrackPoint(trackId);
|
||||||
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint, TEST_DESC);
|
||||||
marker.setDescription(TEST_DESC);
|
Marker otherMarker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint, TEST_DESC);
|
||||||
Marker otherMarker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
|
||||||
otherMarker.setDescription(TEST_DESC);
|
|
||||||
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
|
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
|
||||||
contentProviderUtils.insertMarker(otherMarker);
|
contentProviderUtils.insertMarker(otherMarker);
|
||||||
|
|
||||||
@@ -690,12 +755,14 @@ public class CustomContentProviderUtilsTest {
|
|||||||
assertEquals(2, dir.list().length);
|
assertEquals(2, dir.list().length);
|
||||||
|
|
||||||
// Update one marker deleting photo.
|
// Update one marker deleting photo.
|
||||||
marker = contentProviderUtils.getMarker(markerId);
|
MarkerBuilder markerBuilder = new MarkerBuilder(contentProviderUtils.getMarker(markerId));
|
||||||
marker.setPhotoUrl(null);
|
markerBuilder.setPhotoUrl(null);
|
||||||
contentProviderUtils.updateMarker(context, marker);
|
contentProviderUtils.updateMarker(context, markerBuilder.getMarker());
|
||||||
|
|
||||||
assertEquals(TEST_DESC, contentProviderUtils.getMarker(markerId).getDescription());
|
// then
|
||||||
assertFalse(marker.hasPhoto());
|
marker = contentProviderUtils.getMarker(markerId);
|
||||||
|
assertEquals(TEST_DESC, contentProviderUtils.getMarker(markerId).description());
|
||||||
|
assertNull(marker.photoUrl());
|
||||||
assertTrue(dir.exists());
|
assertTrue(dir.exists());
|
||||||
assertTrue(dir.isDirectory());
|
assertTrue(dir.isDirectory());
|
||||||
assertEquals(1, dir.list().length);
|
assertEquals(1, dir.list().length);
|
||||||
@@ -726,50 +793,6 @@ public class CustomContentProviderUtilsTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the method {@link ContentProviderUtils#createTrackPoint(Cursor)}.
|
|
||||||
*/
|
|
||||||
//TODO incomplete
|
|
||||||
@Test
|
|
||||||
public void testCreateTrackPoint() {
|
|
||||||
// given
|
|
||||||
when(cursorMock.getColumnIndex(TrackPointsColumns._ID)).thenReturn(1);
|
|
||||||
|
|
||||||
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE)).thenReturn(2);
|
|
||||||
when(cursorMock.isNull(2)).thenReturn(false);
|
|
||||||
int longitude = 11;
|
|
||||||
when(cursorMock.getInt(2)).thenReturn(longitude * 1000000);
|
|
||||||
|
|
||||||
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.LATITUDE)).thenReturn(3);
|
|
||||||
when(cursorMock.isNull(3)).thenReturn(false);
|
|
||||||
int latitude = 22;
|
|
||||||
when(cursorMock.getInt(3)).thenReturn(latitude * 1000000);
|
|
||||||
|
|
||||||
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.TIME)).thenReturn(4);
|
|
||||||
when(cursorMock.isNull(4)).thenReturn(false);
|
|
||||||
long time = System.currentTimeMillis();
|
|
||||||
when(cursorMock.getLong(4)).thenReturn(time);
|
|
||||||
|
|
||||||
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.SPEED)).thenReturn(5);
|
|
||||||
when(cursorMock.isNull(5)).thenReturn(false);
|
|
||||||
float speed = 2.2f;
|
|
||||||
when(cursorMock.getFloat(5)).thenReturn(speed);
|
|
||||||
|
|
||||||
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_HEARTRATE)).thenReturn(6);
|
|
||||||
when(cursorMock.isNull(6)).thenReturn(false);
|
|
||||||
when(cursorMock.getFloat(6)).thenReturn(75f);
|
|
||||||
|
|
||||||
// when
|
|
||||||
TrackPoint trackPoint = contentProviderUtils.createTrackPoint(cursorMock);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(latitude, trackPoint.getPosition().latitude(), 0.01);
|
|
||||||
assertEquals(longitude, trackPoint.getPosition().longitude(), 0.01);
|
|
||||||
assertEquals(time, trackPoint.getTime().toEpochMilli());
|
|
||||||
assertEquals(speed, trackPoint.getSpeed().toMPS(), 0.01);
|
|
||||||
assertEquals(HeartRate.of(75f), trackPoint.getHeartRate());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method
|
* Tests the method
|
||||||
* {@link ContentProviderUtils#insertTrackPoint(TrackPoint, Track.Id)}.
|
* {@link ContentProviderUtils#insertTrackPoint(TrackPoint, Track.Id)}.
|
||||||
@@ -778,7 +801,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
public void testInsertTrackPoint() {
|
public void testInsertTrackPoint() {
|
||||||
// Insert track, point at first.
|
// Insert track, point at first.
|
||||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||||
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||||
|
|
||||||
contentProviderUtils.insertTrackPoint(TestDataUtil.createTrackPoint(22), trackId);
|
contentProviderUtils.insertTrackPoint(TestDataUtil.createTrackPoint(22), trackId);
|
||||||
// when
|
// when
|
||||||
@@ -792,35 +815,29 @@ public class CustomContentProviderUtilsTest {
|
|||||||
public void testInsertAndLoadTrackPoint() {
|
public void testInsertAndLoadTrackPoint() {
|
||||||
// given
|
// given
|
||||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||||
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||||
|
|
||||||
TrackPoint trackPoint = TestDataUtil.createTrackPoint(5);
|
TrackPoint trackPoint = new TrackPoint(
|
||||||
trackPoint.setHeartRate(1F);
|
null,
|
||||||
trackPoint.setCadence(2F);
|
TrackPoint.Type.TRACKPOINT,
|
||||||
trackPoint.setPower(3F);
|
TestDataUtil.createTrackPoint(5).position(),
|
||||||
|
null,
|
||||||
|
HeartRate.of(1),
|
||||||
|
null,
|
||||||
|
Cadence.of(2),
|
||||||
|
Power.of(3),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
contentProviderUtils.insertTrackPoint(trackPoint, trackId);
|
contentProviderUtils.insertTrackPoint(trackPoint, trackId);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
assertTrue(trackPoints.get(10).hasHeartRate());
|
assertNotNull(trackPoints.get(10).heartRate());
|
||||||
assertEquals(trackPoint.getHeartRate(), trackPoints.get(10).getHeartRate());
|
assertEquals(trackPoint.heartRate(), trackPoints.get(10).heartRate());
|
||||||
assertEquals(trackPoint.getCadence(), trackPoints.get(10).getCadence());
|
assertEquals(trackPoint.cadence(), trackPoints.get(10).cadence());
|
||||||
assertEquals(trackPoint.getPower(), trackPoints.get(10).getPower());
|
assertEquals(trackPoint.power(), trackPoints.get(10).power());
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the method {@link ContentProviderUtils#getLastValidTrackPoint(Track.Id)}.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testGetLastValidTrackPoint() {
|
|
||||||
// Insert track, points at first.
|
|
||||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
|
||||||
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
|
||||||
|
|
||||||
TrackPoint lastTrackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
|
||||||
checkLocation(9, lastTrackPoint.getLocation());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -831,8 +848,8 @@ public class CustomContentProviderUtilsTest {
|
|||||||
contentProviderUtils.insertTrack(track.first);
|
contentProviderUtils.insertTrack(track.first);
|
||||||
|
|
||||||
List<TrackPoint.Id> trackpointIds = track.second.stream()
|
List<TrackPoint.Id> trackpointIds = track.second.stream()
|
||||||
.map(it -> ContentUris.parseId(contentProviderUtils.insertTrackPoint(it, track.first.getId())))
|
.map(it -> ContentUris.parseId(contentProviderUtils.insertTrackPoint(it, track.first.id())))
|
||||||
.map(TrackPoint.Id::new).collect(Collectors.toList());
|
.map(TrackPoint.Id::new).toList();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
try (Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds.get(8))) {
|
try (Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds.get(8))) {
|
||||||
@@ -842,45 +859,36 @@ public class CustomContentProviderUtilsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetTrackPointLocationIterator_asc() {
|
public void testGetTrackPointIterator_asc() {
|
||||||
// given
|
// given
|
||||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||||
Pair<Track, List<TrackPoint>> track = TestDataUtil.createTrack(trackId, 10);
|
Pair<Track, List<TrackPoint>> track = TestDataUtil.createTrack(trackId, 10);
|
||||||
contentProviderUtils.insertTrack(track.first);
|
contentProviderUtils.insertTrack(track.first);
|
||||||
|
|
||||||
List<TrackPoint.Id> trackpointIds = track.second.stream()
|
List<TrackPoint.Id> trackpointIds = track.second.stream()
|
||||||
.map(it -> ContentUris.parseId(contentProviderUtils.insertTrackPoint(it, track.first.getId())))
|
.map(it -> ContentUris.parseId(contentProviderUtils.insertTrackPoint(it, track.first.id())))
|
||||||
.map(TrackPoint.Id::new).collect(Collectors.toList());
|
.map(TrackPoint.Id::new).toList();
|
||||||
|
|
||||||
TrackPoint.Id startTrackPointId = trackpointIds.get(0);
|
TrackPoint.Id startTrackPointId = trackpointIds.get(0);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, trackpointIds.get(0));
|
TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointIterator(trackId, trackpointIds.get(0));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
for (int i = 0; i < trackpointIds.size(); i++) {
|
for (int i = 0; i < trackpointIds.size(); i++) {
|
||||||
assertTrue(trackPointIterator.hasNext());
|
assertTrue(trackPointIterator.hasNext());
|
||||||
TrackPoint trackPoint = trackPointIterator.next();
|
TrackPoint trackPoint = trackPointIterator.next();
|
||||||
assertEquals(startTrackPointId.id() + i, trackPoint.getId().id());
|
assertEquals(startTrackPointId.id() + i, trackPoint.id().id());
|
||||||
|
|
||||||
checkLocation(i, trackPoint.getLocation());
|
Position position = trackPoint.position();
|
||||||
|
assertEquals(TestDataUtil.INITIAL_LATITUDE + i / 10000.0, position.latitude(), 0.01);
|
||||||
|
assertEquals(TestDataUtil.INITIAL_LONGITUDE - i / 10000.0, position.longitude(), 0.01);
|
||||||
|
assertEquals(i / 100.0, position.horizontalAccuracy().distance_m(), 0.01);
|
||||||
|
assertEquals(i * TestDataUtil.ALTITUDE_INTERVAL, position.altitude().toM(), 0.01);
|
||||||
}
|
}
|
||||||
assertFalse(trackPointIterator.hasNext());
|
assertFalse(trackPointIterator.hasNext());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the value of a location.
|
|
||||||
*
|
|
||||||
* @param i the index of this location which created in the method {@link TestDataUtil#createTrack(Track.Id, int)}
|
|
||||||
* @param location the location to be checked
|
|
||||||
*/
|
|
||||||
private void checkLocation(int i, Location location) {
|
|
||||||
assertEquals(TestDataUtil.INITIAL_LATITUDE + (double) i / 10000.0, location.getLatitude(), 0.01);
|
|
||||||
assertEquals(TestDataUtil.INITIAL_LONGITUDE - (double) i / 10000.0, location.getLongitude(), 0.01);
|
|
||||||
assertEquals((float) i / 100.0f, location.getAccuracy(), 0.01);
|
|
||||||
assertEquals(i * TestDataUtil.ALTITUDE_INTERVAL, location.getAltitude(), 0.01);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFormatIdListForUri() {
|
public void testFormatIdListForUri() {
|
||||||
assertEquals("", ContentProviderUtils.formatIdListForUri());
|
assertEquals("", ContentProviderUtils.formatIdListForUri());
|
||||||
@@ -892,11 +900,18 @@ public class CustomContentProviderUtilsTest {
|
|||||||
public void testGetSensorStats_noSensorData() {
|
public void testGetSensorStats_noSensorData() {
|
||||||
// given
|
// given
|
||||||
List<TrackPoint> trackPointList = new ArrayList<>();
|
List<TrackPoint> trackPointList = new ArrayList<>();
|
||||||
TrackPoint trackPoint = TestDataUtil.createTrackPoint(1);
|
TrackPoint trackPoint = new TrackPoint(
|
||||||
trackPoint.setType(TrackPoint.Type.TRACKPOINT);
|
null,
|
||||||
trackPoint.setPower(null);
|
TrackPoint.Type.TRACKPOINT,
|
||||||
trackPoint.setCadence(null);
|
TestDataUtil.createTrackPoint(1).position(),
|
||||||
trackPoint.setHeartRate(null);
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
trackPointList.add(trackPoint);
|
trackPointList.add(trackPoint);
|
||||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||||
Track track = TestDataUtil.createTrack(trackId);
|
Track track = TestDataUtil.createTrack(trackId);
|
||||||
@@ -1180,7 +1195,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
assertEquals(sensorStatistics.avgPower().getW(), stats.avgPower, 0f);
|
assertEquals(sensorStatistics.avgPower().getW(), stats.avgPower, 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testGetSensorStats_randomData(int totalPoints, boolean withStartSegments) {
|
private Pair<SensorStatistics, TestSensorDataUtil.SensorDataStats> testGetSensorStats_randomData(int totalPoints, boolean withStartSegments) {
|
||||||
// given
|
// given
|
||||||
Instant start = Instant.now();
|
Instant start = Instant.now();
|
||||||
TestSensorDataUtil sensorDataUtil = new TestSensorDataUtil();
|
TestSensorDataUtil sensorDataUtil = new TestSensorDataUtil();
|
||||||
@@ -1203,21 +1218,41 @@ public class CustomContentProviderUtilsTest {
|
|||||||
SensorStatistics sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
SensorStatistics sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
||||||
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
||||||
|
|
||||||
// then
|
return Pair.create(sensorStatistics, stats);
|
||||||
assertEquals(sensorStatistics.avgHeartRate().getBPM(), stats.avgHr, 0.01f);
|
|
||||||
assertEquals(sensorStatistics.maxHeartRate().getBPM(), stats.maxHr, 0.01f);
|
|
||||||
assertEquals(sensorStatistics.avgCadence().getRPM(), stats.avgCadence, 0.01f);
|
|
||||||
assertEquals(sensorStatistics.maxCadence().getRPM(), stats.maxCadence, 0.01f);
|
|
||||||
assertEquals(sensorStatistics.avgPower().getW(), stats.avgPower, 0.01f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetSensorStats_veryLongActivity12h() {
|
public void testGetSensorStats_veryLongActivity12h() {
|
||||||
testGetSensorStats_randomData(43200 / 6, false);
|
Pair<SensorStatistics, TestSensorDataUtil.SensorDataStats> result = testGetSensorStats_randomData(43200 / 6, false);
|
||||||
|
assertEquals(result.first.avgHeartRate().getBPM(), result.second.avgHr, 0.01f);
|
||||||
|
assertEquals(result.first.maxHeartRate().getBPM(), result.second.maxHr, 0.01f);
|
||||||
|
assertEquals(result.first.avgCadence().getRPM(), result.second.avgCadence, 0.01f);
|
||||||
|
assertEquals(result.first.maxCadence().getRPM(), result.second.maxCadence, 0.01f);
|
||||||
|
assertEquals(result.first.avgPower().getW(), result.second.avgPower, 0.01f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetSensorStats_withSeveralRandomStartSegments() {
|
public void testGetSensorStats_withSeveralRandomStartSegments() {
|
||||||
testGetSensorStats_randomData(5000, true);
|
Pair<SensorStatistics, TestSensorDataUtil.SensorDataStats> result = testGetSensorStats_randomData(5000, true);
|
||||||
|
assertEquals(result.first.avgHeartRate().getBPM(), result.second.avgHr, 0.01f);
|
||||||
|
assertEquals(result.first.maxHeartRate().getBPM(), result.second.maxHr, 0.01f);
|
||||||
|
assertEquals(result.first.avgCadence().getRPM(), result.second.avgCadence, 0.01f);
|
||||||
|
assertEquals(result.first.maxCadence().getRPM(), result.second.maxCadence, 0.01f);
|
||||||
|
assertEquals(result.first.avgPower().getW(), result.second.avgPower, 0.01f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TrackPoint getLastValidTrackPoint(Track.Id trackId) {
|
||||||
|
try (TrackPointIterator trackPointsCursor = contentProviderUtils.getTrackPointIterator(trackId, null)) {
|
||||||
|
List<TrackPoint> trackpoints = new ArrayList<>();
|
||||||
|
while (trackPointsCursor.hasNext())
|
||||||
|
trackpoints.add(trackPointsCursor.next());
|
||||||
|
trackpoints = trackpoints.reversed();
|
||||||
|
|
||||||
|
return trackpoints.reversed()
|
||||||
|
.stream()
|
||||||
|
.filter(it -> List.of(TrackPoint.Type.TRACKPOINT, TrackPoint.Type.SEGMENT_START_AUTOMATIC).contains(it.type()))
|
||||||
|
.findFirst().orElseThrow();
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-27
@@ -48,12 +48,10 @@ public class CustomSQLiteOpenHelperTest {
|
|||||||
public static Map<String, String> getSQL(SQLiteDatabase db, String type) {
|
public static Map<String, String> getSQL(SQLiteDatabase db, String type) {
|
||||||
HashMap<String, String> sqlMap = new HashMap<>();
|
HashMap<String, String> sqlMap = new HashMap<>();
|
||||||
try (Cursor cursor = db.query("sqlite_master", new String[]{"name", "SQL"}, "type=?", new String[]{type}, null, null, "name")) {
|
try (Cursor cursor = db.query("sqlite_master", new String[]{"name", "SQL"}, "type=?", new String[]{type}, null, null, "name")) {
|
||||||
if (cursor != null) {
|
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
sqlMap.put(cursor.getString(0), cursor.getString(1));
|
sqlMap.put(cursor.getString(0), cursor.getString(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return sqlMap;
|
return sqlMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +62,6 @@ public class CustomSQLiteOpenHelperTest {
|
|||||||
*/
|
*/
|
||||||
private static boolean hasSqlCreate(SQLiteDatabase db, String sqlCreate) {
|
private static boolean hasSqlCreate(SQLiteDatabase db, String sqlCreate) {
|
||||||
try (Cursor cursor = db.rawQuery("SELECT SQL FROM sqlite_master", null)) {
|
try (Cursor cursor = db.rawQuery("SELECT SQL FROM sqlite_master", null)) {
|
||||||
if (cursor != null) {
|
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
String sql = cursor.getString(0);
|
String sql = cursor.getString(0);
|
||||||
if (sqlCreate.equals(sql)) {
|
if (sqlCreate.equals(sql)) {
|
||||||
@@ -72,7 +69,6 @@ public class CustomSQLiteOpenHelperTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,18 +100,18 @@ public class CustomSQLiteOpenHelperTest {
|
|||||||
// Open database with SQL upgrade
|
// Open database with SQL upgrade
|
||||||
Map<String, String> tableByUpgrade;
|
Map<String, String> tableByUpgrade;
|
||||||
Map<String, String> indicesByUpgrade;
|
Map<String, String> indicesByUpgrade;
|
||||||
try (SQLiteDatabase dbUpgraded = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getReadableDatabase()) {
|
try (CustomSQLiteOpenHelper dbUpgraded = new CustomSQLiteOpenHelper(context, DATABASE_NAME)) {
|
||||||
tableByUpgrade = getSQL(dbUpgraded, "table");
|
tableByUpgrade = getSQL(dbUpgraded.getReadableDatabase(), "table");
|
||||||
indicesByUpgrade = getSQL(dbUpgraded, "index");
|
indicesByUpgrade = getSQL(dbUpgraded.getReadableDatabase(), "index");
|
||||||
}
|
}
|
||||||
context.deleteDatabase(DATABASE_NAME);
|
context.deleteDatabase(DATABASE_NAME);
|
||||||
|
|
||||||
// Open database via creation script
|
// Open database via creation script
|
||||||
Map<String, String> tablesByCreate;
|
Map<String, String> tablesByCreate;
|
||||||
Map<String, String> indicesByCreate;
|
Map<String, String> indicesByCreate;
|
||||||
try (SQLiteDatabase dbCreated = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getReadableDatabase()) {
|
try (CustomSQLiteOpenHelper dbCreated = new CustomSQLiteOpenHelper(context, DATABASE_NAME)) {
|
||||||
tablesByCreate = getSQL(dbCreated, "table");
|
tablesByCreate = getSQL(dbCreated.getReadableDatabase(), "table");
|
||||||
indicesByCreate = getSQL(dbCreated, "index");
|
indicesByCreate = getSQL(dbCreated.getReadableDatabase(), "index");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -138,14 +134,16 @@ public class CustomSQLiteOpenHelperTest {
|
|||||||
@Test
|
@Test
|
||||||
public void onDowngrade_ToVersion23() {
|
public void onDowngrade_ToVersion23() {
|
||||||
// Create most recent database schema
|
// Create most recent database schema
|
||||||
new CustomSQLiteOpenHelper(context, DATABASE_NAME).getReadableDatabase().close();
|
try(CustomSQLiteOpenHelper db = new CustomSQLiteOpenHelper(context, DATABASE_NAME)) {
|
||||||
|
db.getReadableDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
// Downgrade schema to version 23 (base version)
|
// Downgrade schema to version 23 (base version)
|
||||||
Map<String, String> tablesByDowngrade;
|
Map<String, String> tablesByDowngrade;
|
||||||
Map<String, String> indicesByDowngrade;
|
Map<String, String> indicesByDowngrade;
|
||||||
try (SQLiteDatabase db = new CustomSQLiteOpenHelper(context, DATABASE_NAME, 23).getReadableDatabase()) {
|
try (CustomSQLiteOpenHelper db = new CustomSQLiteOpenHelper(context, DATABASE_NAME, 23)) {
|
||||||
tablesByDowngrade = getSQL(db, "table");
|
tablesByDowngrade = getSQL(db.getReadableDatabase(), "table");
|
||||||
indicesByDowngrade = getSQL(db, "index");
|
indicesByDowngrade = getSQL(db.getReadableDatabase(), "index");
|
||||||
}
|
}
|
||||||
|
|
||||||
// then - verify table structure
|
// then - verify table structure
|
||||||
@@ -159,9 +157,9 @@ public class CustomSQLiteOpenHelperTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void track_uuid_unique() {
|
public void track_uuid_unique() {
|
||||||
try (SQLiteDatabase db = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getWritableDatabase()) {
|
try (CustomSQLiteOpenHelper db = new CustomSQLiteOpenHelper(context, DATABASE_NAME)) {
|
||||||
db.execSQL("INSERT INTO tracks (uuid) VALUES (0x00)");
|
db.getWritableDatabase().execSQL("INSERT INTO tracks (uuid) VALUES (0x00)");
|
||||||
db.execSQL("INSERT INTO tracks (uuid) VALUES (0x00)");
|
db.getWritableDatabase().execSQL("INSERT INTO tracks (uuid) VALUES (0x00)");
|
||||||
fail("unique constraint not enforced");
|
fail("unique constraint not enforced");
|
||||||
} catch (SQLiteConstraintException e) {
|
} catch (SQLiteConstraintException e) {
|
||||||
assertTrue(e.getMessage().contains("UNIQUE constraint failed: tracks.uuid"));
|
assertTrue(e.getMessage().contains("UNIQUE constraint failed: tracks.uuid"));
|
||||||
@@ -172,7 +170,8 @@ public class CustomSQLiteOpenHelperTest {
|
|||||||
public void upgrade_data_to_30() {
|
public void upgrade_data_to_30() {
|
||||||
// given: a track in version 29
|
// given: a track in version 29
|
||||||
createVersion23();
|
createVersion23();
|
||||||
try (SQLiteDatabase db29 = new CustomSQLiteOpenHelper(context, DATABASE_NAME, 29).getWritableDatabase()) {
|
try (CustomSQLiteOpenHelper db = new CustomSQLiteOpenHelper(context, DATABASE_NAME, 29)) {
|
||||||
|
SQLiteDatabase db29 = db.getWritableDatabase();
|
||||||
db29.beginTransaction();
|
db29.beginTransaction();
|
||||||
db29.execSQL("INSERT INTO tracks (_id) VALUES (1)");
|
db29.execSQL("INSERT INTO tracks (_id) VALUES (1)");
|
||||||
|
|
||||||
@@ -205,7 +204,8 @@ public class CustomSQLiteOpenHelperTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// when / then
|
// when / then
|
||||||
try (SQLiteDatabase db30 = new CustomSQLiteOpenHelper(context, DATABASE_NAME, 30).getWritableDatabase()) {
|
try (CustomSQLiteOpenHelper db = new CustomSQLiteOpenHelper(context, DATABASE_NAME, 30)) {
|
||||||
|
SQLiteDatabase db30 = db.getWritableDatabase();
|
||||||
{
|
{
|
||||||
// Track 1
|
// Track 1
|
||||||
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||||
@@ -265,7 +265,7 @@ public class CustomSQLiteOpenHelperTest {
|
|||||||
|
|
||||||
private void createVersion23() {
|
private void createVersion23() {
|
||||||
// Manually create database schema with version 23 (base version)
|
// Manually create database schema with version 23 (base version)
|
||||||
SQLiteDatabase dbBase = new SQLiteOpenHelper(context, DATABASE_NAME, null, 23) {
|
try(SQLiteOpenHelper dbBase = new SQLiteOpenHelper(context, DATABASE_NAME, null, 23) {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(SQLiteDatabase db) {
|
public void onCreate(SQLiteDatabase db) {
|
||||||
}
|
}
|
||||||
@@ -273,12 +273,10 @@ public class CustomSQLiteOpenHelperTest {
|
|||||||
@Override
|
@Override
|
||||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
}
|
}
|
||||||
}.getWritableDatabase();
|
}) {
|
||||||
|
dbBase.getWritableDatabase().execSQL(TRACKS_CREATE_TABLE_V23);
|
||||||
dbBase.execSQL(TRACKS_CREATE_TABLE_V23);
|
dbBase.getWritableDatabase().execSQL(TRACKPOINTS_CREATE_TABLE_V23);
|
||||||
dbBase.execSQL(TRACKPOINTS_CREATE_TABLE_V23);
|
dbBase.getWritableDatabase().execSQL(WAYPOINTS_CREATE_TABLE_V23);
|
||||||
dbBase.execSQL(WAYPOINTS_CREATE_TABLE_V23);
|
}
|
||||||
|
|
||||||
dbBase.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,6 +9,7 @@ import org.junit.runner.RunWith;
|
|||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
TrackSelection filter = new TrackSelection();
|
TrackSelection filter = new TrackSelection();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
SelectionData selection = filter.buildSelection();
|
ContentProviderUtils.SelectionData selection = filter.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertNull(selection.selection());
|
assertNull(selection.selection());
|
||||||
@@ -34,12 +35,11 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
TrackSelection filter = new TrackSelection().addTrackId(trackId);
|
TrackSelection filter = new TrackSelection().addTrackId(trackId);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
SelectionData selection = filter.buildSelection();
|
ContentProviderUtils.SelectionData selection = filter.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertEquals(selection.selection(), "_id IN (?)");
|
assertEquals("_id IN (?)", selection.selection());
|
||||||
assertEquals(selection.selectionArgs().length, 1);
|
assertEquals(List.of("1"), selection.selectionArgs());
|
||||||
assertEquals(selection.selectionArgs()[0], "1");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -54,28 +54,24 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
.addTrackId(trackId3);
|
.addTrackId(trackId3);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
SelectionData selection = filter.buildSelection();
|
ContentProviderUtils.SelectionData selection = filter.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertEquals(selection.selection(), "_id IN (?,?,?)");
|
assertEquals("_id IN (?,?,?)", selection.selection());
|
||||||
assertEquals(selection.selectionArgs().length, 3);
|
assertEquals(List.of("1", "2", "3"), selection.selectionArgs());
|
||||||
assertEquals(selection.selectionArgs()[0], "1");
|
|
||||||
assertEquals(selection.selectionArgs()[1], "2");
|
|
||||||
assertEquals(selection.selectionArgs()[2], "3");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFilterBuildSelection_onlyOneCategory() {
|
public void testFilterBuildSelection_onlyOneactivity_type_localized() {
|
||||||
// given
|
// given
|
||||||
TrackSelection filter = new TrackSelection().addActivityType("running");
|
TrackSelection filter = new TrackSelection().addActivityType("running");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
SelectionData selection = filter.buildSelection();
|
ContentProviderUtils.SelectionData selection = filter.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertEquals(selection.selection(), "category IN (?)");
|
assertEquals("activity_type_localized IN (?)", selection.selection());
|
||||||
assertEquals(selection.selectionArgs().length, 1);
|
assertEquals(List.of("running"), selection.selectionArgs());
|
||||||
assertEquals(selection.selectionArgs()[0], "running");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -88,15 +84,11 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
.addActivityType("trail walking");
|
.addActivityType("trail walking");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
SelectionData selection = filter.buildSelection();
|
ContentProviderUtils.SelectionData selection = filter.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertEquals(selection.selection(), "category IN (?,?,?,?)");
|
assertEquals("activity_type_localized IN (?,?,?,?)", selection.selection());
|
||||||
assertEquals(selection.selectionArgs().length, 4);
|
assertEquals(List.of("running", "road biking", "mountain biking", "trail walking"), selection.selectionArgs());
|
||||||
assertEquals(selection.selectionArgs()[0], "running");
|
|
||||||
assertEquals(selection.selectionArgs()[1], "road biking");
|
|
||||||
assertEquals(selection.selectionArgs()[2], "mountain biking");
|
|
||||||
assertEquals(selection.selectionArgs()[3], "trail walking");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -109,9 +101,9 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
TrackSelection filterOk = new TrackSelection().addDateRange(instant, instant.plus(Duration.ofDays(1)));
|
TrackSelection filterOk = new TrackSelection().addDateRange(instant, instant.plus(Duration.ofDays(1)));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
SelectionData selectionWrong1 = filterWrong1.buildSelection();
|
ContentProviderUtils.SelectionData selectionWrong1 = filterWrong1.buildSelection();
|
||||||
SelectionData selectionWrong2 = filterWrong2.buildSelection();
|
ContentProviderUtils.SelectionData selectionWrong2 = filterWrong2.buildSelection();
|
||||||
SelectionData selectionOk = filterOk.buildSelection();
|
ContentProviderUtils.SelectionData selectionOk = filterOk.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertNull(selectionWrong1.selection());
|
assertNull(selectionWrong1.selection());
|
||||||
@@ -120,10 +112,8 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
assertNull(selectionWrong2.selection());
|
assertNull(selectionWrong2.selection());
|
||||||
assertNull(selectionWrong2.selectionArgs());
|
assertNull(selectionWrong2.selectionArgs());
|
||||||
|
|
||||||
assertEquals(selectionOk.selection(), "starttime BETWEEN ? AND ?");
|
assertEquals("time_start BETWEEN ? AND ?", selectionOk.selection());
|
||||||
assertEquals(selectionOk.selectionArgs().length, 2);
|
assertEquals(List.of(Long.toString(instant.toEpochMilli()), Long.toString(instant.toEpochMilli() + oneDay)) , selectionOk.selectionArgs());
|
||||||
assertEquals(selectionOk.selectionArgs()[0], Long.toString(instant.toEpochMilli()));
|
|
||||||
assertEquals(selectionOk.selectionArgs()[1], Long.toString(instant.toEpochMilli() + oneDay));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -140,16 +130,11 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
.addActivityType("road biking");
|
.addActivityType("road biking");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
SelectionData selection = filter.buildSelection();
|
ContentProviderUtils.SelectionData selection = filter.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertEquals(selection.selection(), "_id IN (?,?,?) AND category IN (?,?)");
|
assertEquals("_id IN (?,?,?) AND activity_type_localized IN (?,?)", selection.selection());
|
||||||
assertEquals(selection.selectionArgs().length, 5);
|
assertEquals(List.of("1", "2", "3", "running", "road biking"), selection.selectionArgs());
|
||||||
assertEquals(selection.selectionArgs()[0], "1");
|
|
||||||
assertEquals(selection.selectionArgs()[1], "2");
|
|
||||||
assertEquals(selection.selectionArgs()[2], "3");
|
|
||||||
assertEquals(selection.selectionArgs()[3], "running");
|
|
||||||
assertEquals(selection.selectionArgs()[4], "road biking");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -169,16 +154,11 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
.addDateRange(instant, instant.plusMillis(oneDay));
|
.addDateRange(instant, instant.plusMillis(oneDay));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
SelectionData selection = filter.buildSelection();
|
ContentProviderUtils.SelectionData selection = filter.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertEquals(selection.selection(), "_id IN (?,?,?) AND starttime BETWEEN ? AND ?");
|
assertEquals("_id IN (?,?,?) AND time_start BETWEEN ? AND ?", selection.selection());
|
||||||
assertEquals(selection.selectionArgs().length, 5);
|
assertEquals(List.of("1", "2", "3", Long.toString(instant.toEpochMilli()), Long.toString(instant.toEpochMilli() + oneDay)), selection.selectionArgs());
|
||||||
assertEquals(selection.selectionArgs()[0], "1");
|
|
||||||
assertEquals(selection.selectionArgs()[1], "2");
|
|
||||||
assertEquals(selection.selectionArgs()[2], "3");
|
|
||||||
assertEquals(selection.selectionArgs()[3], Long.toString(instant.toEpochMilli()));
|
|
||||||
assertEquals(selection.selectionArgs()[4], Long.toString(instant.toEpochMilli() + oneDay));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -193,14 +173,10 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
.addDateRange(instant, instant.plusMillis(oneDay));
|
.addDateRange(instant, instant.plusMillis(oneDay));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
SelectionData selection = filter.buildSelection();
|
ContentProviderUtils.SelectionData selection = filter.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertEquals(selection.selection(), "category IN (?,?) AND starttime BETWEEN ? AND ?");
|
assertEquals("activity_type_localized IN (?,?) AND time_start BETWEEN ? AND ?", selection.selection());
|
||||||
assertEquals(selection.selectionArgs().length, 4);
|
assertEquals(List.of("running", "road biking", Long.toString(instant.toEpochMilli()), Long.toString(instant.toEpochMilli() + oneDay)), selection.selectionArgs());
|
||||||
assertEquals(selection.selectionArgs()[0], "running");
|
|
||||||
assertEquals(selection.selectionArgs()[1], "road biking");
|
|
||||||
assertEquals(selection.selectionArgs()[2], Long.toString(instant.toEpochMilli()));
|
|
||||||
assertEquals(selection.selectionArgs()[3], Long.toString(instant.toEpochMilli() + oneDay));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user