Compare commits
393 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9dac5d3e72 | |||
| cd7bf36aa9 | |||
| ab07d3b130 | |||
| 8b5320e40e | |||
| ef15632d7b | |||
| f5ce76955b | |||
| 3701b61852 | |||
| 3b8a243f55 | |||
| e19f55a618 | |||
| 9e6a787ed9 | |||
| 9626c5dd0b | |||
| 1e42ef5e6c | |||
| e5792336a8 | |||
| 9d4ec8ba2a | |||
| e928adf3b5 | |||
| f061243a7d | |||
| 1d623a076f | |||
| 13ed210933 | |||
| eba4918b01 | |||
| 9bde8944b9 | |||
| 1163f74b98 | |||
| c4b8082efc | |||
| 1fb8fc6a09 | |||
| dbc29a88ce | |||
| 05af52a21b | |||
| 1a5de37f8a | |||
| 015ddabf3a | |||
| 12a008a99a | |||
| 99ccf65cc2 | |||
| a159d4c375 | |||
| 01ac31716a | |||
| cfb2d0c2d3 | |||
| c05fa638a0 | |||
| 519f2b016e | |||
| 5cd6992c90 | |||
| 1631836327 | |||
| 07074ad260 | |||
| 8c20d5583a | |||
| 39bce2a487 | |||
| a221194ab1 | |||
| 55d3502ff9 | |||
| 0cd22a8ba0 | |||
| aff4bce3fe | |||
| bb290e2ee4 | |||
| 6d04659370 | |||
| 7193171a09 | |||
| 7d0d87e962 | |||
| d6cc7a0321 | |||
| c7bbaddc06 | |||
| 9af213fd12 | |||
| 77f56a5376 | |||
| ea5ac186d0 | |||
| 84c51ba8bb | |||
| 89f9f25c55 | |||
| f8cc257895 | |||
| ba66c45005 | |||
| 24b638aa30 | |||
| 45125bcda3 | |||
| 3359a30907 | |||
| 6f822afc71 | |||
| 230e97147c | |||
| b4ce99440a | |||
| 4d333bbafa | |||
| 0ee1c77680 | |||
| 183b47a1ea | |||
| 6fe6cee475 | |||
| 52228d44a9 | |||
| bc84e8cb9a | |||
| fb9e67ae85 | |||
| 8877f3fe14 | |||
| 963a37b877 | |||
| 3be1348df1 | |||
| 530967f4f6 | |||
| 25e590da02 | |||
| 0be4b25508 | |||
| 4c7d9384fb | |||
| 101701c6ff | |||
| 8f003e2f7f | |||
| 03d825d2ec | |||
| 0977d34889 | |||
| 6dee9884ff | |||
| 7b44b42def | |||
| ad712f28c6 | |||
| b287967bc4 | |||
| 29c72802aa | |||
| f5241e8046 | |||
| 1e720b2fa7 | |||
| 19382f19c4 | |||
| 14b8ab21db | |||
| 00b0e1face | |||
| 7f9057c519 | |||
| 02cec48ae4 | |||
| 0d45efdf07 | |||
| b6d935cf34 | |||
| 3228f58187 | |||
| 1e27542aea | |||
| fd7892bdf7 | |||
| 2ae8a753c9 | |||
| bb7efaa87c | |||
| 2eab7425f2 | |||
| a19bbd6153 | |||
| b523b8af9e | |||
| e9a71a10f6 | |||
| a0d69b589b | |||
| 71af495835 | |||
| ed4fb30b35 | |||
| 7f1c014bf8 | |||
| affa72fad7 | |||
| 14a14cd91f | |||
| ff89d2c5ec | |||
| 54f1690478 | |||
| 681058c3e6 | |||
| d93e025963 | |||
| 1f0a99c49a | |||
| 976d4f848f | |||
| 540a3340b3 | |||
| 2b4c68f3ec | |||
| 90d55e419e | |||
| ac31f4fd6a | |||
| 32a75bb479 | |||
| d32f1e05ec | |||
| defd6aed85 | |||
| 7463dc703c | |||
| 2ab04da936 | |||
| 6b84d3b9ba | |||
| 78b57cdc67 | |||
| 35de5b6b26 | |||
| d6da5f0862 | |||
| c3d55127fe | |||
| b1032f898a | |||
| d4a3762641 | |||
| ce5eec6a68 | |||
| cf71829bec | |||
| 39606ccd9f | |||
| 9dd67e4db7 | |||
| 212345dea1 | |||
| 08ba7d50c7 | |||
| 62401b2c5c | |||
| 7cfdacecc3 | |||
| 8f2e234c63 | |||
| dcd101a20a | |||
| cb5f3ace5a | |||
| 8e889fbce2 | |||
| ec390c4a81 | |||
| 30e9e930ab | |||
| 3719d9ac8f | |||
| e33cfa8666 | |||
| 8ff1db6a23 | |||
| a9228017fc | |||
| 41e5e71804 | |||
| 9f858168f8 | |||
| cfdcea2d88 | |||
| 2d2db41c5d | |||
| 2c73d0645a | |||
| ed1027eccc | |||
| 0dec17eba5 | |||
| 774d701a09 | |||
| 23bcbf09a5 | |||
| 115966cbdf | |||
| 1862bcc6f4 | |||
| 900910e9f7 | |||
| 45571c68c9 | |||
| 083b620e41 | |||
| 2f7a56bb17 | |||
| d8988663cb | |||
| c742dae6e0 | |||
| 292437e6bd | |||
| 8e9cde7222 | |||
| d07ab3cc22 | |||
| 90493ae498 | |||
| f6b994572a | |||
| 24826ff442 | |||
| d97e5ab1ef | |||
| 52a110d998 | |||
| 12e18b0bcd | |||
| 66dac4b0ca | |||
| 1bb8cbb82a | |||
| 2f5b7b63e4 | |||
| 58ca78b976 | |||
| 8079a25a42 | |||
| 0527e4ab14 | |||
| b6b4c5aa39 | |||
| 8c5c9d7947 | |||
| eac3d4b3db | |||
| d7bc550b29 | |||
| d76d5673a8 | |||
| 847be686ea | |||
| 1e3445cfa2 | |||
| d0f7939f1a | |||
| 000b8149f8 | |||
| 05ffa5a373 | |||
| e92f1107aa | |||
| 2ad1fab670 | |||
| 8c04129ef9 | |||
| 400d9fe948 | |||
| 5d79510750 | |||
| 18bb64580f | |||
| 7adebfc0d1 | |||
| 2178ff97d3 | |||
| f070495fb4 | |||
| d99fb5ea47 | |||
| d69a726e0e | |||
| e0111c2ffd | |||
| 487bd6c3a0 | |||
| 40aab9444a | |||
| 82b460b57b | |||
| 68baad16e2 | |||
| 26ed9b24d3 | |||
| 9f6089e7f1 | |||
| d0eef44bf8 | |||
| eead3b5721 | |||
| b8f0c59198 | |||
| 8f408dceba | |||
| d1dfaa5443 | |||
| b2da849e25 | |||
| 88579be0a2 | |||
| 70d971d8fd | |||
| 70b0dac4b6 | |||
| 038e8d3e06 | |||
| 932bb5ed5a | |||
| 8f2d1722ad | |||
| 9cb3d8c1bb | |||
| fa02ba9c69 | |||
| 4f31607e3e | |||
| b5165de7f0 | |||
| b32e87e10b | |||
| 29a1daa177 | |||
| d5cfd118f6 | |||
| 5981340bb9 | |||
| f562d51d9a | |||
| d77b9243af | |||
| 2c7e665206 | |||
| 1aa6b94f19 | |||
| 68c447f77e | |||
| 6b3a2eae5e | |||
| 3869dba72b | |||
| 5594a125d0 | |||
| 13380afef0 | |||
| 9c4c8131f5 | |||
| 7f10ae5c3f | |||
| 1fa45a3393 | |||
| f1b0c83196 | |||
| f0f7bca54b | |||
| 8f39bf139c | |||
| 85585969bd | |||
| 976e7be370 | |||
| 48dc64d4d7 | |||
| 16e69e850b | |||
| 5ece301ac0 | |||
| a5e500cdb1 | |||
| 8433ed82fe | |||
| 7c7defa233 | |||
| 1f84e44a8d | |||
| 858a8f20a8 | |||
| 6761c41dbf | |||
| 59e55be214 | |||
| 1b68cba1ba | |||
| 32f47340a9 | |||
| e25814c674 | |||
| 522baa5193 | |||
| b597b56b84 | |||
| d956e8d3f0 | |||
| 274781ccfa | |||
| e7e3ef23ff | |||
| 3289fa27e4 | |||
| 96fc074a4b | |||
| eaab09747c | |||
| bdb2a9dbc6 | |||
| 7e26571124 | |||
| 33b23a6e8f | |||
| 584adc7c57 | |||
| 667da76c4f | |||
| df6a3a7f04 | |||
| b254f5e40b | |||
| 7a9c3efb3b | |||
| 53dd7ada1f | |||
| 6a5edcbdef | |||
| 6e954261bb | |||
| 277061ad27 | |||
| c03f9fc792 | |||
| 657b82b57a | |||
| 4608edec56 | |||
| b086c30696 | |||
| 7eca033a8e | |||
| 6f3efd9ff2 | |||
| 7a2741e508 | |||
| 017eee8b68 | |||
| 024d7f8652 | |||
| 05c3a89682 | |||
| a3872581bb | |||
| e75451dab3 | |||
| 2897171132 | |||
| ed2f506b7b | |||
| 1e2ba3798d | |||
| a0a8ef9016 | |||
| e1f4406674 | |||
| d9b7f77bbb | |||
| e60dcbe539 | |||
| fc0cec6157 | |||
| 0ac34e0638 | |||
| 242147e491 | |||
| eba7a7fd89 | |||
| 348f74c4b3 | |||
| d898d51f1a | |||
| 6e9c95f232 | |||
| 9c7f458802 | |||
| f52b153e3a | |||
| 145cbf870b | |||
| 8e52574378 | |||
| e3ca1b7f21 | |||
| 2cdae4f460 | |||
| 422e171406 | |||
| 130581da6f | |||
| 9cd212f6b1 | |||
| 08701dcbf9 | |||
| 0f7f332087 | |||
| 9eed5aa70f | |||
| ea8cd9b6fc | |||
| 0ef40e7fab | |||
| 919ecbbffb | |||
| 21369860a7 | |||
| 7bad6a106a | |||
| bdf0c91714 | |||
| 453cbd705e | |||
| 0669eadc01 | |||
| 7a661cb521 | |||
| bb7cba0e47 | |||
| 1402563702 | |||
| 4d8c8e4ec9 | |||
| ef7f36d49e | |||
| e2ebf04621 | |||
| 029b60efff | |||
| 70bfa3e862 | |||
| 9f1087c9e6 | |||
| c64bae827b | |||
| 05bde1cf33 | |||
| 13c65b25ae | |||
| 0ab0f5d7a3 | |||
| 70ca0d50e2 | |||
| 8da64ad28d | |||
| 48d19aaa67 | |||
| 63bb512e3a | |||
| 3205c9de8a | |||
| bda1603e87 | |||
| 28d2f42096 | |||
| d83bc0b32a | |||
| 87508fca3f | |||
| 3250be9480 | |||
| 3848dd8fbd | |||
| 022cf5835b | |||
| 325681c6cd | |||
| 54be1b74fb | |||
| fd1a53d1a6 | |||
| 19f6a477c4 | |||
| 94babc9cd2 | |||
| 26414b0c7c | |||
| 956031264f | |||
| 7d8ac1f411 | |||
| a4121319fa | |||
| 1fdf1dd607 | |||
| 7759d1f9b0 | |||
| 43b114b64b | |||
| a808c52d48 | |||
| bb90c53057 | |||
| bd5bf530f0 | |||
| a4580fc84e | |||
| 9ab34eb846 | |||
| 997ddbb4f1 | |||
| 86a0054c24 | |||
| 3bd00c906a | |||
| 684419e86e | |||
| d42d1214ea | |||
| db24639829 | |||
| 0f6480197b | |||
| cce62c4c47 | |||
| bfdc04fbd9 | |||
| 18702bb6b3 | |||
| 65e920e8b6 | |||
| 7d24262b38 | |||
| 223ad70d36 | |||
| 7f618da7a6 | |||
| 0332b9eb87 | |||
| 29aefff788 | |||
| c9478ea38d | |||
| 1255ec8111 | |||
| f9d3108cb2 | |||
| e18d6b942d | |||
| cc5ced6383 | |||
| 163879487c | |||
| d7dd9f7c1b | |||
| 1259c42d28 | |||
| 9bccbe79a4 | |||
| 401e07d7e7 |
@@ -1,4 +1,5 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
liberapay: OpenTracks
|
||||
github: [rgmf, pstorch, dennisguse]
|
||||
custom: https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore
|
||||
|
||||
@@ -23,4 +23,5 @@ If applicable:
|
||||
**Technical information**
|
||||
- Device: [e.g. Nexus 7 (2013)]
|
||||
- OS: [e.g. LineageOS 15.1]
|
||||
- Version [e.g. v3.2.4]
|
||||
- OpenTracks version: [e.g. v3.2.4]
|
||||
- OpenTracks commit id (only for nightly builds): [e.g. 4b333bb]
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#https://github.com/marketplace/actions/android-emulator-runner
|
||||
name: Build nightly
|
||||
on: [ push ]
|
||||
|
||||
jobs:
|
||||
nightly-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2.4.0
|
||||
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: '11'
|
||||
|
||||
- name: Assemble nighlty APK
|
||||
shell: bash
|
||||
run: |
|
||||
echo ${NIGHTLY_STORE_FILE} | base64 -d > KEY_NIGHTLY.jks
|
||||
./gradlew assembleNightly -Dnightly_store_file=KEY_NIGHTLY.jks -Dnightly_store_password="${NIGHTLY_STORE_PASSWORD}" -Dnightly_key_alias="${NIGHTLY_KEY_ALIAS}" -Dnightly_key_password="${NIGHTLY_STORE_PASSWORD}"
|
||||
env:
|
||||
NIGHTLY_STORE_FILE: ${{ secrets.NIGHTLY_STORE_FILE }}
|
||||
NIGHTLY_STORE_PASSWORD: ${{ secrets.NIGHTLY_STORE_PASSWORD }}
|
||||
NIGHTLY_KEY_ALIAS: ${{ secrets.NIGHTLY_KEY_ALIAS }}
|
||||
|
||||
- name: Archive
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: OpenTracks-nightly.apk
|
||||
path: ./build/outputs/apk/nightly/*.apk
|
||||
retention-days: 7
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
schedule:
|
||||
- cron: '0 6 * * 1'
|
||||
|
||||
jobs:
|
||||
analyse:
|
||||
name: Analyse
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: java
|
||||
|
||||
- run: |
|
||||
./gradlew build
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
@@ -0,0 +1,19 @@
|
||||
name: Linting
|
||||
|
||||
on: [ push ]
|
||||
|
||||
jobs:
|
||||
analyse:
|
||||
name: Analyse
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2.4.0
|
||||
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '11'
|
||||
|
||||
- run: ./gradlew lint
|
||||
@@ -1,19 +1,26 @@
|
||||
#https://github.com/marketplace/actions/android-emulator-runner
|
||||
name: Test
|
||||
on: [push, pull_request]
|
||||
on: [ push ]
|
||||
jobs:
|
||||
test:
|
||||
timeout-minutes: 45
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
api-level: [ 23, 26, 29 ]
|
||||
target: [default]
|
||||
api-level: [ 23, 30 ]
|
||||
target: [ default ]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.4.0
|
||||
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '11'
|
||||
|
||||
- name: run tests
|
||||
uses: reactivecircus/android-emulator-runner@v2.19.0
|
||||
uses: reactivecircus/android-emulator-runner@v2.22.0
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
target: ${{ matrix.target }}
|
||||
@@ -21,3 +28,11 @@ jobs:
|
||||
profile: Nexus 6
|
||||
script: ./gradlew connectedCheck
|
||||
|
||||
- name: Archive
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: code-coverage-report
|
||||
path: build/reports/androidTests/
|
||||
retention-days: 7
|
||||
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DesignSurface">
|
||||
<option name="filePathToZoomLevelMap">
|
||||
<map>
|
||||
<entry key="src/main/res/menu/aggregated_statistics.xml" value="0.10208333333333333" />
|
||||
<entry key="src/main/res/xml/settings_sensors.xml" value="0.2101851851851852" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
@@ -6,7 +6,8 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Free</th>
|
||||
<th>Free (F-Droid)</th>
|
||||
<th>Free (Nightly for F-Droid)</th>
|
||||
<th>Donations</th>
|
||||
<th>Translations</th>
|
||||
</tr>
|
||||
@@ -16,6 +17,11 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
||||
<img alt="Get it on F-Droid" src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="60" align="middle">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://fdroid.storchp.de/fdroid/repo?fingerprint=99985A7E73DCB0B16C9BDDCE7A0B4996F88068AE7C771ED53E217E69CD1FF196">
|
||||
<img alt="Nightly builds (for F-Droid client)" src="https://opentracksapp.com/static/img/fdroid.storchp.de.png" height="90" align="middle">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://liberapay.com/OpenTracks/donate">
|
||||
<img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg" height="45" align="middle">
|
||||
@@ -31,14 +37,16 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
||||
<td align="center">
|
||||
<img alt="OpenTracks version published on F-Droid" src="https://img.shields.io/f-droid/v/de.dennisguse.opentracks.svg" align="middle" >
|
||||
</td>
|
||||
<td></td>
|
||||
<td align="center">
|
||||
<a href="bitcoin:bc1qckpls3lpjlcpyxzm66hcy65hnesas9zgacy6yc">
|
||||
<img alt="bitcoin:bc1qckpls3lpjlcpyxzm66hcy65hnesas9zgacy6yc" src="promotion/donations-bitcoin.png" height="90" align="middle">
|
||||
</a>
|
||||
</td align="center">
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td align="center">
|
||||
<a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">
|
||||
@@ -47,20 +55,30 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td align="center">
|
||||
<a href="https://www.amazon.de/dp/B09K2KNQ7X/">
|
||||
<img alt="Get it on Amazon App Store" src="https://images-na.ssl-images-amazon.com/images/G/01/mobile-apps/devportal2/res/images/amazon-appstore-badge-english-white.png" height="40" align="middle">
|
||||
</a>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Screenshots
|
||||
<div>
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot1.png">
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot2.png">
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot3.png">
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot4.png">
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot1.png">
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot2.png">
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot3.png">
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot4.png">
|
||||
</div>
|
||||
<div>
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot5.png">
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot6.png">
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot7.png">
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot8.png">
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot5.png">
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot6.png">
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot7.png">
|
||||
<img width="23%" src="fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot8.png">
|
||||
</div>
|
||||
|
||||
## Features
|
||||
@@ -69,7 +87,7 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
||||
* __Photos and Markers:__ mark interesting locations while tracking
|
||||
* __Export:__
|
||||
* export tracks either as KMZ (incl. photos), KML, or GPX
|
||||
* export automatically after each recording (e.g., to sync)
|
||||
* export automatically after each recording (e.g., to sync via [Nextcloud](https://nextcloud.com/))
|
||||
* avoid duplication: each exported file contain a random unique identifier (i.e., `opentracks:trackid`)
|
||||
* __Altitude:__
|
||||
* gain/loss via barometric sensor (if present)
|
||||
@@ -79,6 +97,10 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
||||
* cycling: speed and distance
|
||||
* cycling: cadence
|
||||
* cycling: power meter
|
||||
* running: speed and cadence
|
||||
* support for BLE sensor training only (i.e., without GPS) for indoor training
|
||||
|
||||
An overview of tested sensors: [README_TESTED_SENSORS.md](README_TESTED_SENSORS.md)
|
||||
|
||||
### Gadgetbridge integration
|
||||
|
||||
|
||||
@@ -28,9 +28,10 @@ Requirement: `gem install fastlane`
|
||||
(requires keystore)
|
||||
2. Upload signed apk to [Google Play Console](https://play.google.com/apps/publish)
|
||||
3. Update store meta data (e.g., description and screenshots)
|
||||
`fastlane supply --version_code VERSION_CODE --skip_upload_apk`
|
||||
(use the version code from the manifest)
|
||||
(requires API key)
|
||||
`fastlane metadata`
|
||||
(requires API key configured in `Appfile`)
|
||||
|
||||
Supported languages: https://support.google.com/googleplay/android-developer/table/4419860
|
||||
|
||||
## Translations (localization)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Overview of tested Bluetooth LE sensors
|
||||
|
||||
## HRS: Heartrate Service
|
||||
## 0x180D: Heartrate Service
|
||||
|
||||
* Decathlon|Geonaute|Kalenji Dual HRM Belt
|
||||
* moofit heart rate monitor
|
||||
@@ -11,31 +11,30 @@
|
||||
* 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)
|
||||
* 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)
|
||||
* Garmin HRM-Dual (Reference: 010-12883-00)
|
||||
|
||||
## CSCP: Cycling Cadence and Speed Service
|
||||
## 0x1816: Cycling Cadence and Speed Service
|
||||
|
||||
Please note that the according to the specification these sensors _may_ provide speed as well as cadence.
|
||||
However, often only one value is provided.
|
||||
|
||||
### Speed
|
||||
### Speed & Cadence
|
||||
|
||||
* Wahoo Blue SC
|
||||
|
||||
### Speed only
|
||||
|
||||
* Garmin Speed Sensor 2
|
||||
* Has updatable firmware that requires an account for garmin.com
|
||||
* This sensor reports speed data as cadence.
|
||||
A workaround is in place.
|
||||
|
||||
* Wahoo Speed (Model: WFRPMSPD)
|
||||
|
||||
* CooSpo cadence and speed sensor (model: BK467)
|
||||
|
||||
### Cadence
|
||||
### Cadence only
|
||||
|
||||
* Garmin Cadence Sensor 2
|
||||
* Wahoo Cadence (Model: WFPODCAD2)
|
||||
This sensor reports cadence data as speed.
|
||||
A workaround is in place.
|
||||
|
||||
## Cycling Power Measurement
|
||||
## 0x1818: Power Measurement
|
||||
|
||||
We only support _Instantaneous Power_.
|
||||
|
||||
@@ -43,6 +42,7 @@ _WARNING_: we only support physically movements with valid GPS coordinates.
|
||||
Using a device indoors (no GPS available) will only show the current power values, but won't store anything.
|
||||
Also the distance is not computed.
|
||||
|
||||
For cycling:
|
||||
* Assioma Duo Powermeter
|
||||
* Elite drivo II
|
||||
* QUARQ Red DZero Powermeter
|
||||
@@ -50,3 +50,28 @@ Also the distance is not computed.
|
||||
* Wahoo Kickr v4.0
|
||||
* Rotor 2INpower DM Road (only supports power measurement, cadence measurement is propietary @ firmware v1.061)
|
||||
|
||||
For running:
|
||||
* Stryd
|
||||
|
||||
## 0x1814: running cadence and speed
|
||||
|
||||
We do not support type of movement (i.e., walking vs running).
|
||||
|
||||
* Polar Stride
|
||||
* Stryd (incl. power)
|
||||
|
||||
## Tested Barometric Sensor Smartphones
|
||||
|
||||
There are smartphones that have a barometric sensor that OpenTracks uses to compute altitude gain and loss.
|
||||
|
||||
In our tests there are smartphones whose barometric sensor works well and others don't. Here is a list of the smartphones tested:
|
||||
|
||||
|Brand|Model|Android Version|Tests|
|
||||
|-----|-----|---------------|-----------|
|
||||
|Google|Pixel|LineageOS 17.1|Ok|
|
||||
|Google|Pixel 2 XL|LineageOS 17.1|Ok on the bike handlebars but it fails when I carry it in a pocket or backpack|
|
||||
|Google|Pixel 3|Android 11|Failure|
|
||||
|Google|Pixel 3a|Android 11|Ok|
|
||||
|Google|Pixel 3a|Lineage 18.1|Ok|
|
||||
|Google|Pixel 4a|Android 11|Ok|
|
||||
|Sony|Xperia XZ1 Compact|LineageOS 16.1|Failure|
|
||||
|
||||
@@ -6,7 +6,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.2.2'
|
||||
classpath 'com.android.tools.build:gradle:7.0.4'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,13 +24,21 @@ def getVersionName = { ->
|
||||
commandLine 'git', 'describe', '--tags'
|
||||
standardOutput = stdout
|
||||
}
|
||||
return stdout.toString().trim()
|
||||
} catch (ignored) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
def getVersionCode = { ->
|
||||
try {
|
||||
def stdout = new ByteArrayOutputStream()
|
||||
exec {
|
||||
commandLine 'git', 'log', '--format="%h"', '-n 1'
|
||||
commandLine 'git', 'rev-list', 'HEAD', '--count'
|
||||
standardOutput = stdout
|
||||
}
|
||||
return stdout.toString().replace('\n', '-').replace('"', '').trim()
|
||||
}
|
||||
catch (ignored) {
|
||||
return Integer.valueOf(stdout.toString().trim())
|
||||
} catch (ignored) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -39,14 +47,14 @@ android {
|
||||
compileOptions {
|
||||
coreLibraryDesugaringEnabled true
|
||||
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
compileSdkVersion 30
|
||||
compileSdkVersion 31
|
||||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
viewBinding true
|
||||
}
|
||||
|
||||
aaptOptions {
|
||||
@@ -55,17 +63,27 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "de.dennisguse.opentracks"
|
||||
versionCode 4253
|
||||
versionName "v3.18.6"
|
||||
versionCode 4646
|
||||
versionName "v3.24.0"
|
||||
|
||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||
|
||||
|
||||
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
targetSdkVersion 31
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
testInstrumentationRunner "de.dennisguse.opentracks.TestRunner"
|
||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||
}
|
||||
signingConfigs {
|
||||
nightly {
|
||||
if (System.getProperty("nightly_store_file") != null) {
|
||||
storeFile file(System.getProperty("nightly_store_file"))
|
||||
storePassword System.getProperty("nightly_store_password")
|
||||
keyAlias System.getProperty("nightly_key_alias")
|
||||
keyPassword System.getProperty("nightly_key_password")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -74,6 +92,11 @@ android {
|
||||
versionNameSuffix "-debug"
|
||||
}
|
||||
|
||||
nightly {
|
||||
signingConfig signingConfigs.nightly
|
||||
applicationIdSuffix ".nightly"
|
||||
}
|
||||
|
||||
release {
|
||||
minifyEnabled false
|
||||
}
|
||||
@@ -86,13 +109,21 @@ android {
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
variant.resValue "string", "applicationId", variant.applicationId
|
||||
|
||||
if (variant.buildType.name == 'nightly') {
|
||||
variant.outputs.all {
|
||||
setVersionCodeOverride(getVersionCode())
|
||||
setVersionNameOverride(getVersionName())
|
||||
outputFileName = "${applicationId}_${variant.versionCode}.apk"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.3.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.4.0'
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||
implementation 'com.google.android.material:material:1.4.0'
|
||||
@@ -103,5 +134,7 @@ dependencies {
|
||||
androidTestImplementation 'androidx.test:rules:1.4.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.4.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
androidTestImplementation 'org.mockito:mockito-android:3.10.0'
|
||||
androidTestImplementation 'org.mockito:mockito-android:4.2.0'
|
||||
|
||||
androidTestUtil 'androidx.test:orchestrator:1.4.1'
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ class TrackPoint {
|
||||
- sensor_power
|
||||
}
|
||||
|
||||
|
||||
class Marker {
|
||||
- id
|
||||
- trackId
|
||||
@@ -58,6 +57,26 @@ class Marker {
|
||||
- photoUrl
|
||||
}
|
||||
|
||||
class DataField {
|
||||
- key
|
||||
- title
|
||||
- isVisible
|
||||
- isPrimary
|
||||
- isWide
|
||||
}
|
||||
|
||||
class StatisticData {
|
||||
- value
|
||||
- unit
|
||||
- description
|
||||
}
|
||||
StatisticData o-- DataField
|
||||
|
||||
class Layout {
|
||||
- profile
|
||||
}
|
||||
Layout "1" o-- "many" DataField
|
||||
|
||||
note left of TrackPoint
|
||||
As of OpenTracks version 3.15.0, all times are using device time.
|
||||
Before that TrackPoint.time contained GPS time (determined by GPS hardware).
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
viewBox="0 0 24 24"
|
||||
id="vector"
|
||||
version="1.1"
|
||||
sodipodi:docname="ic_clear_filter.svg"
|
||||
inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
|
||||
<metadata
|
||||
id="metadata8">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs6" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
id="namedview4"
|
||||
showgrid="false"
|
||||
inkscape:zoom="16.541667"
|
||||
inkscape:cx="12.392292"
|
||||
inkscape:cy="24.710689"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="vector" />
|
||||
<path
|
||||
id="path"
|
||||
style="fill:#707070;fill-opacity:1"
|
||||
d="M 5.0390625 4 C 4.2090625 4 3.74 4.949375 4.25 5.609375 C 6.0862696 7.9637998 8.9006606 11.587089 9.4765625 12.328125 L 17.804688 4 L 5.0390625 4 z M 14 13.958984 L 10 17.958984 L 10 19 C 10 19.55 10.45 20 11 20 L 13 20 C 13.55 20 14 19.55 14 19 L 14 13.958984 z " />
|
||||
<rect
|
||||
style="fill:#707070;fill-opacity:1;stroke:none;stroke-width:1.118;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect833"
|
||||
width="21.37405"
|
||||
height="1.7606405"
|
||||
x="-10.108474"
|
||||
y="16.886091"
|
||||
ry="0.88032025"
|
||||
transform="rotate(-45)" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,3 @@
|
||||
README.md
|
||||
Appfile
|
||||
report.xml
|
||||
@@ -16,23 +16,11 @@
|
||||
default_platform(:android)
|
||||
|
||||
platform :android do
|
||||
desc "Runs all the tests"
|
||||
lane :test do
|
||||
gradle(task: "test")
|
||||
end
|
||||
|
||||
desc "Submit a new Beta Build to Crashlytics Beta"
|
||||
lane :beta do
|
||||
gradle(task: "clean assembleRelease")
|
||||
crashlytics
|
||||
|
||||
# sh "your_script.sh"
|
||||
# You can also use other beta testing services here
|
||||
end
|
||||
|
||||
desc "Deploy a new version to the Google Play"
|
||||
lane :deploy do
|
||||
gradle(task: "clean assembleRelease")
|
||||
upload_to_play_store
|
||||
desc "Upload metadata"
|
||||
lane :metadata do
|
||||
upload_to_play_store(
|
||||
skip_upload_apk: true,
|
||||
skip_upload_changelogs: true
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,4 +2,3 @@ v3.13.2: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Crash in landscape mode
|
||||
|
||||
|
||||
@@ -15,4 +15,3 @@ Developer:
|
||||
- Replaced communication via SharedPreferences
|
||||
- Use non-static service binder
|
||||
- Use altitude in the code base (instead of elevation)
|
||||
|
||||
|
||||
@@ -8,4 +8,3 @@ Bugfix:
|
||||
- Crash if using pace for voice announcements (introduced in v3.17.0)
|
||||
- Statistics showed pace initially (introduced in v3.17.0)
|
||||
- Rare crash when deleting tracks
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
v3.19.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Support for Running Speed and Cadence (RCS) sensors implemented
|
||||
- When sharing a track: a share picture is created
|
||||
- No vibration when starting a recording
|
||||
- While recording: UI shows most recently received altitude, speed, and bearing (even if not stored)
|
||||
|
||||
Bugfix:
|
||||
- Computation of total duration should not include paused durations (requires re-import)
|
||||
- (again) Use custom summaries if present
|
||||
|
||||
Developer:
|
||||
- Upgrade to SDK31 and AGP7.0.0 (requires JDK11)
|
||||
- TrackRecordingService: extacted sensor fusion (TrackPointCreator) and TrackPoint storage (TrackRecordingManager)
|
||||
- ExportImportTest is now deterministics/ constant
|
||||
@@ -0,0 +1,8 @@
|
||||
v3.19.1: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Updated translations.
|
||||
|
||||
Bugfix:
|
||||
- Export All Tracks bug fixed.
|
||||
- Resume Track bug fixed.
|
||||
@@ -0,0 +1,9 @@
|
||||
v3.20.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- While recording: statistics UI is now configurable
|
||||
|
||||
Bugfix:
|
||||
- Changing track name/ category while recording was ignored (introduced in v3.19.0)
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
v3.20.1: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Announcements can now be used by both distance and time
|
||||
- Announcements beep if no Text-to-speech software is installed
|
||||
- Removed adaptive GPS location frequency
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
v3.20.2: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Elevation gain and loss are integers instead of floats
|
||||
- Ask for permission before using Bluetooth by a Toast message
|
||||
|
||||
Bugfix:
|
||||
- Set FLAG_IMMUTABLE for PendingIntents to avoid crash on Android 12
|
||||
- Cadence sensor returns 0 instead of None when user is not pedaling
|
||||
- Speed sensor returns 0 instead of the last speed when it's stopped
|
||||
- With sensors, moving time stops when is not moving
|
||||
@@ -0,0 +1,19 @@
|
||||
v3.21.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- While recording: remove idle/non-idle creation of TrackPoints
|
||||
- Settings are now in separate folders
|
||||
- Opening OpenTracks from notification (while recording) and pressing back opens track list
|
||||
- GPX compatibilty: support <speed> for import
|
||||
|
||||
Bugfixes:
|
||||
- Altitude and distance (from BlE sensor): only reset counters if data was actually stored
|
||||
- Import without speed, requires computing speed
|
||||
- BLE running sensor not connected, should not be reported as present
|
||||
- BLE running sensor: show data while recording
|
||||
- Activity type icon was not updated while changing activity
|
||||
|
||||
Developer:
|
||||
- SharedPrefences via static reference
|
||||
- Tests are executed against in-memory database
|
||||
- TrackRecordingManager keeps last TrackPoint in memory
|
||||
@@ -0,0 +1,11 @@
|
||||
v3.22.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Locations are only stored if traveled distance > recording distance interval
|
||||
(removed left over functionality from idle/non-idle detection)
|
||||
- UI can be configured to show a clock
|
||||
- Idle speed is now configurable
|
||||
|
||||
Bugfix:
|
||||
- Intervals: crash when gain but not loss was stored
|
||||
- While recording: prevent crash when disabling voice announcements
|
||||
@@ -0,0 +1,10 @@
|
||||
v3.23.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Added UI profiles
|
||||
- Aggregated statistics for selected tracks
|
||||
- Re-implemented idle/non-idle TrackPoints as moving time stopped working (introduced in v3.22.0)
|
||||
|
||||
Bugfixes:
|
||||
- BLE sensors: clear data on disconnect
|
||||
- Automatic export needs to persist permission
|
||||
@@ -0,0 +1,17 @@
|
||||
v3.24.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- GPS is optional (also for indoor use): if using a BLE sensors that provide distance are used
|
||||
- Settings: option to disable BLE filtering of devices
|
||||
- Charts: altitude starts at minimal value instead of 0m.
|
||||
- UI supports showing stored timezone information
|
||||
|
||||
Bugfix:
|
||||
- Re-added workaround for crash due to clock traveling back in time (workaround was removed in v3.23.0)
|
||||
- Removed unecessary workaround for BLE cycling sensor (RTFM)
|
||||
- GPX importer did not handle missing data properly
|
||||
|
||||
Developer:
|
||||
- BLE sensors report data event-driven than pull-based
|
||||
- Reworked and added integration tests for recording
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
OpenTracks is a sport tracking application that completely respects your privacy.
|
||||
A sport tracking buddy that respects your privacy.
|
||||
|
||||
Features:
|
||||
* Sport tracking: track your sport and outdoor activities
|
||||
* Photos and markers: mark interesting locations while tracking
|
||||
* Altitude gain and loss: via barometric sensor
|
||||
* Altitude shown in EGM2008 (above mean sea level); exported as WGS84
|
||||
* Voice announcements
|
||||
* Supports Bluetooth LE sensors: heart rate, speed and distance (cycling), cadence (cycling), and power meter (cycling)
|
||||
* Export data: export tracks either as KMZ (incl. photos), KML, or GPX
|
||||
* No Internet access: Internet is not used
|
||||
Sport and outdoor activities grants happiness, skill and confidence.
|
||||
|
||||
Value your health by keeping track on your training.
|
||||
Records as you go running or walking, and gives you a bike computer with a bigger screen for cycling.
|
||||
Mark interesting locations along your way with pictures.
|
||||
Keep recorded statistics in great detail for analysis.
|
||||
Share only the data you want others to have.
|
||||
|
||||
* Voice announcements.
|
||||
* Supports Bluetooth LE sensors: heart rate, speed and distance (cycling), cadence (cycling), and power meter (cycling).
|
||||
* Altitude gain and loss: via barometric sensor.
|
||||
* Altitude shown in EGM2008 (above mean sea level); exported as WGS84.
|
||||
* Export data as tracks either as KMZ (incl. photos), KML, or GPX.
|
||||
* No Internet access or extra permissions.
|
||||
* Dark and light theme, respecting system settings.
|
||||
* No ads.
|
||||
|
||||
Libre software / Free Software / Open Source
|
||||
meaning you can use, study, change and share the source code.
|
||||
Licensed Apache 2.0
|
||||
|
||||
|
Before Width: | Height: | Size: 303 KiB After Width: | Height: | Size: 303 KiB |
|
Before Width: | Height: | Size: 574 KiB After Width: | Height: | Size: 574 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 456 KiB After Width: | Height: | Size: 456 KiB |
|
Before Width: | Height: | Size: 630 KiB After Width: | Height: | Size: 630 KiB |
|
Before Width: | Height: | Size: 416 KiB After Width: | Height: | Size: 416 KiB |
|
Before Width: | Height: | Size: 508 KiB After Width: | Height: | Size: 508 KiB |
|
Before Width: | Height: | Size: 798 KiB After Width: | Height: | Size: 798 KiB |
@@ -1 +1 @@
|
||||
A sport tracker that completely respects your privacy.
|
||||
A sport tracker buddy that respects your privacy.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
OpenTracks estas sporto spuranta aplikon kiu tute respektas vian privatecon.
|
||||
OpenTracks estas aplikaĵo por registri vian sportan aktivecon, kiu plene respektas vian privatecon.
|
||||
|
||||
Karakterizaĵoj:
|
||||
* Sporto spurado: spuri via sporto kaj subĉielaj aktivecoj
|
||||
* Fotoj kaj signoj: mark interesaj lokoj dum spurado
|
||||
* Altitudo akiro kaj perdo: tra barometric sentilo
|
||||
* Altitudo montrita en EGM2008 (supre signifas marnivelo); eksportita kiel WGS84
|
||||
* # Vo? o anoncoj
|
||||
* Elportas Bluetooth LE sentiloj: koro imposto, rapido kaj distanco (biciklado), kadenco (biciklado), kaj potenco metro (biciklado)
|
||||
* Eksportado datumoj: eksporti vojetoj aŭ kiel KMZ (incl. fotoj), KML, aŭ GPX
|
||||
* Neniu aliro al Interreto: Internet ne estas uzita
|
||||
Ebloj:
|
||||
• registri vian sportan kaj alian aktivecon eksterdome
|
||||
• fotoj kaj markoj: marki interesajn lokojn dum registri aktivecon
|
||||
• registri ŝanĝon de altitudo uzante barometron
|
||||
• montri altitudon en EGM2008 (super averaĝa marnivelo); elporti ilin kiel WGS84
|
||||
• voĉaj anoncoj
|
||||
• subteno por sentiloj BLE por registri vian pulson, rapidon, veturigitan distancon, pedal-rapidon, povumon (biciklado)
|
||||
• elporti datumojn al dosieroj KMZ (kun fotoj), KML aŭ GPX
|
||||
• funkcias malkonekte: interreta permeso ne estas uzata
|
||||
|
||||
@@ -1 +1 @@
|
||||
Sporto spuristo ke tute respektas vian privatecon.
|
||||
Registrilo de via sporta aktiveco, kiu respektas vian privatecon.
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
v3.19.1: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Traducciones actualizadas.
|
||||
|
||||
Corrección de errores:
|
||||
- Corregido el error de exportar todas las pistas.
|
||||
- Corregido el error de reanudar la pista.
|
||||
@@ -0,0 +1,7 @@
|
||||
v3.20.0: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Durante la grabación: la interfaz de usuario de las estadísticas es ahora configurable
|
||||
|
||||
Corrección de errores:
|
||||
- Se ignoraba el cambio de nombre de pista/categoría durante la grabación (introducido en la v3.19.0)
|
||||
@@ -0,0 +1,6 @@
|
||||
v3.20.1: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Los anuncios ahora se pueden utilizar tanto por distancia como por tiempo
|
||||
- Los anuncios emiten un pitido si no se ha instalado un software de texto a voz
|
||||
- Se ha eliminado la frecuencia de localización GPS adaptativa
|
||||
@@ -0,0 +1,10 @@
|
||||
v3.23.0: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Añadido perfiles UI
|
||||
- Estadísticas agregadas para las pistas seleccionadas
|
||||
- Reimplementación de TrackPoints inactivos/no inactivos ya que el tiempo de movimiento dejó de funcionar (introducido en la v3.22.0)
|
||||
|
||||
Corrección de errores:
|
||||
- Sensores BLE: borrado de datos al desconectarse
|
||||
- La exportación automática necesita persistir el permiso
|
||||
@@ -0,0 +1,8 @@
|
||||
v3.19.1 : OpenTracks
|
||||
|
||||
Changements :
|
||||
- Traductions mises à jour.
|
||||
|
||||
Correction de bogue :
|
||||
- Correction du bug de l'exportation de toutes les pistes.
|
||||
- Correction du bogue "Reprendre une piste".
|
||||
@@ -0,0 +1,7 @@
|
||||
v3.20.0 : OpenTracks
|
||||
|
||||
Changements :
|
||||
- Pendant l'enregistrement : l'interface des statistiques est maintenant configurable
|
||||
|
||||
Correction d'un bug :
|
||||
- Le changement de nom de piste/catégorie pendant l'enregistrement était ignoré (introduit dans la v3.19.0)
|
||||
@@ -0,0 +1,6 @@
|
||||
v3.20.1 : OpenTracks
|
||||
|
||||
Changements :
|
||||
- Les annonces peuvent maintenant être utilisées à la fois par distance et par temps.
|
||||
- Les annonces émettent un bip si aucun logiciel de synthèse vocale n'est installé.
|
||||
- Suppression de la fréquence de localisation GPS adaptative
|
||||
@@ -0,0 +1,16 @@
|
||||
v3.18.6: „OpenTracks“
|
||||
|
||||
Pakeitimai:
|
||||
- „Rodyti žemėlapyje“ funkcionalumas per KML, o ne KMZ
|
||||
- Pranešime balsu nėra programos pavadinimo
|
||||
- Įrašant: aukščio padidėjimas / praradimas atnaujinamas kas sekundę
|
||||
- Išversti parduotuvės aprašymai
|
||||
|
||||
Bufiksai:
|
||||
- Ištaisyti neteisingi balso pranešimų vertimai
|
||||
|
||||
Programuotojas:
|
||||
- „Android12“ leidimai (BLUETOOTH_SCAN)
|
||||
- „Android12“ leidimai (ACCESS_COARSE_LOCATION)
|
||||
- Tvirtinimas „TrackPoints“ (testavimas)
|
||||
- Pašalinti nepanaudoti vertimai
|
||||
@@ -0,0 +1,11 @@
|
||||
„OpenTracks“ yra sporto stebėjimo programa, visiškai gerbianti jūsų privatumą.
|
||||
|
||||
Funkcijos:
|
||||
* Sporto sekimas: stebėkite savo sportinę ir lauko veiklą
|
||||
* Nuotraukos ir žymekliai: stebėdami stebėkite įdomias vietas
|
||||
* Aukščio padidėjimas ir praradimas: per barometrinį jutiklį
|
||||
* EGM2008 nurodytas aukštis (virš vidutinio jūros lygio); eksportuota kaip WGS84
|
||||
* Balso pranešimai
|
||||
* Palaiko „Bluetooth LE“ jutiklius: širdies ritmą, greitį ir atstumą (važiuojant dviračiu), ritmą (dviračiu) ir galios matuoklį (važiuojant dviračiu)
|
||||
* Eksportuoti duomenis: eksportuoti takelius kaip KMZ (įskaitant nuotraukas), KML arba GPX
|
||||
* Nėra interneto prieigos: internetas nenaudojamas
|
||||
@@ -0,0 +1 @@
|
||||
Sporto sekimo priemonė, visiškai gerbianti jūsų privatumą.
|
||||
@@ -0,0 +1 @@
|
||||
OpenTracks
|
||||
@@ -0,0 +1,16 @@
|
||||
v3.18.6: OpenTracks
|
||||
|
||||
Endringer:
|
||||
- «Vis på kart»-funksjonalitet via KML istedenfor KMZ
|
||||
- Stemmekunngjøringer inkluderer ikke programnavn
|
||||
- Under opptak: Høydeendringer registrert hvert sekund
|
||||
- Butikk-oversettelser oversatt
|
||||
|
||||
Feilfikser:
|
||||
- Fikset ugyldige stemmekunngjøringsoversettelser
|
||||
|
||||
Utviklere:
|
||||
- Android12-tilganger (Blåtannsskanning)
|
||||
- Android12-tilganger (tilgang til grovposisjon)
|
||||
- Fastsetting for TrackPoints (testing)
|
||||
- Fjernet ubrukte oversettelser
|
||||
@@ -0,0 +1,11 @@
|
||||
OpenTracks er en app for opptak av sportsaktivitet som respekterer ditt personvern.
|
||||
|
||||
Funksjoner:
|
||||
* Sportsporing: Registrer dine sports- og utendørsaktiviteter
|
||||
* Bilder og markører: Marker interessante steder under sporing
|
||||
* Total stigning og nedstigning: via barometersensor
|
||||
* Høyde vist i EGM2008 (over havnivå); eksportert som WGS84
|
||||
* Talemeldinger
|
||||
* Støtter Blåtann LE-sensorer: puls, hastighet og distanse (sykling), kadens (sykling) og wattmåler (sykling)
|
||||
* Eksport av data: Enten som KMZ (inkl. bilder), KML eller GPX
|
||||
* Ingen internettilgang: Internett brukes ikke
|
||||
@@ -0,0 +1 @@
|
||||
En opptaker av sportsaktivitet som respekterer ditt personvern.
|
||||
@@ -0,0 +1 @@
|
||||
OpenTracks
|
||||
@@ -0,0 +1,16 @@
|
||||
v3.18.6: OpenTracks
|
||||
|
||||
Zmiany:
|
||||
- "Pokaż na mapie"- Funkcjonalość wraz z KML zamiast KMZ
|
||||
- Komunikaty głosowe nie podają nazwy aplikacji
|
||||
- Podczas śledzenia: Zmiana wysokości aktualizowana co sekundę
|
||||
- Przetłumaczone opisy w sklepach aplikacji
|
||||
|
||||
Naprawione błędy:
|
||||
- Naprawiono nieprawidłowe tłumaczenia wiadomości głosowych
|
||||
|
||||
Developer:
|
||||
- Uprawnienia Android12 (BLUETOOTH_SCAN)
|
||||
- Uprawnienia Android12 (ACCESS_COARSE_LOCATION)
|
||||
- Zastosowanie TrackPoints (testowane)
|
||||
- Usunięcie niepotrzebnych tłumaczeń
|
||||
@@ -0,0 +1,11 @@
|
||||
v3.22.0: OpenTracks
|
||||
|
||||
Zmiany:
|
||||
- Lokalizacja zapisywana tylko wtedy gdy pokonany dystans jest większy od minimalnego przedziału śledzenia dystansu
|
||||
(usunięto funkcjonalność z wykrywania bezczynności)
|
||||
- UI może zostać skonfigurowane do wyświetlania zegara
|
||||
- Prędkość ruchu bez oporu (prędkość jałowa) jest teraz konfigurowalna
|
||||
|
||||
Poprawki:
|
||||
- Przedziały: awaria, gdy zapisano zwiększenie wysokości, ale nie spadek
|
||||
- Podczas nagrywania: zapobieganie awariom podczas wyłączania komunikatów głosowych
|
||||
@@ -3,9 +3,9 @@ OpenTracks jest aplikacją do śledzenia sportu, która całkowicie szanuje Twoj
|
||||
Cechy:
|
||||
* Śledzenie sportu: śledź swoją aktywność sportową i outdoorową.
|
||||
* Zdjęcia i markery: zaznacz interesujące lokalizacje podczas śledzenia.
|
||||
* Przyrost i utrata wysokości: poprzez czujnik barometryczny
|
||||
* Zmiana wysokości: poprzez czujnik barometryczny
|
||||
* Wysokość pokazana w EGM2008 (powyżej średniego poziomu morza); wyeksportowana jako WGS84
|
||||
* Komunikaty głosowe
|
||||
* Obsługa czujników Bluetooth LE: tętno, prędkość i dystans (kolarstwo), kadencja (kolarstwo) i miernik mocy (kolarstwo)
|
||||
* Eksport danych: eksport tras jako KMZ (w tym zdjęcia), KML lub GPX
|
||||
* Brak dostępu do Internetu: Internet nie jest używany
|
||||
* Dostępu do Internetu: Internet nie jest używany
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
v3.18.6: OpenTracks
|
||||
|
||||
Mudanças:
|
||||
- Funcionalidade "Mostrar no mapa" via KML em vez de KMZ
|
||||
- O anúncio de voz não inclui o nome do aplicativo
|
||||
- Durante a gravação: o ganho / perda de elevação é atualizado a cada segundo
|
||||
- Descrições da loja traduzidas
|
||||
|
||||
Correções:
|
||||
- Correções de traduções inválidas no anúncios de voz
|
||||
|
||||
Desenvolvedores:
|
||||
- Permissões Android12 (BLUETOOTH_SCAN)
|
||||
- Permissões Android12 (ACCESS_COARSE_LOCATION)
|
||||
- Confirmação de TrackPoints (em teste)
|
||||
- Removidas as traduções não utilizadas
|
||||
@@ -7,5 +7,5 @@ Características:
|
||||
* Altitude mostrada no EGM2008 (acima do nível médio do mar); exportado como WGS84
|
||||
* Anúncios de voz
|
||||
* Suporta sensores Bluetooth LE: frequência cardíaca, velocidade e distância (ciclismo), cadência (ciclismo) e medidor de potência (ciclismo)
|
||||
* Exportar dados: exporte trilhas como KMZ (incluindo fotos), KML ou GPX
|
||||
* Exportar dados: exporte trilhas como KMZ (incluindo fotos), KML, ou GPX
|
||||
* Sem acesso à Internet: a Internet não é usada
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
v3.18.6: OpenTracks
|
||||
|
||||
Spremembe:
|
||||
|
||||
- "Prikaži na zemljevidu"-funkcionalnost prek KML namesto KMZ
|
||||
- Glasovno obvestilo ne vključuje imena aplikacije
|
||||
- Med snemanjem: pridobitev/izguba nadmorske višine se posodobi vsako sekundo
|
||||
- Prevedeni opisi trgovin
|
||||
|
||||
Bufiksi:
|
||||
- Vključene so bile naslednje možnosti: popravljeni neveljavni prevodi glasovnih napovedi
|
||||
|
||||
Razvijalec:
|
||||
- Android12 (BLUETOOTH_SCAN)
|
||||
- Android12 dovoljenja (ACCESS_COARSE_LOCATION)
|
||||
- Zagotavljanje za TrackPoints (testiranje)
|
||||
@@ -1,7 +1,7 @@
|
||||
OpenTracks je aplikacija za sledenje športu, ki popolnoma spoštuje vašo zasebnost.
|
||||
|
||||
Značilnosti:
|
||||
* Športno sledenje: sledite svojim športnim aktivnostim in aktivnostim na prostem.
|
||||
* Sledenje športu: sledite svojim športnim aktivnostim in aktivnostim na prostem.
|
||||
* Fotografije in oznake: med sledenjem označite zanimive lokacije
|
||||
* Pridobivanje in izgubljanje nadmorske višine: prek barometričnega senzorja
|
||||
* Nadmorska višina je prikazana v EGM2008 (nad srednjo morsko gladino); izvozi se kot WGS84
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
v3.18.6: OpenTracks
|
||||
|
||||
Förändring:
|
||||
- "Visa på kartan" - funktionalitet via KML istället för KMZ
|
||||
- Röstmeddelande inkluderar inte appnamn
|
||||
- Under inspelning: höjd vinst / förlust uppdateras varje sekund
|
||||
- Store beskrivningar översatt
|
||||
|
||||
Bufixar:
|
||||
- Fast ogiltig röst tillkännagivande översättningar
|
||||
|
||||
Utvecklare:
|
||||
- Android12 behörigheter (BLUETOOTH_SCAN)
|
||||
- Android12 behörigheter (ACCESS_COARSE_LOCATION)
|
||||
- Bedömning av spårpunkter (provning)
|
||||
- Bort oanvända översättningar
|
||||
@@ -1,5 +1,6 @@
|
||||
#Wed Jul 28 23:10:13 CEST 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
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.matcher.ViewMatchers.hasDescendant;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.espresso.ViewInteraction;
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.hamcrest.core.IsInstanceOf;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class EspressoAggregatedFilterTest {
|
||||
|
||||
private final String CATEGORY = "category";
|
||||
|
||||
@Rule
|
||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Pair<Track, List<TrackPoint>> pair = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 20);
|
||||
pair.first.setCategory(CATEGORY);
|
||||
TestDataUtil.insertTrackWithLocations(new ContentProviderUtils(ApplicationProvider.getApplicationContext()), pair.first, pair.second);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void espressoAggregatedFilterTest() {
|
||||
// open AggregatedStatisticsActivity through toolbar's menu item
|
||||
onView(withId(R.id.toolbar)).check(matches(isDisplayed()));
|
||||
onView(withId(R.id.toolbar)).check(matches(hasDescendant(withId(R.id.track_list_aggregated_stats))));
|
||||
onView(withId(R.id.track_list_aggregated_stats)).perform(click());
|
||||
|
||||
// open FilterDialogFragment through toolbar's menu item
|
||||
onView(withId(R.id.toolbar)).check(matches(isDisplayed()));
|
||||
onView(withId(R.id.toolbar)).check(matches(hasDescendant(withId(R.id.aggregated_statistics_filter))));
|
||||
onView(withId(R.id.aggregated_statistics_filter)).perform(click());
|
||||
|
||||
// check there's a checkbox with CATEGORY text
|
||||
ViewInteraction checkBox = onView(
|
||||
allOf(withId(R.id.filter_dialog_check_button), withText(CATEGORY),
|
||||
withParent(allOf(withId(R.id.filter_items),
|
||||
withParent(IsInstanceOf.<View>instanceOf(android.view.ViewGroup.class)))),
|
||||
isDisplayed()));
|
||||
checkBox.check(matches(isDisplayed()));
|
||||
|
||||
// check there's an edit text for "from date"
|
||||
onView(withId(R.id.filter_date_edit_text_from)).check(matches(isDisplayed()));
|
||||
|
||||
// check there's an edit text for "to date"
|
||||
onView(withId(R.id.filter_date_edit_text_to)).check(matches(isDisplayed()));
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
|
||||
import androidx.test.espresso.DataInteraction;
|
||||
import androidx.test.espresso.UiController;
|
||||
import androidx.test.espresso.ViewAction;
|
||||
import androidx.test.espresso.ViewInteraction;
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onData;
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.scrollTo;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.anything;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
/**
|
||||
* Espresso test that checks the unit for activity category selected.
|
||||
* Also it checks that unit is properly when user change from a pace activity to an speed one.
|
||||
*/
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class EspressoCategoryUnitTest {
|
||||
|
||||
@Rule
|
||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||
|
||||
@Rule
|
||||
public TestRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
private final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
|
||||
@Rule
|
||||
public TestRule mLocaleRule = new PreferenceMetricUnitRule(context, true);
|
||||
|
||||
@Test
|
||||
public void changeBetweenCategoryUpdateProperlyUnitTest() {
|
||||
// Start record.
|
||||
ViewInteraction appCompatImageButton = onView(
|
||||
allOf(withId(R.id.controller_record), withContentDescription("Record"),
|
||||
childAtPosition(
|
||||
allOf(withId(R.id.controller_container),
|
||||
childAtPosition(
|
||||
withId(R.id.controller_fragment),
|
||||
0)),
|
||||
0),
|
||||
isDisplayed()));
|
||||
appCompatImageButton.perform(click());
|
||||
|
||||
{
|
||||
// Change to running activity (pace activity).
|
||||
ViewInteraction appCompatImageView = onView(
|
||||
allOf(withId(R.id.stats_activity_type_icon),
|
||||
childAtPosition(
|
||||
childAtPosition(
|
||||
withClassName(is("android.widget.ScrollView")),
|
||||
0),
|
||||
7)));
|
||||
appCompatImageView.perform(scrollTo(), click());
|
||||
|
||||
DataInteraction imageView = onData(anything())
|
||||
.inAdapterView(allOf(withId(R.id.choose_activity_type_grid_view),
|
||||
childAtPosition(
|
||||
withClassName(is("android.widget.LinearLayout")),
|
||||
0)))
|
||||
.atPosition(1);
|
||||
imageView.perform(click());
|
||||
}
|
||||
|
||||
// Pace (min/km) in all fields.
|
||||
{
|
||||
ViewInteraction textView = onView(allOf(withId(R.id.stats_speed_unit)));
|
||||
textView.perform(waitFor(2000));
|
||||
textView.check(matches(withText("min/km")));
|
||||
}
|
||||
{
|
||||
ViewInteraction textView = onView(allOf(withId(R.id.stats_average_speed_unit)));
|
||||
textView.check(matches(withText("min/km")));
|
||||
}
|
||||
{
|
||||
ViewInteraction textView = onView(allOf(withId(R.id.stats_max_speed_unit)));
|
||||
textView.check(matches(withText("min/km")));
|
||||
}
|
||||
{
|
||||
ViewInteraction textView = onView(allOf(withId(R.id.stats_moving_speed_unit)));
|
||||
textView.check(matches(withText("min/km")));
|
||||
}
|
||||
|
||||
{
|
||||
// Change to biking activity (speed activity).
|
||||
ViewInteraction appCompatImageView = onView(
|
||||
allOf(withId(R.id.stats_activity_type_icon),
|
||||
childAtPosition(
|
||||
childAtPosition(
|
||||
withClassName(is("android.widget.ScrollView")),
|
||||
0),
|
||||
7)));
|
||||
appCompatImageView.perform(scrollTo(), click());
|
||||
|
||||
DataInteraction imageView = onData(anything())
|
||||
.inAdapterView(allOf(withId(R.id.choose_activity_type_grid_view),
|
||||
childAtPosition(
|
||||
withClassName(is("android.widget.LinearLayout")),
|
||||
0)))
|
||||
.atPosition(9);
|
||||
imageView.perform(click());
|
||||
}
|
||||
|
||||
// Speed (km/h) in all fields.
|
||||
{
|
||||
ViewInteraction textView = onView(allOf(withId(R.id.stats_speed_unit)));
|
||||
textView.perform(waitFor(2000));
|
||||
textView.check(matches(withText("km/h")));
|
||||
}
|
||||
{
|
||||
ViewInteraction textView = onView(allOf(withId(R.id.stats_average_speed_unit)));
|
||||
textView.check(matches(withText("km/h")));
|
||||
}
|
||||
{
|
||||
ViewInteraction textView = onView(allOf(withId(R.id.stats_max_speed_unit)));
|
||||
textView.check(matches(withText("km/h")));
|
||||
}
|
||||
{
|
||||
ViewInteraction textView = onView(allOf(withId(R.id.stats_moving_speed_unit)));
|
||||
textView.check(matches(withText("km/h")));
|
||||
}
|
||||
}
|
||||
|
||||
private static Matcher<View> childAtPosition(
|
||||
final Matcher<View> parentMatcher, final int position) {
|
||||
|
||||
return new TypeSafeMatcher<View>() {
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("Child at position " + position + " in parent ");
|
||||
parentMatcher.describeTo(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesSafely(View view) {
|
||||
ViewParent parent = view.getParent();
|
||||
return parent instanceof ViewGroup && parentMatcher.matches(parent)
|
||||
&& view.equals(((ViewGroup) parent).getChildAt(position));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static ViewAction waitFor(final long duration_ms) {
|
||||
return new ViewAction() {
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Wait for milliseconds.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(UiController uiController, final View view) {
|
||||
uiController.loopMainThreadForAtLeast(duration_ms);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.assertThat;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.espresso.NoMatchingViewException;
|
||||
import androidx.test.espresso.UiController;
|
||||
import androidx.test.espresso.ViewAction;
|
||||
import androidx.test.espresso.ViewAssertion;
|
||||
import androidx.test.espresso.ViewInteraction;
|
||||
import androidx.test.espresso.action.MotionEvents;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.ActivityTestRule;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.DataField;
|
||||
import de.dennisguse.opentracks.content.data.Layout;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class EspressoCustomLayoutTest {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<TrackListActivity> mActivityTestRule = new ActivityTestRule<>(TrackListActivity.class);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule =
|
||||
GrantPermissionRule.grant(
|
||||
"android.permission.ACCESS_FINE_LOCATION");
|
||||
|
||||
@Test
|
||||
public void customLayoutTest() {
|
||||
// TrackListActivity: start recording
|
||||
ViewInteraction trackControllerRecordButton = onView(withId(R.id.controller_record));
|
||||
trackControllerRecordButton.perform(click());
|
||||
|
||||
// Get custom layout preferences and check all data fields are showed.
|
||||
Layout layout = PreferencesUtils.getCustomLayout();
|
||||
|
||||
onView(withId(R.id.stats_recycler_view)).check(new RecyclerViewItemCountAssertion((int) layout.getFields().stream().filter(DataField::isVisible).count()));
|
||||
|
||||
// stop recording
|
||||
ViewInteraction trackControllerStopButton = onView(withId(R.id.controller_stop));
|
||||
trackControllerStopButton.perform(veryLongTouch(1600));
|
||||
}
|
||||
|
||||
private static class RecyclerViewItemCountAssertion implements ViewAssertion {
|
||||
private final int expectedCount;
|
||||
|
||||
public RecyclerViewItemCountAssertion(int expectedCount) {
|
||||
this.expectedCount = expectedCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(View view, NoMatchingViewException noViewFoundException) {
|
||||
if (noViewFoundException != null) {
|
||||
throw noViewFoundException;
|
||||
}
|
||||
|
||||
RecyclerView recyclerView = (RecyclerView) view;
|
||||
RecyclerView.Adapter<?> adapter = recyclerView.getAdapter();
|
||||
assertThat(adapter.getItemCount(), is(expectedCount));
|
||||
}
|
||||
}
|
||||
|
||||
private static Matcher<View> childAtPosition(
|
||||
final Matcher<View> parentMatcher, final int position) {
|
||||
|
||||
return new TypeSafeMatcher<>() {
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("Child at position " + position + " in parent ");
|
||||
parentMatcher.describeTo(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesSafely(View view) {
|
||||
ViewParent parent = view.getParent();
|
||||
return parent instanceof ViewGroup && parentMatcher.matches(parent)
|
||||
&& view.equals(((ViewGroup) parent).getChildAt(position));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static ViewAction veryLongTouch(final int duration_ms) {
|
||||
return new ViewAction() {
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Perform long touch.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(UiController uiController, final View view) {
|
||||
// Get view absolute position
|
||||
int[] location = new int[2];
|
||||
view.getLocationOnScreen(location);
|
||||
|
||||
// Offset coordinates by view position
|
||||
float[] coordinates = new float[]{location[0] + 1, location[1] + 1};
|
||||
|
||||
// Send down event, pause, and send up
|
||||
MotionEvent down = MotionEvents.sendDown(uiController, coordinates, new float[]{1f, 1f}).down;
|
||||
uiController.loopMainThreadForAtLeast(duration_ms);
|
||||
MotionEvents.sendUp(uiController, down, coordinates);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,19 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
|
||||
import static androidx.test.espresso.Espresso.onData;
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.Espresso.pressBack;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.scrollTo;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.anything;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Instrumentation;
|
||||
import android.content.Context;
|
||||
@@ -35,19 +48,6 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.services.TrackDeleteService;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onData;
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.Espresso.pressBack;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.scrollTo;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.anything;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class EspressoDeleteTrackTest {
|
||||
@@ -143,7 +143,7 @@ public class EspressoDeleteTrackTest {
|
||||
private static Matcher<View> childAtPosition(
|
||||
final Matcher<View> parentMatcher, final int position) {
|
||||
|
||||
return new TypeSafeMatcher<View>() {
|
||||
return new TypeSafeMatcher<>() {
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("Child at position " + position + " in parent ");
|
||||
@@ -160,13 +160,15 @@ public class EspressoDeleteTrackTest {
|
||||
}
|
||||
|
||||
public static Matcher<View> withListSize (final int size) {
|
||||
return new TypeSafeMatcher<View> () {
|
||||
@Override public boolean matchesSafely (final View view) {
|
||||
return ((ListView) view).getCount () == size;
|
||||
return new TypeSafeMatcher<>() {
|
||||
@Override
|
||||
public boolean matchesSafely(final View view) {
|
||||
return ((ListView) view).getCount() == size;
|
||||
}
|
||||
|
||||
@Override public void describeTo (final Description description) {
|
||||
description.appendText ("ListView should have " + size + " items");
|
||||
@Override
|
||||
public void describeTo(final Description description) {
|
||||
description.appendText("ListView should have " + size + " items");
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -234,7 +236,6 @@ public class EspressoDeleteTrackTest {
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
|
||||
import static androidx.test.espresso.action.ViewActions.replaceText;
|
||||
import static androidx.test.espresso.action.ViewActions.scrollTo;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
|
||||
import androidx.test.espresso.PerformException;
|
||||
import androidx.test.espresso.UiController;
|
||||
import androidx.test.espresso.ViewAction;
|
||||
import androidx.test.espresso.ViewInteraction;
|
||||
import androidx.test.espresso.action.MotionEvents;
|
||||
import androidx.test.espresso.util.HumanReadables;
|
||||
import androidx.test.espresso.util.TreeIterables;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.ActivityTestRule;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
import org.hamcrest.core.IsInstanceOf;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class EspressoEditTrackRecordingTest {
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<TrackListActivity> mActivityTestRule = new ActivityTestRule<>(TrackListActivity.class);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule =
|
||||
GrantPermissionRule.grant(
|
||||
"android.permission.ACCESS_FINE_LOCATION",
|
||||
"android.permission.ACCESS_COARSE_LOCATION");
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void espressoEditTrackRecordingTest() {
|
||||
{
|
||||
// TrackListActivity: start recording
|
||||
ViewInteraction trackControllerRecordButton = onView(withId(R.id.controller_record));
|
||||
trackControllerRecordButton.perform(click());
|
||||
}
|
||||
{
|
||||
// TrackRecordingActivity
|
||||
ViewInteraction trackControllerRecordButton = onView(withId(R.id.controller_record));
|
||||
ViewInteraction trackControllerStopButton = onView(withId(R.id.controller_stop));
|
||||
|
||||
// wait; stay recording
|
||||
trackControllerRecordButton.perform(waitFor(15000));
|
||||
|
||||
// open menu
|
||||
ViewInteraction overflowMenuButton = onView(
|
||||
allOf(withContentDescription("More options"),
|
||||
childAtPosition(
|
||||
childAtPosition(
|
||||
withId(R.id.toolbar),
|
||||
2),
|
||||
2),
|
||||
isDisplayed()));
|
||||
overflowMenuButton.perform(click());
|
||||
|
||||
// click on edit
|
||||
ViewInteraction appCompatTextView = onView(
|
||||
allOf(withId(R.id.title), withText("Edit"),
|
||||
childAtPosition(
|
||||
childAtPosition(
|
||||
withId(R.id.content),
|
||||
0),
|
||||
0),
|
||||
isDisplayed()));
|
||||
appCompatTextView.perform(click());
|
||||
|
||||
// change name for "New Name"
|
||||
ViewInteraction textInputEditText = onView(
|
||||
allOf(withId(R.id.track_edit_name),
|
||||
childAtPosition(
|
||||
childAtPosition(
|
||||
withClassName(is("com.google.android.material.textfield.TextInputLayout")),
|
||||
0),
|
||||
1)));
|
||||
textInputEditText.perform(scrollTo(), replaceText("New Name"));
|
||||
|
||||
ViewInteraction textInputEditText2 = onView(
|
||||
allOf(withId(R.id.track_edit_name), withText("New Name"),
|
||||
childAtPosition(
|
||||
childAtPosition(
|
||||
withClassName(is("com.google.android.material.textfield.TextInputLayout")),
|
||||
0),
|
||||
1),
|
||||
isDisplayed()));
|
||||
textInputEditText2.perform(closeSoftKeyboard());
|
||||
|
||||
// save edition
|
||||
ViewInteraction appCompatButton = onView(
|
||||
allOf(withId(R.id.track_edit_save),
|
||||
childAtPosition(
|
||||
childAtPosition(
|
||||
withClassName(is("android.widget.LinearLayout")),
|
||||
3),
|
||||
1),
|
||||
isDisplayed()));
|
||||
appCompatButton.perform(click());
|
||||
|
||||
//pause
|
||||
trackControllerRecordButton.perform(veryLongTouch(1600));
|
||||
|
||||
// wait; stay paused
|
||||
trackControllerRecordButton.perform(waitFor(1000));
|
||||
|
||||
// resume
|
||||
trackControllerRecordButton.perform(click());
|
||||
|
||||
// wait; stay recording
|
||||
trackControllerRecordButton.perform(waitFor(1000));
|
||||
|
||||
// stop;
|
||||
trackControllerStopButton.perform(veryLongTouch(1600));
|
||||
|
||||
// check track's name is "New Name"
|
||||
ViewInteraction textView = onView(
|
||||
allOf(withId(R.id.stats_name_value),
|
||||
withParent(withParent(IsInstanceOf.instanceOf(android.widget.ScrollView.class))),
|
||||
isDisplayed()));
|
||||
textView.check(matches(withText("New Name")));
|
||||
}
|
||||
}
|
||||
|
||||
private static Matcher<View> childAtPosition(
|
||||
final Matcher<View> parentMatcher, final int position) {
|
||||
|
||||
return new TypeSafeMatcher<>() {
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("Child at position " + position + " in parent ");
|
||||
parentMatcher.describeTo(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesSafely(View view) {
|
||||
ViewParent parent = view.getParent();
|
||||
return parent instanceof ViewGroup && parentMatcher.matches(parent)
|
||||
&& view.equals(((ViewGroup) parent).getChildAt(position));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static ViewAction waitId(final int viewId, final long millis) {
|
||||
return new ViewAction() {
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "wait for a specific view with id <" + viewId + "> during " + millis + " millis.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(final UiController uiController, final View view) {
|
||||
uiController.loopMainThreadUntilIdle();
|
||||
final long startTime = System.currentTimeMillis();
|
||||
final long endTime = startTime + millis;
|
||||
final Matcher<View> viewMatcher = withId(viewId);
|
||||
|
||||
do {
|
||||
for (View child : TreeIterables.breadthFirstViewTraversal(view)) {
|
||||
// found view with required ID
|
||||
if (viewMatcher.matches(child)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uiController.loopMainThreadForAtLeast(50);
|
||||
}
|
||||
while (System.currentTimeMillis() < endTime);
|
||||
|
||||
// timeout happens
|
||||
throw new PerformException.Builder()
|
||||
.withActionDescription(this.getDescription())
|
||||
.withViewDescription(HumanReadables.describe(view))
|
||||
.withCause(new TimeoutException())
|
||||
.build();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static ViewAction waitFor(final long duration_ms) {
|
||||
return new ViewAction() {
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Wait for milliseconds.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(UiController uiController, final View view) {
|
||||
uiController.loopMainThreadForAtLeast(duration_ms);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static ViewAction veryLongTouch(final int duration_ms) {
|
||||
return new ViewAction() {
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Perform long touch.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(UiController uiController, final View view) {
|
||||
// Get view absolute position
|
||||
int[] location = new int[2];
|
||||
view.getLocationOnScreen(location);
|
||||
|
||||
// Offset coordinates by view position
|
||||
float[] coordinates = new float[]{location[0] + 1, location[1] + 1};
|
||||
|
||||
// Send down event, pause, and send up
|
||||
MotionEvent down = MotionEvents.sendDown(uiController, coordinates, new float[]{1f, 1f}).down;
|
||||
uiController.loopMainThreadForAtLeast(duration_ms);
|
||||
MotionEvents.sendUp(uiController, down, coordinates);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,15 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
public class PreferenceMetricUnitRule implements TestRule {
|
||||
private final Context context;
|
||||
private final boolean metricUnits;
|
||||
|
||||
public PreferenceMetricUnitRule(@NonNull Context context, boolean metricUnits) {
|
||||
this.context = context;
|
||||
public PreferenceMetricUnitRule(boolean metricUnits) {
|
||||
this.metricUnits = metricUnits;
|
||||
}
|
||||
|
||||
@@ -25,14 +18,13 @@ public class PreferenceMetricUnitRule implements TestRule {
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
final boolean previousMetricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, context);
|
||||
final boolean previousMetricUnits = PreferencesUtils.isMetricUnits();
|
||||
|
||||
try {
|
||||
PreferencesUtils.setMetricUnits(sharedPreferences, context, metricUnits);
|
||||
PreferencesUtils.setMetricUnits(metricUnits);
|
||||
base.evaluate();
|
||||
} finally {
|
||||
PreferencesUtils.setMetricUnits(sharedPreferences, context, previousMetricUnits);
|
||||
PreferencesUtils.setMetricUnits(previousMetricUnits);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
public class TestApplication extends Startup {
|
||||
@Override
|
||||
public String getDatabaseName() {
|
||||
// null will make SQLiteOpenHelper create an in-memory database
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.runner.AndroidJUnitRunner;
|
||||
|
||||
public class TestRunner extends AndroidJUnitRunner {
|
||||
@Override
|
||||
public Application newApplication(ClassLoader cl, String className, Context context) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||
return super.newApplication(cl, TestApplication.class.getName(), context);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
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;
|
||||
@@ -7,15 +10,13 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.Altitude;
|
||||
import de.dennisguse.opentracks.content.data.Distance;
|
||||
import de.dennisguse.opentracks.content.data.Speed;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.fragments.TrackStubUtils;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ChartPointTest {
|
||||
|
||||
@@ -26,7 +27,7 @@ public class ChartPointTest {
|
||||
statistics.setTotalTime(Duration.ofSeconds(1000));
|
||||
|
||||
// when
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), 0, false, false);
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), Altitude.EGM2008.of(0), false, false);
|
||||
|
||||
// then
|
||||
assertEquals(1000000, (long) point.getTimeOrDistance());
|
||||
@@ -39,7 +40,7 @@ public class ChartPointTest {
|
||||
statistics.setTotalDistance(Distance.of(1000));
|
||||
|
||||
// when
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), 0, true, true);
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), Altitude.EGM2008.of(0), true, true);
|
||||
|
||||
// then
|
||||
assertEquals(1, (long) point.getTimeOrDistance());
|
||||
@@ -51,7 +52,7 @@ public class ChartPointTest {
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
|
||||
// when
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), 50, false, true);
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), Altitude.EGM2008.of(50), false, true);
|
||||
|
||||
// then
|
||||
assertEquals(50, point.getAltitude(), 0.01);
|
||||
@@ -65,7 +66,7 @@ public class ChartPointTest {
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
|
||||
// when
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), 50, false, true);
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), Altitude.EGM2008.of(50), false, true);
|
||||
|
||||
// then
|
||||
assertNull(point.getHeartRate());
|
||||
@@ -78,13 +79,13 @@ public class ChartPointTest {
|
||||
// given
|
||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
|
||||
trackPoint.setHeartRate_bpm(100f);
|
||||
trackPoint.setCyclingCadence_rpm(101f);
|
||||
trackPoint.setCadence_rpm(101f);
|
||||
trackPoint.setPower(102f);
|
||||
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
|
||||
// when
|
||||
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), 50, false, true);
|
||||
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), Altitude.EGM2008.of(50), false, true);
|
||||
|
||||
// then
|
||||
assertEquals(100.0, point.getHeartRate(), 0.01);
|
||||
|
||||
@@ -27,6 +27,8 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Distance;
|
||||
@@ -91,7 +93,7 @@ public class DescriptionGeneratorTest {
|
||||
+ "Min elevation: -500 m (-1640 ft)<br>"
|
||||
+ "Elevation gain: 6000 m (19685 ft)<br>"
|
||||
+ "Elevation loss: 6000 m (19685 ft)<br>"
|
||||
+ "Recorded: " + StringUtils.formatDateTime(context, START_TIME) + "<br>";
|
||||
+ "Recorded: " + StringUtils.formatDateTime(OffsetDateTime.ofInstant(START_TIME, ZoneId.systemDefault())) + "<br>";
|
||||
|
||||
assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
|
||||
}
|
||||
@@ -127,7 +129,7 @@ public class DescriptionGeneratorTest {
|
||||
+ "Fastest pace: 0:10 min/km (0:16 min/mi)<br>"
|
||||
+ "Elevation gain: 6000 m (19685 ft)<br>"
|
||||
+ "Elevation loss: 6000 m (19685 ft)<br>"
|
||||
+ "Recorded: " + StringUtils.formatDateTime(context, START_TIME) + "<br>";
|
||||
+ "Recorded: " + StringUtils.formatDateTime(OffsetDateTime.ofInstant(START_TIME, ZoneId.systemDefault())) + "<br>";
|
||||
|
||||
assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
package de.dennisguse.opentracks.content.data;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class LayoutTest extends TestCase {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private final Resources resources = ApplicationProvider.getApplicationContext().getResources();
|
||||
|
||||
@Test
|
||||
public void testFromCsv() {
|
||||
// given a csv line
|
||||
String csv = "running;2;" + context.getString(R.string.stats_custom_layout_speed_key) + ",1,1,0;" + context.getString(R.string.stats_custom_layout_distance_key) + ",1,0,0;" + context.getString(R.string.stats_custom_layout_altitude_key) + ",0,1,0;" + context.getString(R.string.stats_custom_layout_gain_key) + ",0,0,0;";
|
||||
|
||||
// when create a layout from CSV line
|
||||
Layout layout = Layout.fromCsv(csv, resources);
|
||||
List<DataField> dataFieldList = layout.getFields();
|
||||
|
||||
// then layout and data fields are built correctly
|
||||
assertEquals(layout.getName(), "running");
|
||||
assertEquals(layout.getColumnsPerRow(), 2);
|
||||
assertEquals(dataFieldList.size(), 4);
|
||||
assertEquals(dataFieldList.get(0).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
||||
assertEquals(dataFieldList.get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
||||
assertEquals(dataFieldList.get(2).getKey(), context.getString(R.string.stats_custom_layout_altitude_key));
|
||||
assertEquals(dataFieldList.get(3).getKey(), context.getString(R.string.stats_custom_layout_gain_key));
|
||||
assertEquals(dataFieldList.get(0).getTitle(), context.getString(R.string.stats_speed));
|
||||
assertEquals(dataFieldList.get(1).getTitle(), context.getString(R.string.stats_distance));
|
||||
assertEquals(dataFieldList.get(2).getTitle(), context.getString(R.string.stats_altitude));
|
||||
assertEquals(dataFieldList.get(3).getTitle(), context.getString(R.string.stats_gain));
|
||||
assertTrue(dataFieldList.get(0).isVisible());
|
||||
assertTrue(dataFieldList.get(0).isPrimary());
|
||||
assertFalse(dataFieldList.get(0).isWide());
|
||||
assertTrue(dataFieldList.get(1).isVisible());
|
||||
assertFalse(dataFieldList.get(1).isPrimary());
|
||||
assertFalse(dataFieldList.get(1).isWide());
|
||||
assertFalse(dataFieldList.get(2).isVisible());
|
||||
assertTrue(dataFieldList.get(2).isPrimary());
|
||||
assertFalse(dataFieldList.get(2).isWide());
|
||||
assertFalse(dataFieldList.get(3).isVisible());
|
||||
assertFalse(dataFieldList.get(3).isPrimary());
|
||||
assertFalse(dataFieldList.get(3).isWide());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromCsv_Wrong1() {
|
||||
// given a csv line without number of columns
|
||||
String csv1 = "Layout Name;speed,1,1;distance,0,0;";
|
||||
|
||||
// when create a layout from CSV line
|
||||
Layout layout = Layout.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = layout.getFields();
|
||||
|
||||
// then layout and data fields are built correctly
|
||||
assertEquals(layout.getName(), PreferencesUtils.getDefaultLayoutName());
|
||||
assertEquals(layout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
||||
assertEquals(layout.getFields().size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromCsv_Wrong2() {
|
||||
// given a csv line without any field
|
||||
String csv1 = "Layout Name;2;";
|
||||
|
||||
// when create a layout from CSV line
|
||||
Layout layout = Layout.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = layout.getFields();
|
||||
|
||||
// then layout and data fields are built correctly
|
||||
assertEquals(layout.getName(), PreferencesUtils.getDefaultLayoutName());
|
||||
assertEquals(layout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
||||
assertEquals(layout.getFields().size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromCsv_Wrong3() {
|
||||
// given a csv line with wrong fields description
|
||||
String csv1 = "Layout Name;2;speed,distance,total time;";
|
||||
|
||||
// when create a layout from CSV line
|
||||
Layout layout = Layout.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = layout.getFields();
|
||||
|
||||
// then layout and data fields are built correctly
|
||||
assertEquals(layout.getName(), "Layout Name");
|
||||
assertEquals(layout.getColumnsPerRow(), 2);
|
||||
assertEquals(layout.getFields().size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromCsv_Wrong4() {
|
||||
// given a csv line with the first field ok but not the others
|
||||
String csv1 = "Layout Name;2;speed,1,0;distance;";
|
||||
|
||||
// when create a layout from CSV line
|
||||
Layout layout = Layout.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = layout.getFields();
|
||||
|
||||
// then layout and data fields are built correctly
|
||||
assertEquals(layout.getName(), "Layout Name");
|
||||
assertEquals(layout.getColumnsPerRow(), 2);
|
||||
assertEquals(layout.getFields().size(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToCsv() {
|
||||
// given a layout's object
|
||||
Layout layout = new Layout("Test Layout", 2);
|
||||
layout.addField("key1", "Title 1", false, false, false);
|
||||
layout.addField("key2", "Title 2", false, true, false);
|
||||
layout.addField("key3", "Title 3", true, false, false);
|
||||
layout.addField("key4", "Title 4", true, true, false);
|
||||
layout.addField("key5", "Title 5", true, true, true);
|
||||
|
||||
// when converts it to CSV
|
||||
String csv = layout.toCsv();
|
||||
|
||||
// then csv is well built
|
||||
assertEquals(csv, "Test Layout;2;key1,0,0,0;key2,0,1,0;key3,1,0,0;key4,1,1,0;key5,1,1,1;");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToCsv_columnsByDefault() {
|
||||
// given a layout's object
|
||||
Layout layout = new Layout("Test Layout");
|
||||
layout.addField("key1", "Title 1", false, false, false);
|
||||
layout.addField("key2", "Title 2", false, true, false);
|
||||
layout.addField("key3", "Title 3", true, false, false);
|
||||
layout.addField("key4", "Title 4", true, true, false);
|
||||
layout.addField("key5", "Title 5", true, true, true);
|
||||
|
||||
// when converts it to CSV
|
||||
String csv = layout.toCsv();
|
||||
|
||||
// then csv is well built
|
||||
assertEquals(csv, "Test Layout;" + PreferencesUtils.getLayoutColumnsByDefault() + ";key1,0,0,0;key2,0,1,0;key3,1,0,0;key4,1,1,0;key5,1,1,1;");
|
||||
}
|
||||
}
|
||||
@@ -112,16 +112,15 @@ public class TestDataUtil {
|
||||
}
|
||||
|
||||
public static TrackPoint createTrackPoint(int i) {
|
||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT);
|
||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(i + 1));
|
||||
trackPoint.setLatitude(INITIAL_LATITUDE + (double) i / 10000.0);
|
||||
trackPoint.setLongitude(INITIAL_LONGITUDE - (double) i / 10000.0);
|
||||
trackPoint.setAccuracy((float) i / 100.0f);
|
||||
trackPoint.setHorizontalAccuracy(Distance.of(i / 100.0f));
|
||||
trackPoint.setAltitude(i * ALTITUDE_INTERVAL);
|
||||
trackPoint.setTime(Instant.ofEpochSecond(i + 1));
|
||||
trackPoint.setSpeed(Speed.of(5f + (i / 10f)));
|
||||
|
||||
trackPoint.setHeartRate_bpm(100f + i);
|
||||
trackPoint.setCyclingCadence_rpm(300f + i);
|
||||
trackPoint.setCadence_rpm(300f + i);
|
||||
trackPoint.setPower(400f + i);
|
||||
trackPoint.setAltitudeGain(ALTITUDE_GAIN);
|
||||
trackPoint.setAltitudeLoss(ALTITUDE_LOSS);
|
||||
@@ -160,12 +159,13 @@ public class TestDataUtil {
|
||||
}
|
||||
|
||||
public static List<TrackPoint> getTrackPoints(ContentProviderUtils contentProviderUtils, Track.Id trackId) {
|
||||
TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null);
|
||||
ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
||||
while (trackPointIterator.hasNext()) {
|
||||
trackPoints.add(trackPointIterator.next());
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
||||
while (trackPointIterator.hasNext()) {
|
||||
trackPoints.add(trackPointIterator.next());
|
||||
}
|
||||
return trackPoints;
|
||||
}
|
||||
return trackPoints;
|
||||
}
|
||||
|
||||
public static Pair<Track.Id, TrackStatistics> buildTrackWithTrackPoints(ContentProviderUtils contentProviderUtils, int numberOfPoints) {
|
||||
|
||||
@@ -12,16 +12,15 @@ public class TestSensorDataUtil {
|
||||
|
||||
public void add(Instant time, Float hr, Float cadence, Float power, TrackPoint.Type type) {
|
||||
sensorDataList.add(new TestSensorDataUtil.SensorData(time, hr, cadence, power, type));
|
||||
TrackPoint tp = new TrackPoint(type);
|
||||
TrackPoint tp = new TrackPoint(type, time);
|
||||
int i = trackPointList.size() + 1;
|
||||
tp.setLatitude(TestDataUtil.INITIAL_LATITUDE + (double) i / 10000.0);
|
||||
tp.setLongitude(TestDataUtil.INITIAL_LONGITUDE - (double) i / 10000.0);
|
||||
tp.setHeartRate_bpm(hr);
|
||||
tp.setCyclingCadence_rpm(cadence);
|
||||
tp.setCadence_rpm(cadence);
|
||||
tp.setPower(power);
|
||||
tp.setAccuracy(1f);
|
||||
tp.setHorizontalAccuracy(Distance.of(1f));
|
||||
tp.setAltitude(1f);
|
||||
tp.setTime(time);
|
||||
tp.setSpeed(Speed.of(5f + (i / 10f)));
|
||||
tp.setAltitudeGain(3f);
|
||||
tp.setAltitudeLoss(3f);
|
||||
|
||||
@@ -13,27 +13,25 @@ public class TrackPointTest {
|
||||
|
||||
@Test
|
||||
public void isRecent_true() {
|
||||
TrackPoint tp = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL);
|
||||
tp.setTime(Instant.now());
|
||||
TrackPoint tp = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.now());
|
||||
|
||||
assertTrue(tp.isRecent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isRecent_false() {
|
||||
TrackPoint tp = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL);
|
||||
tp.setTime(Instant.now().minus(2, ChronoUnit.MINUTES));
|
||||
TrackPoint tp = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.now().minus(2, ChronoUnit.MINUTES));
|
||||
|
||||
assertFalse(tp.isRecent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void distanceToPrevious() {
|
||||
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochMilli(0))
|
||||
.setLatitude(0)
|
||||
.setLongitude(0.0001);
|
||||
|
||||
TrackPoint tp2 = new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
TrackPoint tp2 = new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochMilli(1))
|
||||
.setLatitude(0)
|
||||
.setLongitude(0.0002);
|
||||
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
package de.dennisguse.opentracks.content.data;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.content.provider.SelectionData;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TrackSelectionTest extends TestCase {
|
||||
@Test
|
||||
public void testFilterBuildSelection_empty() {
|
||||
// given
|
||||
TrackSelection filter = new TrackSelection();
|
||||
|
||||
// when
|
||||
SelectionData selection = filter.buildSelection();
|
||||
|
||||
// Then
|
||||
assertNull(selection.getSelection());
|
||||
assertNull(selection.getSelectionArgs());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterBuildSelection_onlyOneTrackId() {
|
||||
// given
|
||||
Track.Id trackId = new Track.Id(1);
|
||||
TrackSelection filter = new TrackSelection().addTrackId(trackId);
|
||||
|
||||
// when
|
||||
SelectionData selection = filter.buildSelection();
|
||||
|
||||
// Then
|
||||
assertEquals(selection.getSelection(), "_id IN (?)");
|
||||
assertEquals(selection.getSelectionArgs().length, 1);
|
||||
assertEquals(selection.getSelectionArgs()[0], "1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterBuildSelection_severalTracksId() {
|
||||
// given
|
||||
Track.Id trackId1 = new Track.Id(1);
|
||||
Track.Id trackId2 = new Track.Id(2);
|
||||
Track.Id trackId3 = new Track.Id(3);
|
||||
TrackSelection filter = new TrackSelection()
|
||||
.addTrackId(trackId1)
|
||||
.addTrackId(trackId2)
|
||||
.addTrackId(trackId3);
|
||||
|
||||
// when
|
||||
SelectionData selection = filter.buildSelection();
|
||||
|
||||
// Then
|
||||
assertEquals(selection.getSelection(), "_id IN (?,?,?)");
|
||||
assertEquals(selection.getSelectionArgs().length, 3);
|
||||
assertEquals(selection.getSelectionArgs()[0], "1");
|
||||
assertEquals(selection.getSelectionArgs()[1], "2");
|
||||
assertEquals(selection.getSelectionArgs()[2], "3");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterBuildSelection_onlyOneCategory() {
|
||||
// given
|
||||
TrackSelection filter = new TrackSelection().addCategory("running");
|
||||
|
||||
// when
|
||||
SelectionData selection = filter.buildSelection();
|
||||
|
||||
// Then
|
||||
assertEquals(selection.getSelection(), "category IN (?)");
|
||||
assertEquals(selection.getSelectionArgs().length, 1);
|
||||
assertEquals(selection.getSelectionArgs()[0], "running");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterBuildSelection_severalCategories() {
|
||||
// given
|
||||
TrackSelection filter = new TrackSelection()
|
||||
.addCategory("running")
|
||||
.addCategory("road biking")
|
||||
.addCategory("mountain biking")
|
||||
.addCategory("trail walking");
|
||||
|
||||
// when
|
||||
SelectionData selection = filter.buildSelection();
|
||||
|
||||
// Then
|
||||
assertEquals(selection.getSelection(), "category IN (?,?,?,?)");
|
||||
assertEquals(selection.getSelectionArgs().length, 4);
|
||||
assertEquals(selection.getSelectionArgs()[0], "running");
|
||||
assertEquals(selection.getSelectionArgs()[1], "road biking");
|
||||
assertEquals(selection.getSelectionArgs()[2], "mountain biking");
|
||||
assertEquals(selection.getSelectionArgs()[3], "trail walking");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterBuildSelection_onlyDateRange() {
|
||||
// given
|
||||
Instant instant = Instant.now();
|
||||
long oneDay = 24 * 60 * 60 * 1000;
|
||||
TrackSelection filterWrong1 = new TrackSelection().addDateRange(instant, null);
|
||||
TrackSelection filterWrong2 = new TrackSelection().addDateRange(null, instant);
|
||||
TrackSelection filterOk = new TrackSelection().addDateRange(instant, instant.plusMillis(oneDay));
|
||||
|
||||
// when
|
||||
SelectionData selectionWrong1 = filterWrong1.buildSelection();
|
||||
SelectionData selectionWrong2 = filterWrong2.buildSelection();
|
||||
SelectionData selectionOk = filterOk.buildSelection();
|
||||
|
||||
// Then
|
||||
assertNull(selectionWrong1.getSelection());
|
||||
assertNull(selectionWrong1.getSelectionArgs());
|
||||
|
||||
assertNull(selectionWrong2.getSelection());
|
||||
assertNull(selectionWrong2.getSelectionArgs());
|
||||
|
||||
assertEquals(selectionOk.getSelection(), "starttime BETWEEN ? AND ?");
|
||||
assertEquals(selectionOk.getSelectionArgs().length, 2);
|
||||
assertEquals(selectionOk.getSelectionArgs()[0], Long.toString(instant.toEpochMilli()));
|
||||
assertEquals(selectionOk.getSelectionArgs()[1], Long.toString(instant.toEpochMilli() + oneDay));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterBuildSelection_tracksId_and_categories() {
|
||||
// given
|
||||
Track.Id trackId1 = new Track.Id(1);
|
||||
Track.Id trackId2 = new Track.Id(2);
|
||||
Track.Id trackId3 = new Track.Id(3);
|
||||
TrackSelection filter = new TrackSelection()
|
||||
.addTrackId(trackId1)
|
||||
.addTrackId(trackId2)
|
||||
.addTrackId(trackId3)
|
||||
.addCategory("running")
|
||||
.addCategory("road biking");
|
||||
|
||||
// when
|
||||
SelectionData selection = filter.buildSelection();
|
||||
|
||||
// Then
|
||||
assertEquals(selection.getSelection(), "_id IN (?,?,?) AND category IN (?,?)");
|
||||
assertEquals(selection.getSelectionArgs().length, 5);
|
||||
assertEquals(selection.getSelectionArgs()[0], "1");
|
||||
assertEquals(selection.getSelectionArgs()[1], "2");
|
||||
assertEquals(selection.getSelectionArgs()[2], "3");
|
||||
assertEquals(selection.getSelectionArgs()[3], "running");
|
||||
assertEquals(selection.getSelectionArgs()[4], "road biking");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterBuildSelection_tracksId_and_dateRange() {
|
||||
// given
|
||||
Instant instant = Instant.now();
|
||||
long oneDay = 24 * 60 * 60 * 1000;
|
||||
|
||||
Track.Id trackId1 = new Track.Id(1);
|
||||
Track.Id trackId2 = new Track.Id(2);
|
||||
Track.Id trackId3 = new Track.Id(3);
|
||||
|
||||
TrackSelection filter = new TrackSelection()
|
||||
.addTrackId(trackId1)
|
||||
.addTrackId(trackId2)
|
||||
.addTrackId(trackId3)
|
||||
.addDateRange(instant, instant.plusMillis(oneDay));
|
||||
|
||||
// when
|
||||
SelectionData selection = filter.buildSelection();
|
||||
|
||||
// Then
|
||||
assertEquals(selection.getSelection(), "_id IN (?,?,?) AND starttime BETWEEN ? AND ?");
|
||||
assertEquals(selection.getSelectionArgs().length, 5);
|
||||
assertEquals(selection.getSelectionArgs()[0], "1");
|
||||
assertEquals(selection.getSelectionArgs()[1], "2");
|
||||
assertEquals(selection.getSelectionArgs()[2], "3");
|
||||
assertEquals(selection.getSelectionArgs()[3], Long.toString(instant.toEpochMilli()));
|
||||
assertEquals(selection.getSelectionArgs()[4], Long.toString(instant.toEpochMilli() + oneDay));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterBuildSelection_categories_and_dateRange() {
|
||||
// given
|
||||
Instant instant = Instant.now();
|
||||
long oneDay = 24 * 60 * 60 * 1000;
|
||||
|
||||
TrackSelection filter = new TrackSelection()
|
||||
.addCategory("running")
|
||||
.addCategory("road biking")
|
||||
.addDateRange(instant, instant.plusMillis(oneDay));
|
||||
|
||||
// when
|
||||
SelectionData selection = filter.buildSelection();
|
||||
|
||||
// Then
|
||||
assertEquals(selection.getSelection(), "category IN (?,?) AND starttime BETWEEN ? AND ?");
|
||||
assertEquals(selection.getSelectionArgs().length, 4);
|
||||
assertEquals(selection.getSelectionArgs()[0], "running");
|
||||
assertEquals(selection.getSelectionArgs()[1], "road biking");
|
||||
assertEquals(selection.getSelectionArgs()[2], Long.toString(instant.toEpochMilli()));
|
||||
assertEquals(selection.getSelectionArgs()[3], Long.toString(instant.toEpochMilli() + oneDay));
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,13 @@
|
||||
*/
|
||||
package de.dennisguse.opentracks.content.provider;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
@@ -57,13 +64,6 @@ import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
import de.dennisguse.opentracks.util.UUIDUtils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* A unit test for {@link ContentProviderUtils}.
|
||||
*
|
||||
@@ -135,10 +135,10 @@ public class CustomContentProviderUtilsTest {
|
||||
|
||||
List<TrackPoint> trackPoints = new ArrayList<>(numPoints);
|
||||
for (int i = 0; i < numPoints; ++i) {
|
||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochMilli(i))
|
||||
.setLatitude(37.0 + (double) i / 10000.0)
|
||||
.setLongitude(57.0 - (double) i / 10000.0)
|
||||
.setAccuracy((float) i / 100.0f)
|
||||
.setHorizontalAccuracy(Distance.of(i / 100.0f))
|
||||
.setAltitude(i * 2.5);
|
||||
trackPoints.add(trackPoint);
|
||||
}
|
||||
@@ -192,6 +192,19 @@ public class CustomContentProviderUtilsTest {
|
||||
assertEquals(name, track.getName());
|
||||
}
|
||||
|
||||
private void assertCount(int trackCount, int trackPointCount, int markerCount) {
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
try (Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID)) {
|
||||
assertEquals(trackCount, tracksCursor.getCount());
|
||||
}
|
||||
try (Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID)) {
|
||||
assertEquals(trackPointCount, tracksPointsCursor.getCount());
|
||||
}
|
||||
try (Cursor markerCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID)) {
|
||||
assertEquals(markerCount, markerCursor.getCount());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtils#deleteAllTracks(Context)}
|
||||
*/
|
||||
@@ -204,22 +217,13 @@ public class CustomContentProviderUtilsTest {
|
||||
Marker marker = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
contentProviderUtils.insertMarker(marker);
|
||||
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||
assertEquals(1, tracksCursor.getCount());
|
||||
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
||||
assertEquals(10, tracksPointsCursor.getCount());
|
||||
Cursor markerCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
||||
assertEquals(1, markerCursor.getCount());
|
||||
// Delete all.
|
||||
assertCount(1, 10, 1);
|
||||
|
||||
// when
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
// Check whether all have been deleted.
|
||||
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||
assertEquals(0, tracksCursor.getCount());
|
||||
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
||||
assertEquals(0, tracksPointsCursor.getCount());
|
||||
markerCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
||||
assertEquals(0, markerCursor.getCount());
|
||||
|
||||
// then
|
||||
assertCount(0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -235,28 +239,18 @@ public class CustomContentProviderUtilsTest {
|
||||
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
||||
contentProviderUtils.insertMarker(marker);
|
||||
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||
assertEquals(1, tracksCursor.getCount());
|
||||
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
||||
assertEquals(10, tracksPointsCursor.getCount());
|
||||
Cursor markerCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
||||
assertEquals(1, markerCursor.getCount());
|
||||
// Check marker has photo and it's in the external storage.
|
||||
assertCount(1, 10, 1);
|
||||
assertTrue(marker.hasPhoto());
|
||||
File dir = FileUtils.getPhotoDir(context, trackId);
|
||||
assertTrue(dir.isDirectory());
|
||||
assertEquals(1, dir.list().length);
|
||||
assertTrue(dir.exists());
|
||||
// Delete all.
|
||||
|
||||
// when
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
// Check whether all have been deleted.
|
||||
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||
assertEquals(0, tracksCursor.getCount());
|
||||
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
||||
assertEquals(0, tracksPointsCursor.getCount());
|
||||
markerCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
||||
assertEquals(0, markerCursor.getCount());
|
||||
|
||||
// then
|
||||
assertCount(0, 0, 0);
|
||||
assertFalse(dir.exists());
|
||||
}
|
||||
|
||||
@@ -279,22 +273,13 @@ public class CustomContentProviderUtilsTest {
|
||||
Marker marker = new Marker(trackId1, contentProviderUtils.getLastValidTrackPoint(trackId2));
|
||||
contentProviderUtils.insertMarker(marker);
|
||||
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||
assertEquals(3, tracksCursor.getCount());
|
||||
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
||||
assertEquals(30, tracksPointsCursor.getCount());
|
||||
Cursor markerCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
||||
assertEquals(1, markerCursor.getCount());
|
||||
// Delete one track.
|
||||
assertCount(3, 30, 1);
|
||||
|
||||
// when
|
||||
contentProviderUtils.deleteTrack(context, trackId1);
|
||||
// Check whether all data of a track has been deleted.
|
||||
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||
assertEquals(2, tracksCursor.getCount());
|
||||
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
||||
assertEquals(20, tracksPointsCursor.getCount());
|
||||
markerCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
||||
assertEquals(0, markerCursor.getCount());
|
||||
|
||||
// then
|
||||
assertCount(2, 20, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -324,13 +309,7 @@ public class CustomContentProviderUtilsTest {
|
||||
File dir2 = FileUtils.getPhotoDir(context, trackId2);
|
||||
|
||||
// Check.
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||
assertEquals(3, tracksCursor.getCount());
|
||||
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
||||
assertEquals(30, tracksPointsCursor.getCount());
|
||||
Cursor markerCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
||||
assertEquals(2, markerCursor.getCount());
|
||||
assertCount(3, 30, 2);
|
||||
assertTrue(marker1.hasPhoto());
|
||||
assertTrue(dir1.isDirectory());
|
||||
assertEquals(1, dir1.list().length);
|
||||
@@ -338,15 +317,12 @@ public class CustomContentProviderUtilsTest {
|
||||
assertTrue(dir2.isDirectory());
|
||||
assertEquals(1, dir2.list().length);
|
||||
assertTrue(dir2.exists());
|
||||
// Delete one track.
|
||||
|
||||
// when
|
||||
contentProviderUtils.deleteTrack(context, trackId1);
|
||||
// Check whether all data of a track has been deleted.
|
||||
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||
assertEquals(2, tracksCursor.getCount());
|
||||
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
||||
assertEquals(20, tracksPointsCursor.getCount());
|
||||
markerCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
||||
assertEquals(1, markerCursor.getCount());
|
||||
|
||||
// then
|
||||
assertCount(2, 20, 1);
|
||||
assertFalse(dir1.exists());
|
||||
assertTrue(dir2.exists());
|
||||
}
|
||||
@@ -835,7 +811,7 @@ public class CustomContentProviderUtilsTest {
|
||||
|
||||
TrackPoint trackPoint = TestDataUtil.createTrackPoint(5);
|
||||
trackPoint.setHeartRate_bpm(1F);
|
||||
trackPoint.setCyclingCadence_rpm(2F);
|
||||
trackPoint.setCadence_rpm(2F);
|
||||
trackPoint.setPower(3F);
|
||||
|
||||
// when
|
||||
@@ -845,7 +821,7 @@ public class CustomContentProviderUtilsTest {
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
assertTrue(trackPoints.get(10).hasHeartRate());
|
||||
assertEquals(trackPoint.getHeartRate_bpm(), trackPoints.get(10).getHeartRate_bpm(), 0.01);
|
||||
assertEquals(trackPoint.getCyclingCadence_rpm(), trackPoints.get(10).getCyclingCadence_rpm(), 0.01);
|
||||
assertEquals(trackPoint.getCadence_rpm(), trackPoints.get(10).getCadence_rpm(), 0.01);
|
||||
assertEquals(trackPoint.getPower(), trackPoints.get(10).getPower(), 0.01);
|
||||
}
|
||||
|
||||
@@ -934,7 +910,7 @@ public class CustomContentProviderUtilsTest {
|
||||
TrackPoint trackPoint = TestDataUtil.createTrackPoint(1);
|
||||
trackPoint.setType(TrackPoint.Type.TRACKPOINT);
|
||||
trackPoint.setPower(null);
|
||||
trackPoint.setCyclingCadence_rpm(null);
|
||||
trackPoint.setCadence_rpm(null);
|
||||
trackPoint.setHeartRate_bpm(null);
|
||||
trackPointList.add(trackPoint);
|
||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||
|
||||
@@ -2,6 +2,7 @@ package de.dennisguse.opentracks.content.sensor;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -11,6 +12,7 @@ import de.dennisguse.opentracks.util.UintUtils;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SensorDataCyclingTest {
|
||||
@@ -81,7 +83,9 @@ public class SensorDataCyclingTest {
|
||||
assertEquals(60, current.getValue(), 0.01);
|
||||
}
|
||||
|
||||
@Ignore("Disabled from #953")
|
||||
@Test
|
||||
@Deprecated
|
||||
public void compute_cadence_rollOverCount() {
|
||||
// given
|
||||
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
|
||||
@@ -94,6 +98,19 @@ public class SensorDataCyclingTest {
|
||||
assertEquals(60, current.getValue(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compute_cadence_overflow() {
|
||||
// given
|
||||
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
|
||||
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 0, 2048);
|
||||
|
||||
// when
|
||||
current.compute(previous);
|
||||
|
||||
// then
|
||||
assertNull(current.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compute_speed() {
|
||||
// given
|
||||
@@ -104,11 +121,13 @@ public class SensorDataCyclingTest {
|
||||
current.compute(previous, Distance.ofMM(2150));
|
||||
|
||||
// then
|
||||
assertEquals(2.15, current.getValue().distance.toM(), 0.01);
|
||||
assertEquals(1.20, current.getValue().speed.toMPS(), 0.01);
|
||||
assertEquals(2.15, current.getValue().getDistance().toM(), 0.01);
|
||||
assertEquals(1.20, current.getValue().getSpeed().toMPS(), 0.01);
|
||||
}
|
||||
|
||||
@Ignore("Disabled from #953")
|
||||
@Test
|
||||
@Deprecated
|
||||
public void compute_speed_rollOverCount() {
|
||||
// given
|
||||
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
|
||||
@@ -118,8 +137,21 @@ public class SensorDataCyclingTest {
|
||||
current.compute(previous, Distance.ofMM(2000));
|
||||
|
||||
// then
|
||||
assertEquals(2, current.getValue().distance.toM(), 0.01);
|
||||
assertEquals(2, current.getValue().speed.toMPS(), 0.01);
|
||||
assertEquals(2, current.getValue().getDistance().toM(), 0.01);
|
||||
assertEquals(2, current.getValue().getSpeed().toMPS(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compute_speed_overflow() {
|
||||
// given
|
||||
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
|
||||
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048);
|
||||
|
||||
// when
|
||||
current.compute(previous, Distance.ofMM(2000));
|
||||
|
||||
// then
|
||||
assertNull(current.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -18,6 +18,7 @@ package de.dennisguse.opentracks.fragments;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.Distance;
|
||||
import de.dennisguse.opentracks.content.data.Speed;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
|
||||
@@ -32,7 +33,7 @@ public class TrackStubUtils {
|
||||
public static final long INITIAL_TIME = 1000L;
|
||||
private static final double INITIAL_LATITUDE = 22;
|
||||
private static final double INITIAL_LONGITUDE = 22;
|
||||
private static final float INITIAL_ACCURACY = 5;
|
||||
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;
|
||||
|
||||
@@ -55,7 +56,7 @@ public class TrackStubUtils {
|
||||
.setLatitude(latitude)
|
||||
.setLongitude(longitude)
|
||||
.setAltitude(altitude)
|
||||
.setAccuracy(INITIAL_ACCURACY)
|
||||
.setHorizontalAccuracy(INITIAL_ACCURACY)
|
||||
.setSpeed(INITIAL_SPEED)
|
||||
.setBearing(INITIAL_BEARING);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package de.dennisguse.opentracks.io.file.exporter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
@@ -10,12 +12,11 @@ import org.junit.runners.JUnit4;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public class KmlTrackExporterTest {
|
||||
|
||||
@@ -27,6 +28,8 @@ public class KmlTrackExporterTest {
|
||||
@Test
|
||||
public void writeCloseSegment_only_write_sensordata_if_present() {
|
||||
String expected = "<when>1970-01-01T00:00:00Z</when>\n" +
|
||||
"<gx:coord/>\n" +
|
||||
"<when>1970-01-01T01:00:00+01:00</when>\n" +
|
||||
"<gx:coord/>\n" +
|
||||
"<ExtendedData>\n" +
|
||||
"<SchemaData schemaUrl=\"#schema\">\n" +
|
||||
@@ -41,7 +44,8 @@ public class KmlTrackExporterTest {
|
||||
KMLTrackExporter kmlTrackWriter = (KMLTrackExporter) TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
||||
kmlTrackWriter.prepare(outputStream);
|
||||
|
||||
kmlTrackWriter.writeTrackPoint(trackPoint);
|
||||
kmlTrackWriter.writeTrackPoint(ZoneOffset.UTC, trackPoint);
|
||||
kmlTrackWriter.writeTrackPoint(ZoneOffset.ofTotalSeconds(3600), trackPoint);
|
||||
|
||||
// when
|
||||
kmlTrackWriter.writeCloseSegment();
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
package de.dennisguse.opentracks.io.file.importer;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@@ -19,19 +25,19 @@ import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Altitude;
|
||||
import de.dennisguse.opentracks.content.data.Distance;
|
||||
import de.dennisguse.opentracks.content.data.Marker;
|
||||
import de.dennisguse.opentracks.content.data.Speed;
|
||||
@@ -39,25 +45,25 @@ import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataCycling;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataCyclingPower;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataHeartRate;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceTestUtils;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.services.sensors.AltitudeSumManager;
|
||||
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Export a track to {@link TrackFileFormat} and verify that the import is identical.
|
||||
* <p>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExportImportTest {
|
||||
|
||||
private static final String TAG = ExportImportTest.class.getSimpleName();
|
||||
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
@@ -81,7 +87,6 @@ public class ExportImportTest {
|
||||
private File tmpFile;
|
||||
private Uri tmpFileUri;
|
||||
|
||||
private Track track;
|
||||
private List<Marker> markers = new ArrayList<>();
|
||||
private List<TrackPoint> trackPoints = new ArrayList<>();
|
||||
|
||||
@@ -91,43 +96,71 @@ public class ExportImportTest {
|
||||
private TrackImporter trackImporter;
|
||||
|
||||
@Before
|
||||
public void fileSetup() throws IOException {
|
||||
public void fileSetup() throws IOException, TimeoutException {
|
||||
TrackRecordingServiceTestUtils.resetService(mServiceRule, context);
|
||||
|
||||
tmpFile = File.createTempFile("test", "test", context.getFilesDir());
|
||||
tmpFileUri = Uri.fromFile(tmpFile);
|
||||
|
||||
trackImporter = new TrackImporter(context, contentProviderUtils, Distance.of(10), Distance.of(200), true);
|
||||
|
||||
TrackRecordingServiceTestUtils.resetService(mServiceRule, context);
|
||||
}
|
||||
|
||||
@After
|
||||
public void FileTearDown() {
|
||||
public void tearDown() throws TimeoutException {
|
||||
tmpFile.deleteOnExit();
|
||||
tmpFileUri = null;
|
||||
|
||||
// Ensure that the database is empty after every test
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
|
||||
TrackRecordingServiceTestUtils.resetService(mServiceRule, context);
|
||||
}
|
||||
|
||||
public void setUp(boolean hasSensorDistance) throws TimeoutException {
|
||||
public void setUp() throws TimeoutException {
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)))
|
||||
.getService();
|
||||
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:02Z");
|
||||
trackId = service.startNewTrack();
|
||||
|
||||
Distance sensorDistance = hasSensorDistance ? Distance.of(5) : null;
|
||||
Distance sensorDistance = Distance.of(10); // recording distance interval
|
||||
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14, 10, 15, 10, 1, 66, 3, 50, sensorDistance), 0);
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3, 14, 10, 15, 10, 1);
|
||||
service.insertMarker("Marker 1", "Marker 1 category", "Marker 1 desc", null);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.001, 10, 15, 10, 0, 66, 3, 50, sensorDistance), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.002, 10, 15, 10, 0, 66, 3, 50, sensorDistance), 0);
|
||||
|
||||
// A sensor-only TrackPoint
|
||||
trackPointCreator.setClock("2020-02-02T02:02:04Z");
|
||||
mockAltitudeChange(trackPointCreator, 1);
|
||||
mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f);
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:05Z");
|
||||
mockBLESensorData(trackPointCreator, 5f, Distance.of(2), 66f, 3f, 50f); // Distance will be added to next TrackPoint
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:05Z", 3, 14.001, 10, 15, 10, 0);
|
||||
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:06Z");
|
||||
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, trackPointCreator));
|
||||
service.pauseCurrentTrack();
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:20Z");
|
||||
service.resumeCurrentTrack();
|
||||
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.003, 10, 15, 10, 0, 66, 3, 50, sensorDistance), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 16, 10, 15, 10, 0, 66, 3, 50, sensorDistance), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 16.001, 10, 15, 10, 0, 66, 3, 50, sensorDistance), 0);
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:21Z", 3, 14.002, 10, 15, 10, 0);
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:22Z", 3, 16, 10, 15, 10, 0);
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:23Z", 3, 16.001, 10, 15, 10, 0);
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:24Z");
|
||||
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, trackPointCreator));
|
||||
service.endCurrentTrack();
|
||||
|
||||
track = contentProviderUtils.getTrack(trackId);
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
track.setIcon(TRACK_ICON);
|
||||
track.setCategory(TRACK_CATEGORY);
|
||||
track.setDescription(TRACK_DESCRIPTION);
|
||||
@@ -140,60 +173,11 @@ public class ExportImportTest {
|
||||
assertEquals(2, markers.size());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
contentProviderUtils.deleteTrack(context, trackId);
|
||||
if (importTrackId != null) {
|
||||
contentProviderUtils.deleteTrack(context, importTrackId);
|
||||
}
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kml_with_trackdetail_and_sensordata() throws TimeoutException, IOException {
|
||||
setUp(true);
|
||||
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
contentProviderUtils.deleteTrack(context, trackId);
|
||||
|
||||
// 2. import
|
||||
InputStream inputStream = context.getContentResolver().openInputStream(tmpFileUri);
|
||||
XMLImporter importer = new XMLImporter(new KmlTrackImporter(context, trackImporter));
|
||||
importTrackId = importer.importFile(inputStream).get(0);
|
||||
|
||||
// then
|
||||
// 1. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals(track.getCategory(), importedTrack.getCategory());
|
||||
assertEquals(track.getDescription(), importedTrack.getDescription());
|
||||
assertEquals(track.getName(), importedTrack.getName());
|
||||
assertEquals(track.getIcon(), importedTrack.getIcon());
|
||||
|
||||
// 2. trackpoints
|
||||
TrackPointAssert a = new TrackPointAssert()
|
||||
.noAccuracy();
|
||||
a.assertEquals(trackPoints, TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId));
|
||||
|
||||
// 2. trackstatistics
|
||||
assertTrackStatistics(false, true);
|
||||
|
||||
// 4. markers
|
||||
assertMarkers();
|
||||
}
|
||||
|
||||
//TODO Does not test images
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kmz_with_trackdetail_and_sensordata() throws TimeoutException, IOException {
|
||||
setUp(true);
|
||||
setUp();
|
||||
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
@@ -221,10 +205,51 @@ public class ExportImportTest {
|
||||
// 2. trackpoints
|
||||
TrackPointAssert a = new TrackPointAssert()
|
||||
.noAccuracy();
|
||||
a.assertEquals(trackPoints, TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId));
|
||||
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
a.assertEquals(trackPoints, actual);
|
||||
|
||||
// 2. trackstatistics
|
||||
assertTrackStatistics(false, true);
|
||||
// 3. trackstatistics
|
||||
TrackStatistics importedTrackStatistics = importedTrack.getTrackStatistics();
|
||||
|
||||
// Time
|
||||
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:02Z"), importedTrackStatistics.getStartTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:24Z"), importedTrackStatistics.getStopTime());
|
||||
|
||||
TrackStatistics originalTrackStatistics = track.getTrackStatistics();
|
||||
|
||||
assertEquals(originalTrackStatistics.getTotalTime(), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(7), importedTrackStatistics.getTotalTime());
|
||||
|
||||
assertEquals(originalTrackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(3), importedTrackStatistics.getMovingTime());
|
||||
|
||||
// Distance
|
||||
assertEquals(originalTrackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance());
|
||||
assertEquals(123.16, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
|
||||
// Speed
|
||||
assertEquals(originalTrackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed());
|
||||
assertEquals(41.05, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
|
||||
assertEquals(originalTrackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed());
|
||||
assertEquals(17.59, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
|
||||
assertEquals(originalTrackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed());
|
||||
assertEquals(41.05, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
|
||||
// Altitude
|
||||
assertEquals(originalTrackStatistics.getMinAltitude(), importedTrackStatistics.getMinAltitude(), 0.01);
|
||||
assertEquals(10, importedTrackStatistics.getMinAltitude(), 0.01);
|
||||
|
||||
assertEquals(originalTrackStatistics.getMaxAltitude(), importedTrackStatistics.getMaxAltitude(), 0.01);
|
||||
assertEquals(10, importedTrackStatistics.getMaxAltitude(), 0.01);
|
||||
|
||||
assertEquals(originalTrackStatistics.getTotalAltitudeGain(), importedTrackStatistics.getTotalAltitudeGain(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeGain(), 0.01);
|
||||
|
||||
assertEquals(originalTrackStatistics.getTotalAltitudeLoss(), importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
|
||||
|
||||
// 4. markers
|
||||
assertMarkers();
|
||||
@@ -233,7 +258,7 @@ public class ExportImportTest {
|
||||
@LargeTest
|
||||
@Test(expected = ImportAlreadyExistsException.class)
|
||||
public void kml_with_trackdetail_and_sensordata_duplicate_trackUUID() throws TimeoutException, IOException {
|
||||
setUp(false);
|
||||
setUp();
|
||||
|
||||
// given
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||
@@ -259,7 +284,7 @@ public class ExportImportTest {
|
||||
@LargeTest
|
||||
@Test
|
||||
public void gpx() throws TimeoutException, IOException {
|
||||
setUp(true);
|
||||
setUp();
|
||||
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
@@ -278,29 +303,90 @@ public class ExportImportTest {
|
||||
|
||||
// then
|
||||
// 1. track
|
||||
Track trackImported = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(trackImported);
|
||||
assertEquals(track.getCategory(), trackImported.getCategory());
|
||||
assertEquals(track.getDescription(), trackImported.getDescription());
|
||||
assertEquals(track.getName(), trackImported.getName());
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals(track.getCategory(), importedTrack.getCategory());
|
||||
assertEquals(track.getDescription(), importedTrack.getDescription());
|
||||
assertEquals(track.getName(), importedTrack.getName());
|
||||
|
||||
//TODO exporting and importing a track icon is not yet supported by GpxTrackWriter.
|
||||
//assertEquals(track.getIcon(), trackImported.getIcon());
|
||||
//assertEquals(track.getIcon(), importedTrack.getIcon());
|
||||
|
||||
// 2. trackpoints
|
||||
// The GPX exporter does not support exporting TrackPoints without lat/lng.
|
||||
// Therefore, the track segmentation is changes.
|
||||
List<TrackPoint> trackPointsWithCoordinates = trackPoints.stream().filter(it -> TrackPoint.Type.SEGMENT_START_AUTOMATIC.equals(it.getType()) || TrackPoint.Type.TRACKPOINT.equals(it.getType())).collect(Collectors.toList());
|
||||
trackPointsWithCoordinates.get(0).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||
trackPointsWithCoordinates.get(3).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||
|
||||
TrackPointAssert a = new TrackPointAssert()
|
||||
.setDelta(0.05)
|
||||
.noAccuracy(); // speed is not fully
|
||||
a.assertEquals(trackPointsWithCoordinates, TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId));
|
||||
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:03Z"))
|
||||
.setLatitude(3)
|
||||
.setLongitude(14)
|
||||
.setAltitude(10)
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeLoss(1f)
|
||||
.setAltitudeGain(1f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:05Z"))
|
||||
.setLatitude(3)
|
||||
.setLongitude(14.001)
|
||||
.setAltitude(10)
|
||||
.setSpeed(Speed.of(5))
|
||||
.setAltitudeLoss(1f)
|
||||
.setAltitudeGain(1f)
|
||||
.setSensorDistance(Distance.of(12))
|
||||
.setHeartRate_bpm(66f)
|
||||
.setPower(50f)
|
||||
.setCadence_rpm(3f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:21Z"))
|
||||
.setLatitude(3)
|
||||
.setLongitude(14.002)
|
||||
.setAltitude(10)
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f)
|
||||
.setSpeed(Speed.of(15)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:22Z"))
|
||||
.setLatitude(3)
|
||||
.setLongitude(16)
|
||||
.setAltitude(10)
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f)
|
||||
.setSpeed(Speed.of(15)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:23Z"))
|
||||
.setLatitude(3)
|
||||
.setLongitude(16.001)
|
||||
.setAltitude(10)
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f)
|
||||
.setSpeed(Speed.of(15))
|
||||
), actual);
|
||||
|
||||
// 3. trackstatistics
|
||||
assertTrackStatistics(true, false);
|
||||
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||
TrackStatistics importedTrackStatistics = importedTrack.getTrackStatistics();
|
||||
|
||||
// Time
|
||||
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:03Z"), importedTrackStatistics.getStartTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:23Z"), importedTrackStatistics.getStopTime());
|
||||
|
||||
assertEquals(Duration.ofSeconds(3), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(3), importedTrackStatistics.getMovingTime());
|
||||
|
||||
// Distance
|
||||
assertEquals(123.16, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
|
||||
// Speed
|
||||
assertEquals(41.05, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(41.05, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(41.05, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
|
||||
// Altitude
|
||||
assertEquals(10, importedTrackStatistics.getMinAltitude(), 0.01);
|
||||
assertEquals(10, importedTrackStatistics.getMaxAltitude(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeGain(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
|
||||
|
||||
// 4. markers
|
||||
assertMarkers();
|
||||
@@ -309,7 +395,7 @@ public class ExportImportTest {
|
||||
@LargeTest
|
||||
@Test(expected = ImportAlreadyExistsException.class)
|
||||
public void gpx_duplicate_trackUUID() throws TimeoutException, IOException {
|
||||
setUp(false);
|
||||
setUp();
|
||||
|
||||
// given
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||
@@ -353,56 +439,53 @@ public class ExportImportTest {
|
||||
}
|
||||
}
|
||||
|
||||
private void assertTrackStatistics(boolean isGpx, boolean verifyDistance) {
|
||||
double delta = isGpx ? 0.1 : 0.01;
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, Float heartRate, Float cyclingCadence, Float power) {
|
||||
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, trackPointCreator) {
|
||||
@Override
|
||||
public SensorDataSet fill(@NonNull TrackPoint trackPoint) {
|
||||
SensorDataSet sensorDataSet = new SensorDataSet();
|
||||
sensorDataSet.set(new SensorDataCyclingPower("power", "power", power));
|
||||
sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", heartRate));
|
||||
|
||||
assertNotNull(importedTrack.getTrackStatistics());
|
||||
SensorDataCycling.Cadence cadence = Mockito.mock(SensorDataCycling.Cadence.class);
|
||||
Mockito.when(cadence.hasValue()).thenReturn(true);
|
||||
Mockito.when(cadence.getValue()).thenReturn(cyclingCadence);
|
||||
sensorDataSet.set(cadence);
|
||||
|
||||
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||
TrackStatistics importedTrackStatistics = importedTrack.getTrackStatistics();
|
||||
if (distance != null && speed != null) {
|
||||
SensorDataCycling.DistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCycling.DistanceSpeed.Data.class);
|
||||
Mockito.when(distanceSpeedData.getDistanceOverall()).thenReturn(distance);
|
||||
Mockito.when(distanceSpeedData.getSpeed()).thenReturn(Speed.of(speed));
|
||||
SensorDataCycling.DistanceSpeed distanceSpeed = Mockito.mock(SensorDataCycling.DistanceSpeed.class);
|
||||
Mockito.when(distanceSpeed.hasValue()).thenReturn(true);
|
||||
Mockito.when(distanceSpeed.getValue()).thenReturn(distanceSpeedData);
|
||||
sensorDataSet.set(distanceSpeed);
|
||||
}
|
||||
|
||||
// Time
|
||||
assertTrue(trackStatistics.getStartTime().isBefore(trackStatistics.getStopTime())); //Just to be sure.
|
||||
if (!isGpx) {
|
||||
assertEquals(trackStatistics.getStartTime(), importedTrackStatistics.getStartTime());
|
||||
assertEquals(trackStatistics.getStopTime(), importedTrackStatistics.getStopTime());
|
||||
|
||||
assertEquals(trackStatistics.getTotalTime(), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(trackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime());
|
||||
|
||||
// Distance
|
||||
if (verifyDistance) {
|
||||
assertEquals(trackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance());
|
||||
sensorDataSet.fillTrackPoint(trackPoint);
|
||||
return sensorDataSet;
|
||||
}
|
||||
|
||||
// Speed
|
||||
assertEquals(trackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed());
|
||||
assertEquals(trackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed());
|
||||
assertEquals(trackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed());
|
||||
}
|
||||
|
||||
// Altitude
|
||||
assertEquals(trackStatistics.getMinAltitude(), importedTrackStatistics.getMinAltitude(), delta);
|
||||
if (isGpx) {
|
||||
assertEquals(trackStatistics.getMaxAltitude(), importedTrackStatistics.getMaxAltitude(), 2);
|
||||
} else {
|
||||
assertEquals(trackStatistics.getMaxAltitude(), importedTrackStatistics.getMaxAltitude(), delta);
|
||||
}
|
||||
assertEquals(trackStatistics.getTotalAltitudeGain(), importedTrackStatistics.getTotalAltitudeGain(), delta);
|
||||
assertEquals(trackStatistics.getTotalAltitudeLoss(), importedTrackStatistics.getTotalAltitudeLoss(), delta);
|
||||
});
|
||||
trackPointCreator.onChange(null);
|
||||
}
|
||||
|
||||
private static TrackPoint createTrackPoint(long time, double latitude, double longitude, float accuracy, float speed, float altitude, float altitudeGain, float heartRate, float cyclingCadence, float power, Distance distance) {
|
||||
TrackPoint tp = new TrackPoint(latitude, longitude, Altitude.WGS84.of(altitude), Instant.ofEpochMilli(time));
|
||||
tp.setAccuracy(accuracy);
|
||||
tp.setSpeed(Speed.of(speed));
|
||||
tp.setHeartRate_bpm(heartRate);
|
||||
tp.setCyclingCadence_rpm(cyclingCadence);
|
||||
tp.setPower(power);
|
||||
tp.setAltitudeGain(altitudeGain);
|
||||
tp.setAltitudeLoss(altitudeGain); //TODO
|
||||
tp.setSensorDistance(distance);
|
||||
return tp;
|
||||
private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) {
|
||||
AltitudeSumManager altitudeSumManager = trackPointCreator.getAltitudeSumManager();
|
||||
altitudeSumManager.setAltitudeGain_m(altitudeGain);
|
||||
altitudeSumManager.setAltitudeLoss_m(altitudeGain);
|
||||
}
|
||||
|
||||
private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float speed, float altitude, float altitudeGain) {
|
||||
Location location = new Location("mock");
|
||||
location.setLatitude(latitude);
|
||||
location.setLongitude(longitude);
|
||||
location.setAccuracy(accuracy);
|
||||
location.setSpeed(speed);
|
||||
location.setAltitude(altitude);
|
||||
|
||||
mockAltitudeChange(trackPointCreator, altitudeGain);
|
||||
|
||||
trackPointCreator.setClock(time);
|
||||
trackPointCreator.getGpsHandler().onLocationChanged(location);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import org.junit.runners.JUnit4;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
@@ -23,6 +24,7 @@ import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
@@ -67,7 +69,7 @@ public class GPXImportTest {
|
||||
importTrackId = importer.importFile(inputStream).get(0);
|
||||
|
||||
// then
|
||||
// 1. track
|
||||
// 2. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("the category", importedTrack.getCategory());
|
||||
@@ -75,6 +77,8 @@ public class GPXImportTest {
|
||||
assertEquals("2021-01-07 22:51", importedTrack.getName());
|
||||
assertEquals("UNKNOWN", importedTrack.getIcon());
|
||||
|
||||
//TODO Check trackstatistics
|
||||
|
||||
// 3. trackpoints
|
||||
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
assertEquals(6, importedTrackPoints.size());
|
||||
@@ -106,4 +110,102 @@ public class GPXImportTest {
|
||||
.setAltitude(10)
|
||||
), importedTrackPoints);
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void gpx_without_speed() throws IOException {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new GpxTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.gpx11_without_speed);
|
||||
|
||||
// when
|
||||
// 1. import
|
||||
importTrackId = importer.importFile(inputStream).get(0);
|
||||
|
||||
// then
|
||||
// 2. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("", importedTrack.getCategory());
|
||||
assertEquals("", importedTrack.getDescription());
|
||||
assertEquals("20210907_213924.gpx", importedTrack.getName());
|
||||
assertEquals("", importedTrack.getIcon());
|
||||
|
||||
// 3. trackstatistics
|
||||
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
|
||||
assertEquals(1.44, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(Duration.ofSeconds(53), trackStatistics.getMovingTime());
|
||||
|
||||
// 4. trackpoints
|
||||
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
assertEquals(3, importedTrackPoints.size());
|
||||
|
||||
TrackPointAssert a = new TrackPointAssert();
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2021-09-07T22:10:19Z"))
|
||||
.setLatitude(30.14185982)
|
||||
.setLongitude(-40.3863038)
|
||||
.setAltitude(-5),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2021-09-07T22:11:07Z"))
|
||||
.setLatitude(30.14184657)
|
||||
.setLongitude(-40.38670089)
|
||||
.setAltitude(-5)
|
||||
.setSpeed(Speed.of(0.7976524233818054)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2021-09-07T22:12:00Z"))
|
||||
.setLatitude(30.14185982)
|
||||
.setLongitude(-40.3863038)
|
||||
.setAltitude(-5)
|
||||
.setSpeed(Speed.of(0.7224021553993225))
|
||||
), importedTrackPoints);
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void gpx_speed_no_namespace() throws IOException {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new GpxTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.gpx11_with_speed_no_namespace);
|
||||
|
||||
// when
|
||||
// 1. import
|
||||
importTrackId = importer.importFile(inputStream).get(0);
|
||||
|
||||
// then
|
||||
// 2. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("", importedTrack.getCategory());
|
||||
assertEquals("", importedTrack.getDescription());
|
||||
assertEquals("20210907_213924.gpx", importedTrack.getName());
|
||||
assertEquals("", importedTrack.getIcon());
|
||||
|
||||
// 3. trackstatistics
|
||||
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
|
||||
assertEquals(4, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(Duration.ofSeconds(101), trackStatistics.getMovingTime());
|
||||
|
||||
// 4. trackpoints
|
||||
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
assertEquals(3, importedTrackPoints.size());
|
||||
|
||||
TrackPointAssert a = new TrackPointAssert();
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2021-09-07T22:10:19Z"))
|
||||
.setLatitude(30.14185982)
|
||||
.setLongitude(-40.3863038)
|
||||
.setAltitude(-5)
|
||||
.setSpeed(Speed.of(5)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2021-09-07T22:11:07Z"))
|
||||
.setLatitude(30.14184657)
|
||||
.setLongitude(-40.38670089)
|
||||
.setAltitude(-5)
|
||||
.setSpeed(Speed.of(0.7976524233818054))
|
||||
.setSpeed(Speed.of(4)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2021-09-07T22:12:00Z"))
|
||||
.setLatitude(30.14185982)
|
||||
.setLongitude(-40.3863038)
|
||||
.setAltitude(-5)
|
||||
.setSpeed(Speed.of(3))
|
||||
), importedTrackPoints);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
|
||||
public class TrackPointAssert {
|
||||
|
||||
private boolean assertTime = true;
|
||||
private boolean assertAccuracy = true;
|
||||
|
||||
private double delta = 0.001;
|
||||
@@ -17,10 +16,7 @@ public class TrackPointAssert {
|
||||
}
|
||||
|
||||
public void assertEquals(TrackPoint expected, TrackPoint actual) {
|
||||
Assert.assertNotNull(actual.getTime());
|
||||
if (assertTime) {
|
||||
Assert.assertEquals(expected.getTime(), actual.getTime());
|
||||
}
|
||||
Assert.assertEquals(expected.getTime(), actual.getTime());
|
||||
|
||||
Assert.assertEquals(expected.getType(), actual.getType());
|
||||
|
||||
@@ -50,12 +46,12 @@ public class TrackPointAssert {
|
||||
}
|
||||
|
||||
if (assertAccuracy) {
|
||||
Assert.assertEquals(expected.hasAccuracy(), actual.hasAccuracy());
|
||||
if (expected.hasAccuracy()) {
|
||||
Assert.assertEquals(expected.getAccuracy(), actual.getAccuracy(), delta);
|
||||
Assert.assertEquals(expected.hasHorizontalAccuracy(), actual.hasHorizontalAccuracy());
|
||||
if (expected.hasHorizontalAccuracy()) {
|
||||
Assert.assertEquals(expected.getHorizontalAccuracy().toM(), actual.getHorizontalAccuracy().toM(), delta);
|
||||
}
|
||||
} else {
|
||||
Assert.assertFalse(actual.hasAccuracy());
|
||||
Assert.assertFalse(actual.hasHorizontalAccuracy());
|
||||
}
|
||||
|
||||
Assert.assertEquals(expected.hasSensorDistance(), actual.hasSensorDistance());
|
||||
@@ -73,13 +69,26 @@ public class TrackPointAssert {
|
||||
Assert.assertEquals(expected.getPower(), actual.getPower(), delta);
|
||||
}
|
||||
|
||||
Assert.assertEquals(expected.hasCyclingCadence(), actual.hasCyclingCadence());
|
||||
if (expected.hasCyclingCadence()) {
|
||||
Assert.assertEquals(expected.getCyclingCadence_rpm(), actual.getCyclingCadence_rpm(), delta);
|
||||
Assert.assertEquals(expected.hasCadence(), actual.hasCadence());
|
||||
if (expected.hasCadence()) {
|
||||
Assert.assertEquals(expected.getCadence_rpm(), actual.getCadence_rpm(), delta);
|
||||
}
|
||||
}
|
||||
|
||||
public void assertEquals(List<TrackPoint> expected, List<TrackPoint> actual) {
|
||||
try {
|
||||
Assert.assertEquals(expected.size(), actual.size());
|
||||
} catch (AssertionError e) {
|
||||
throw new AssertionError("Size difference; expected: " + expected.size() + "; actual: " + actual.size() + "\nExpected: " + expected + "\n actual: " + actual);
|
||||
}
|
||||
|
||||
for (int i = 0; i < expected.size(); i++) {
|
||||
try {
|
||||
assertEquals(expected.get(i), actual.get(i));
|
||||
} catch (AssertionError e) {
|
||||
throw new AssertionError("Expected: " + i + " to be " + expected.get(i) + "\n actual: " + actual.get(i), e);
|
||||
}
|
||||
}
|
||||
Assert.assertEquals(expected.size(), actual.size());
|
||||
|
||||
for (int i = 0; i < expected.size(); i++) {
|
||||
@@ -87,11 +96,6 @@ public class TrackPointAssert {
|
||||
}
|
||||
}
|
||||
|
||||
public TrackPointAssert ignoreTime() {
|
||||
this.assertTime = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TrackPointAssert noAccuracy() {
|
||||
this.assertAccuracy = false;
|
||||
return this;
|
||||
|
||||
@@ -1,83 +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.services;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import de.dennisguse.opentracks.services.handlers.AdaptiveLocationListenerPolicy;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests the {@link AdaptiveLocationListenerPolicy}.
|
||||
*
|
||||
* @author youtaol
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
public class AdaptiveLocationListenerPolicyTest {
|
||||
|
||||
private static final Duration MIN = Duration.ofMillis(1000);
|
||||
private static final Duration MAX = Duration.ofMillis(3000);
|
||||
private static final int MIN_DISTANCE = 10;
|
||||
private static final Duration NEW_IDLE_TIME_BIG = Duration.ofMillis(10000);
|
||||
private static final Duration NEW_IDLE_TIME_NORMAL = Duration.ofMillis(5000);
|
||||
private static final Duration NEW_IDLE_TIME_SMALL = Duration.ofMillis(2000);
|
||||
private static final Duration NEW_IDLE_TIME_LESS_THAN_MIN = Duration.ofMillis(500);
|
||||
private AdaptiveLocationListenerPolicy adaptiveLocationListenerPolicy;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
adaptiveLocationListenerPolicy = new AdaptiveLocationListenerPolicy(MIN, MAX, MIN_DISTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the {@link AdaptiveLocationListenerPolicy#getDesiredPollingInterval()} in four situations.
|
||||
* <ul>
|
||||
* <li>The newIdleTime is bigger than max interval.</li>
|
||||
* <li>The newIdleTime is between min and max interval.</li>
|
||||
* <li>The newIdleTime is smaller than max interval.</li>
|
||||
* <li>The newIdleTime is smaller than the smallest interval unit.</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Test
|
||||
public void testGetDesiredPollingInterval() {
|
||||
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_BIG);
|
||||
assertEquals(MAX, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
||||
|
||||
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_NORMAL);
|
||||
// First get the half of NEW_IDLE_TIME_NORMAL, and then round it to the nearest second.
|
||||
assertEquals(NEW_IDLE_TIME_NORMAL.dividedBy(2).getSeconds(), adaptiveLocationListenerPolicy.getDesiredPollingInterval().getSeconds());
|
||||
|
||||
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_SMALL);
|
||||
assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
||||
|
||||
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_LESS_THAN_MIN);
|
||||
assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link AdaptiveLocationListenerPolicy#getMinDistance_m()}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetMinDistance() {
|
||||
assertEquals(MIN_DISTANCE, adaptiveLocationListenerPolicy.getMinDistance_m());
|
||||
}
|
||||
}
|
||||
@@ -40,20 +40,21 @@ public class TrackRecordingServiceNotificationManagerTest {
|
||||
|
||||
@Test
|
||||
public void updateLocation_triggersAlertOnlyOnFirstInaccurateLocation() {
|
||||
when(trackPointMock.hasAccuracy()).thenReturn(true);
|
||||
when(trackPointMock.getAccuracy()).thenReturn(999f);
|
||||
when(trackPointMock.hasHorizontalAccuracy()).thenReturn(true);
|
||||
when(trackPointMock.getHorizontalAccuracy()).thenReturn(Distance.of(999f));
|
||||
when(trackPointMock.getSpeed()).thenReturn(Speed.of(0));
|
||||
when(trackStatisticsMock.getTotalDistance()).thenReturn(Distance.of(0));
|
||||
when(notificationCompatBuilder.setOnlyAlertOnce(anyBoolean())).thenReturn(notificationCompatBuilder);
|
||||
when(notificationCompatBuilder.setOnlyAlertOnce(anyBoolean()))
|
||||
.thenReturn(notificationCompatBuilder);
|
||||
|
||||
TrackRecordingServiceNotificationManager subject = new TrackRecordingServiceNotificationManager(notificationManager, notificationCompatBuilder);
|
||||
subject.setMetricUnits(true);
|
||||
|
||||
// when
|
||||
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, 100);
|
||||
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, 100);
|
||||
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, 1000);
|
||||
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, 100);
|
||||
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, Distance.of(100));
|
||||
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, Distance.of(100));
|
||||
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, Distance.of(1000));
|
||||
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, Distance.of(100));
|
||||
|
||||
// then
|
||||
verify(notificationCompatBuilder, times(6)).setOnlyAlertOnce(true);
|
||||
|
||||
@@ -1,418 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 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.services;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.MediumTest;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Marker;
|
||||
import de.dennisguse.opentracks.content.data.Speed;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests for the track recording service.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
* <p>
|
||||
* //TODO ATTENTION: This tests deletes all stored tracks in the database.
|
||||
* So, if it is executed on a real device, data might be lost.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TrackRecordingServiceTest {
|
||||
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
@BeforeClass
|
||||
public static void preSetUp() {
|
||||
// Prepare looper for Android's message queue
|
||||
if (Looper.myLooper() == null) Looper.prepare();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void finalTearDown() {
|
||||
if (Looper.myLooper() != null) Looper.myLooper().quit();
|
||||
}
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private final SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
private final Track.Id trackId = new Track.Id(Math.abs(new Random().nextLong()));
|
||||
|
||||
static Intent createStartIntent(Context context) {
|
||||
return new Intent(context, TrackRecordingService.class);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
// Set up the mock content resolver
|
||||
ContentProvider customContentProvider = new CustomContentProvider() {
|
||||
};
|
||||
customContentProvider.attachInfo(context, null);
|
||||
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
// Let's use default values.
|
||||
sharedPreferences.edit().clear().commit();
|
||||
|
||||
// Ensure that the database is empty before every test
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws TimeoutException {
|
||||
// Reset service (if some previous test failed)
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||
.getService();
|
||||
if (service.isRecording() || service.isPaused()) {
|
||||
service.endCurrentTrack();
|
||||
}
|
||||
|
||||
// Ensure that the database is empty after every test
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
@Test
|
||||
public void testStartable() throws TimeoutException {
|
||||
mServiceRule.startService(createStartIntent(context));
|
||||
assertNotNull(mServiceRule.bindService(createStartIntent(context)));
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testBindable() throws TimeoutException {
|
||||
IBinder service = mServiceRule.bindService(createStartIntent(context));
|
||||
assertNotNull(service);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_noTracks() throws TimeoutException {
|
||||
// given
|
||||
List<Track> tracks = contentProviderUtils.getTracks();
|
||||
assertTrue(tracks.isEmpty());
|
||||
|
||||
// when
|
||||
Intent startIntent = createStartIntent(context);
|
||||
mServiceRule.startService(startIntent);
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(startIntent))
|
||||
.getService();
|
||||
|
||||
// then
|
||||
// Test if we start in no-recording mode by default.
|
||||
assertFalse(service.isRecording());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_oldTracks() throws TimeoutException {
|
||||
// given
|
||||
createDummyTrack(trackId);
|
||||
|
||||
// when
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||
.getService();
|
||||
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_serviceRestart_whileRecording() throws TimeoutException {
|
||||
// given
|
||||
createDummyTrack(trackId);
|
||||
|
||||
//when
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||
.getService();
|
||||
service.resumeTrack(trackId);
|
||||
|
||||
// then
|
||||
assertTrue(service.isRecording());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_start() throws TimeoutException {
|
||||
// given
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||
.getService();
|
||||
|
||||
// when
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
|
||||
// then
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
|
||||
assertEquals(1, trackPoints.size());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_stop() throws TimeoutException {
|
||||
// given
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||
.getService();
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
|
||||
// when
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
|
||||
assertEquals(2, trackPoints.size());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_pauseAndResume() throws TimeoutException {
|
||||
// given
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||
.getService();
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
|
||||
// when
|
||||
service.pauseCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(2, contentProviderUtils.getTrackPointCursor(trackId, null).getCount());
|
||||
|
||||
//when
|
||||
service.resumeTrack(trackId);
|
||||
|
||||
// then
|
||||
assertTrue(service.isRecording());
|
||||
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
assertEquals(3, trackPoints.size());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(2).getType());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_resumeStoppedTrack() throws TimeoutException, InterruptedException {
|
||||
// given
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||
.getService();
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
assertTrue(service.isRecording());
|
||||
service.endCurrentTrack();
|
||||
|
||||
assertEquals(2, contentProviderUtils.getTrackPointCursor(trackId, null).getCount());
|
||||
|
||||
// when
|
||||
service.resumeTrack(trackId);
|
||||
newTrackPoint(service);
|
||||
|
||||
// then
|
||||
assertTrue(service.isRecording());
|
||||
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
assertEquals(4, trackPoints.size());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(2).getType());
|
||||
assertEquals(TrackPoint.Type.TRACKPOINT, trackPoints.get(3).getType());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_stopPausedTrack() throws TimeoutException {
|
||||
// given
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||
.getService();
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
assertTrue(service.isRecording());
|
||||
service.pauseCurrentTrack();
|
||||
|
||||
assertEquals(2, contentProviderUtils.getTrackPointCursor(trackId, null).getCount());
|
||||
|
||||
// when
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
assertEquals(2, trackPoints.size());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testStartNewTrack_alreadyRecording() throws TimeoutException {
|
||||
// given
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||
.getService();
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
assertTrue(service.isRecording());
|
||||
|
||||
// when
|
||||
Track.Id newTrackId = service.startNewTrack();
|
||||
|
||||
// then
|
||||
assertNotNull(trackId);
|
||||
assertNull(newTrackId);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testEndCurrentTrack_noRecording() throws TimeoutException {
|
||||
// given
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||
.getService();
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
// when
|
||||
// Ending the current track when there is no recording should not result in any error.
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testInsertWaypointMarker_noRecordingTrack() throws TimeoutException {
|
||||
// given
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||
.getService();
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
// when
|
||||
Marker.Id markerId = service.insertMarker(null, null, null, null);
|
||||
|
||||
// then
|
||||
assertNull(markerId);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testInsertWaypointMarker_validWaypoint() throws TimeoutException, InterruptedException {
|
||||
// given
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||
.getService();
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
assertTrue(service.isRecording());
|
||||
newTrackPoint(service);
|
||||
|
||||
// when
|
||||
Marker.Id markerId = service.insertMarker(null, null, null, null);
|
||||
|
||||
// then
|
||||
assertNotEquals(new Marker.Id(-1L), markerId);
|
||||
Marker wpt = contentProviderUtils.getMarker(markerId);
|
||||
assertEquals(context.getString(R.string.marker_icon_url), wpt.getIcon());
|
||||
assertEquals(context.getString(R.string.marker_name_format, 1), wpt.getName());
|
||||
assertEquals(trackId, wpt.getTrackId());
|
||||
assertEquals(0.0, wpt.getLength().toM(), 0.01);
|
||||
assertNotNull(wpt.getLocation());
|
||||
|
||||
service.endCurrentTrack();
|
||||
}
|
||||
|
||||
private void addTrack(Track track) {
|
||||
assertNotNull(track.getId());
|
||||
contentProviderUtils.insertTrack(track);
|
||||
assertEquals(track.getId(), contentProviderUtils.getTrack(track.getId()).getId());
|
||||
}
|
||||
|
||||
// NOTE: Do not use to create a track that is currently recording.
|
||||
private void createDummyTrack(Track.Id id) {
|
||||
Track dummyTrack = new Track();
|
||||
dummyTrack.setId(id);
|
||||
dummyTrack.setName("Dummy Track");
|
||||
TrackStatistics trackStatistics = new TrackStatistics();
|
||||
Instant now = Instant.now();
|
||||
trackStatistics.setStartTime(now.minusSeconds(5L));
|
||||
trackStatistics.setStopTime(now.minusSeconds(1L));
|
||||
dummyTrack.setTrackStatistics(trackStatistics);
|
||||
addTrack(dummyTrack);
|
||||
}
|
||||
|
||||
private static void newTrackPoint(TrackRecordingService trackRecordingService) throws InterruptedException {
|
||||
newTrackPoint(trackRecordingService, 45.0f, 35f, 5, 10, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
static void newTrackPoint(TrackRecordingService trackRecordingService, double latitude, double longitude, float accuracy, long speed) throws InterruptedException {
|
||||
newTrackPoint(trackRecordingService, latitude, longitude, accuracy, speed, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a location and waits for 200ms.
|
||||
*/
|
||||
private static void newTrackPoint(TrackRecordingService trackRecordingService, double latitude, double longitude, float accuracy, long speed, long time) {
|
||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochMilli(time))
|
||||
.setLongitude(longitude)
|
||||
.setLatitude(latitude)
|
||||
.setAccuracy(accuracy)
|
||||
.setSpeed(Speed.of(speed))
|
||||
.setBearing(3.0f);
|
||||
trackRecordingService.newTrackPoint(trackPoint, 50);
|
||||
}
|
||||
}
|
||||
@@ -1,496 +0,0 @@
|
||||
package de.dennisguse.opentracks.services;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.MediumTest;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.Speed;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataHeartRate;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.io.file.importer.TrackPointAssert;
|
||||
import de.dennisguse.opentracks.services.sensors.AltitudeSumManager;
|
||||
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
/**
|
||||
* Tests insert location.
|
||||
* <p>
|
||||
* //TODO ATTENTION: This tests deletes all stored tracks in the database.
|
||||
* So, if it is executed on a real device, data might be lost.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
//TODO Implement as mock test; no need to store data in database
|
||||
public class TrackRecordingServiceTestLocation {
|
||||
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
private TrackRecordingService service;
|
||||
|
||||
private final AltitudeSumManager altitudeSumManager = new AltitudeSumManager() {
|
||||
@Override
|
||||
public void fill(@NonNull TrackPoint trackPoint) {
|
||||
trackPoint.setAltitudeGain(0f);
|
||||
trackPoint.setAltitudeLoss(0f);
|
||||
}
|
||||
};
|
||||
|
||||
@BeforeClass
|
||||
public static void preSetUp() {
|
||||
// Prepare looper for Android's message queue
|
||||
if (Looper.myLooper() == null) Looper.prepare();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void finalTearDown() {
|
||||
if (Looper.myLooper() != null) Looper.myLooper().quit();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws TimeoutException {
|
||||
// Set up the mock content resolver
|
||||
ContentProvider customContentProvider = new CustomContentProvider() {
|
||||
};
|
||||
customContentProvider.attachInfo(context, null);
|
||||
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
tearDown();
|
||||
|
||||
// Let's use default values.
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
sharedPreferences.edit().clear().commit();
|
||||
|
||||
service = ((TrackRecordingService.Binder) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context)))
|
||||
.getService();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
// Ensure that the database is empty after every test
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testOnLocationChangedAsync_movingAccurate() throws Exception {
|
||||
// given
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
service.setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 1, 15);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0001, 35.0, 2, 15);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0002, 35.0, 3, 15);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0003, 35.0, 4, 15);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0004, 35.0, 5, 15);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0005, 35.0, 6, 15);
|
||||
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
TrackPointAssert a = new TrackPointAssert()
|
||||
.ignoreTime();
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(1)
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45.0001)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(2)
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45.0001)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(3)
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45.0002)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(4)
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45.0003)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(5)
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45.0004)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(6)
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL)
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
), trackPoints);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testOnLocationChangedAsync_slowMovingAccurate() throws Exception {
|
||||
// given
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
service.setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 1, 15);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.000001, 35.0, 2, 15);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.000002, 35.0, 3, 15);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.000003, 35.0, 4, 15);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.000004, 35.0, 5, 15);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.000005, 35.0, 6, 15);
|
||||
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
TrackPointAssert a = new TrackPointAssert()
|
||||
.ignoreTime();
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(1)
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45.0005)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(6)
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL)
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
), trackPoints);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testOnLocationChangedAsync_idle() throws Exception {
|
||||
// given
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
service.setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 1, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 2, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 3, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 4, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 5, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 6, 0);
|
||||
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
TrackPointAssert a = new TrackPointAssert()
|
||||
.ignoreTime();
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(1)
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT) //TODO Why is this added? Systems is idle and not moving at all.
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(2)
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(6)
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL)
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
), trackPoints);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testOnLocationChangedAsync_idle_withMovement() throws Exception {
|
||||
// given
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
service.setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 1, 15);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 2, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 3, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 4, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 5, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 6, 15);
|
||||
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
TrackPointAssert a = new TrackPointAssert()
|
||||
.ignoreTime();
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(1)
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT) //TODO Check why this trackPoint is inserted.
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(2)
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT) //TODO Check why this trackPoint is inserted.
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(5)
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(6)
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL)
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
), trackPoints);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testOnLocationChangedAsync_idle_withSensorData() throws Exception {
|
||||
// given
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
service.setAltitudeSumManager(altitudeSumManager);
|
||||
service.setRemoteSensorManager(new BluetoothRemoteSensorManager(context) {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fill(@NonNull TrackPoint trackPoint) {
|
||||
SensorDataSet sensorDataSet = new SensorDataSet();
|
||||
sensorDataSet.set(new SensorDataHeartRate("sensorName", "sensorAddress", 5f));
|
||||
sensorDataSet.fillTrackPoint(trackPoint);
|
||||
}
|
||||
});
|
||||
|
||||
// when
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 1, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 2, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 3, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 4, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 5, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 6, 0);
|
||||
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
TrackPointAssert a = new TrackPointAssert()
|
||||
.ignoreTime();
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(1)
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setHeartRate_bpm(5f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(2)
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setHeartRate_bpm(5f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(3)
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setHeartRate_bpm(5f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(4)
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setHeartRate_bpm(5f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(5)
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setHeartRate_bpm(5f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(6)
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setHeartRate_bpm(5f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL)
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setHeartRate_bpm(5f)
|
||||
), trackPoints);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testOnLocationChangedAsync_segment() throws Exception {
|
||||
// given
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
service.setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 1, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.1, 35.0, 2, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.1, 35.0, 3, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.2, 35.0, 4, 0);
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.2, 35.0, 5, 0);
|
||||
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
|
||||
TrackPointAssert a = new TrackPointAssert()
|
||||
.ignoreTime();
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(1)
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC)
|
||||
.setLatitude(45.1)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(2)
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45.1)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(3)
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC)
|
||||
.setLatitude(45.2)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(4)
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLatitude(45.2)
|
||||
.setLongitude(35)
|
||||
.setAccuracy(5)
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL)
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
), trackPoints);
|
||||
}
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
package de.dennisguse.opentracks.services;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.MediumTest;
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Speed;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests for the track recording service, which require a {@link Looper}.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
* <p>
|
||||
* ATTENTION: This tests deletes all stored tracks in the database.
|
||||
* So, if it is executed on a real device, data might be lost.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TrackRecordingServiceTestLooper {
|
||||
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private final SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
@BeforeClass
|
||||
public static void preSetUp() {
|
||||
// Prepare looper for Android's message queue
|
||||
if (Looper.myLooper() == null) Looper.prepare();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void finalTearDown() {
|
||||
if (Looper.myLooper() != null) Looper.myLooper().quit();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
// Set up the mock content resolver
|
||||
ContentProvider customContentProvider = new CustomContentProvider() {
|
||||
};
|
||||
customContentProvider.attachInfo(context, null);
|
||||
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
// Let's use default values.
|
||||
sharedPreferences.edit().clear().apply();
|
||||
|
||||
// Ensure that the database is empty before every test
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws TimeoutException {
|
||||
// Reset service (if some previous test failed)
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context)))
|
||||
.getService();
|
||||
if (service.isRecording() || service.isPaused()) {
|
||||
service.endCurrentTrack();
|
||||
}
|
||||
|
||||
// Ensure that the database is empty after every test
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRequiredAccuracy() throws TimeoutException {
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||
editor.putInt(context.getString(R.string.recording_gps_accuracy_key), 500);
|
||||
editor.commit();
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_voiceFrequencyDefault() throws TimeoutException {
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||
editor.putInt(context.getString(R.string.voice_frequency_key), Integer.parseInt(context.getResources().getString(R.string.voice_frequency_default)));
|
||||
editor.commit();
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_voiceFrequencyByDistance() throws TimeoutException {
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||
editor.putInt(context.getString(R.string.voice_frequency_key), -1);
|
||||
editor.commit();
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_voiceFrequencyByTime() throws TimeoutException {
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||
editor.putInt(context.getString(R.string.voice_frequency_key), 1);
|
||||
editor.commit();
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_maxRecordingDistanceDefault() throws TimeoutException {
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||
editor.putInt(context.getString(R.string.max_recording_distance_key), Integer.parseInt(context.getResources().getString(R.string.max_recording_distance_default)));
|
||||
editor.commit();
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_maxRecordingDistance() throws TimeoutException {
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||
editor.putInt(context.getString(R.string.max_recording_distance_key), 50);
|
||||
editor.commit();
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRecordingDistanceDefault() throws TimeoutException {
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||
editor.putInt(context.getString(R.string.recording_distance_interval_key), Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default)));
|
||||
editor.commit();
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRecordingDistance() throws TimeoutException {
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||
editor.putInt(context.getString(R.string.recording_distance_interval_key), 2);
|
||||
editor.commit();
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_metricUnitsDefault() throws TimeoutException {
|
||||
PreferencesUtils.setString(sharedPreferences, context, R.string.stats_units_key, context.getString(R.string.stats_units_default));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_metricUnitsDisabled() throws TimeoutException {
|
||||
PreferencesUtils.setString(sharedPreferences, context, R.string.stats_units_key, context.getString(R.string.stats_units_imperial));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRecordingIntervalDefault() throws TimeoutException {
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||
editor.putInt(context.getString(R.string.min_recording_interval_key), Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default)));
|
||||
editor.commit();
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRecordingInterval() throws TimeoutException {
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||
editor.putInt(context.getString(R.string.min_recording_interval_key), 2);
|
||||
editor.commit();
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRequiredAccuracyDefault() throws TimeoutException {
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||
editor.putInt(context.getString(R.string.recording_gps_accuracy_key), Integer.parseInt(context.getResources().getString(R.string.recording_gps_accuracy_default)));
|
||||
editor.commit();
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testIntegration_completeRecordingSession() throws TimeoutException {
|
||||
List<Track> tracks = contentProviderUtils.getTracks();
|
||||
assertTrue(tracks.isEmpty());
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
private void fullRecordingSession() throws TimeoutException {
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context)))
|
||||
.getService();
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
// Start a track.
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
assertNotNull(trackId);
|
||||
assertTrue(service.isRecording());
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
assertNotNull(track);
|
||||
assertEquals(trackId, track.getId());
|
||||
|
||||
// Insert a few points, markers and statistics.
|
||||
for (int i = 0; i < 30; i++) {
|
||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT)
|
||||
.setLongitude(35.0f + i / 10.0f)
|
||||
.setLatitude(45.0f - i / 5.0f)
|
||||
.setAccuracy(5)
|
||||
.setSpeed(Speed.of(10))
|
||||
.setBearing(3.0f);
|
||||
|
||||
int prefAccuracy = PreferencesUtils.getRecordingGPSAccuracy(sharedPreferences, context);
|
||||
service.newTrackPoint(trackPoint, prefAccuracy);
|
||||
|
||||
if (i % 7 == 0) {
|
||||
service.insertMarker(null, null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the track. Validate if it has correct data.
|
||||
service.endCurrentTrack();
|
||||
assertFalse(service.isRecording());
|
||||
track = contentProviderUtils.getTrack(trackId);
|
||||
assertNotNull(track);
|
||||
assertEquals(trackId, track.getId());
|
||||
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||
assertNotNull(trackStatistics);
|
||||
assertTrue(trackStatistics.getStartTime().isAfter(Instant.ofEpochMilli(0)));
|
||||
assertTrue(trackStatistics.getStopTime().isAfter(trackStatistics.getStartTime()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2010 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.services;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.MediumTest;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Marker;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TrackRecordingServiceTestMarker {
|
||||
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
private TrackRecordingService service;
|
||||
|
||||
@BeforeClass
|
||||
public static void preSetUp() {
|
||||
// Prepare looper for Android's message queue
|
||||
if (Looper.myLooper() == null) Looper.prepare();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void finalTearDown() {
|
||||
if (Looper.myLooper() != null) Looper.myLooper().quit();
|
||||
}
|
||||
|
||||
private TrackRecordingService startService() throws TimeoutException {
|
||||
Intent startIntent = new Intent(context, TrackRecordingService.class);
|
||||
return ((TrackRecordingService.Binder) mServiceRule.bindService(startIntent))
|
||||
.getService();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws TimeoutException {
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
service = startService();
|
||||
tearDown();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws TimeoutException {
|
||||
TrackRecordingServiceTestUtils.resetService(mServiceRule, context);
|
||||
|
||||
// Ensure that the database is empty after every test
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void notRecording_testInsertMarker() {
|
||||
// given
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
// when
|
||||
Marker.Id markerId = service.insertMarker(null, null, null, null);
|
||||
|
||||
// then
|
||||
assertNull(markerId);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void recording_noGPSfix_cannotCreateMarker() {
|
||||
// given
|
||||
service.startNewTrack();
|
||||
|
||||
// when
|
||||
Marker.Id markerId = service.insertMarker(null, null, null, null);
|
||||
|
||||
// then
|
||||
assertNull(markerId);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void recording_GPSfix_createsMarker() {
|
||||
// given
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.stopGPS();
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:02Z");
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
service.stopUpdateRecordingData();
|
||||
|
||||
assertTrue(service.isRecording());
|
||||
trackPointCreator.onNewTrackPoint(
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:03Z"))
|
||||
.setLatitude(10)
|
||||
.setLongitude(10)
|
||||
);
|
||||
|
||||
// when
|
||||
Marker.Id markerId = service.insertMarker(null, null, null, null);
|
||||
|
||||
// then
|
||||
assertNotEquals(new Marker.Id(-1L), markerId);
|
||||
Marker wpt = contentProviderUtils.getMarker(markerId);
|
||||
assertEquals(context.getString(R.string.marker_icon_url), wpt.getIcon());
|
||||
assertEquals(context.getString(R.string.marker_name_format, 1), wpt.getName());
|
||||
assertEquals(trackId, wpt.getTrackId());
|
||||
assertEquals(0.0, wpt.getLength().toM(), 0.01);
|
||||
assertNotNull(wpt.getLocation());
|
||||
|
||||
service.endCurrentTrack();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,826 @@
|
||||
package de.dennisguse.opentracks.services;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.MediumTest;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Distance;
|
||||
import de.dennisguse.opentracks.content.data.Speed;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataRunning;
|
||||
import de.dennisguse.opentracks.io.file.importer.TrackPointAssert;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.services.sensors.AltitudeSumManager;
|
||||
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
/**
|
||||
* Tests insert location.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TrackRecordingServiceTestRecording {
|
||||
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
private TrackRecordingService service;
|
||||
|
||||
private final AltitudeSumManager altitudeSumManager = new AltitudeSumManager() {
|
||||
@Override
|
||||
public void fill(@NonNull TrackPoint trackPoint) {
|
||||
trackPoint.setAltitudeGain(0f);
|
||||
trackPoint.setAltitudeLoss(0f);
|
||||
}
|
||||
};
|
||||
|
||||
@BeforeClass
|
||||
public static void preSetUp() {
|
||||
// Prepare looper for Android's message queue
|
||||
if (Looper.myLooper() == null) Looper.prepare();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void finalTearDown() {
|
||||
if (Looper.myLooper() != null) Looper.myLooper().quit();
|
||||
}
|
||||
|
||||
private TrackRecordingService startService() throws TimeoutException {
|
||||
Intent startIntent = new Intent(context, TrackRecordingService.class);
|
||||
return ((TrackRecordingService.Binder) mServiceRule.bindService(startIntent))
|
||||
.getService();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws TimeoutException {
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
tearDown();
|
||||
|
||||
PreferencesUtils.setString(R.string.recording_distance_interval_key, R.string.recording_distance_interval_default);
|
||||
PreferencesUtils.setString(R.string.idle_speed_key, R.string.idle_speed_default);
|
||||
|
||||
service = startService();
|
||||
service.getTrackPointCreator().stopGPS();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws TimeoutException {
|
||||
TrackRecordingServiceTestUtils.resetService(mServiceRule, context);
|
||||
// Ensure that the database is empty after every test
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void recording_startStop() {
|
||||
|
||||
// given
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
|
||||
|
||||
// when
|
||||
String startTime = "2020-02-02T02:02:02Z";
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.stopGPS();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, startTime, 0, 0, 0, 0, null, null)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime))
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
|
||||
|
||||
// when
|
||||
String stopTime = "2020-02-02T02:02:03Z";
|
||||
trackPointCreator.setClock(stopTime);
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, stopTime, 0, 1, 0, 0, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_startPauseResume() {
|
||||
// given
|
||||
String startTime = "2020-02-02T02:02:02Z";
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.stopGPS();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String pauseTime = "2020-02-02T02:02:03Z";
|
||||
trackPointCreator.setClock(pauseTime);
|
||||
service.pauseCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, pauseTime, 0, 1, 0, 0, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(pauseTime))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
|
||||
//when
|
||||
String resumeTime = "2020-02-02T02:02:04Z";
|
||||
trackPointCreator.setClock(resumeTime);
|
||||
service.resumeTrack(trackId);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, resumeTime, 0, 1, 0, 0, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(pauseTime))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(resumeTime))
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_startPauseStop() {
|
||||
// given
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
|
||||
String starTime = "2020-02-02T02:02:02Z";
|
||||
trackPointCreator.setClock(starTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.stopGPS();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
String pauseTime = "2020-02-02T02:02:03Z";
|
||||
trackPointCreator.setClock(pauseTime);
|
||||
service.pauseCurrentTrack();
|
||||
|
||||
// when
|
||||
trackPointCreator.setClock("2020-02-02T02:02:04Z");
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
TrackPointAssert a = new TrackPointAssert();
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(starTime)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(pauseTime))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
), trackPoints);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_startStopResumeStop() {
|
||||
// given
|
||||
String startTime = "2020-02-02T02:02:02Z";
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.stopGPS();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
String stopTime = "2020-02-02T02:02:03Z";
|
||||
trackPointCreator.setClock(stopTime);
|
||||
service.endCurrentTrack();
|
||||
|
||||
// when
|
||||
String resumeTime = "2020-02-02T02:02:04Z";
|
||||
trackPointCreator.setClock(resumeTime);
|
||||
service.resumeTrack(trackId);
|
||||
trackPointCreator.stopGPS();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// then
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(resumeTime))
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_gpsOnly_recordingDistance_above() {
|
||||
// given
|
||||
String startTime = "2020-02-02T02:02:02Z";
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
// when
|
||||
String gps2 = "2020-02-02T02:02:06Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0001, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, gps2, 11.113178253173828f, 4, 3, 15, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
// when
|
||||
String gps3 = "2020-02-02T02:02:08Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.0002, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, gps3, 22.226356506347656, 6, 5, 15, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
|
||||
// when
|
||||
String stopTime = "2020-02-02T02:02:12Z";
|
||||
trackPointCreator.setClock(stopTime);
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, stopTime, 22.226356506347656, 10, 5, 15, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps1))
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps2))
|
||||
.setLatitude(45.0001)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps3))
|
||||
.setLatitude(45.0001)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_gpsOnly_recordingDistance_above_speed_0() {
|
||||
// given
|
||||
String startTime = "2020-02-02T02:02:02Z";
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 0);
|
||||
|
||||
// then
|
||||
TrackStatistics gps1statistics = new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f);
|
||||
assertEquals(gps1statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
// when
|
||||
String gps2 = "2020-02-02T02:02:06Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0001, 35.0, 1, 0);
|
||||
|
||||
// then
|
||||
assertEquals(gps1statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
// when
|
||||
String gps3 = "2020-02-02T02:02:08Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.0002, 35.0, 1, 0);
|
||||
|
||||
|
||||
// then
|
||||
assertEquals(gps1statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
// when
|
||||
String stopTime = "2020-02-02T02:02:12Z";
|
||||
trackPointCreator.setClock(stopTime);
|
||||
service.endCurrentTrack();
|
||||
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, stopTime, 0, 10, 0, 0, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps1))
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps3))
|
||||
.setLatitude(45.0001)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_gpsOnly_recordingDistance_below() {
|
||||
// given
|
||||
String startTime = "2020-02-02T02:02:02Z";
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
TrackStatistics gps1Statistics = new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f);
|
||||
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
// when
|
||||
String gps2 = "2020-02-02T02:02:06Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.00001, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
// when
|
||||
String gps3 = "2020-02-02T02:02:08Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.00002, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
|
||||
// when
|
||||
String stopTime = "2020-02-02T02:02:12Z";
|
||||
trackPointCreator.setClock(stopTime);
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, stopTime, 2.222635507583618, 10, 5, 15, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps1))
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps3))
|
||||
.setLatitude(45.00002)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_gpsOnly_recordingDistance_idle() {
|
||||
// given
|
||||
String startTime = "2020-02-02T02:02:02Z";
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 0);
|
||||
|
||||
// then
|
||||
TrackStatistics gps1Statistics = new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f);
|
||||
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
// when
|
||||
String gps2 = "2020-02-02T02:02:06Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0, 35.0, 1, 0);
|
||||
|
||||
// then
|
||||
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
// when
|
||||
String gps3 = "2020-02-02T02:02:08Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.0, 35.0, 1, 0);
|
||||
|
||||
// then
|
||||
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
|
||||
// when
|
||||
String stopTime = "2020-02-02T02:02:12Z";
|
||||
trackPointCreator.setClock(stopTime);
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, stopTime, 0, 10, 0, 0, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps1))
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps3))
|
||||
.setLatitude(45.00002)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_gpsOnly_recordingDistance_idle_movement() {
|
||||
// given
|
||||
String startTime = "2020-02-02T02:02:02Z";
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
// when
|
||||
String gps2 = "2020-02-02T02:02:06Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0, 35.0, 1, 0);
|
||||
|
||||
// then
|
||||
final TrackStatistics gps2statistics = new TrackStatistics(startTime, gps2, 0, 4, 0, 0, 0f, 0f);
|
||||
assertEquals(gps2statistics
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
// when
|
||||
String gps3 = "2020-02-02T02:02:08Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.0, 35.0, 1, 0);
|
||||
|
||||
// then
|
||||
assertEquals(gps2statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
|
||||
// when
|
||||
String gps4 = "2020-02-02T02:02:10Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps4, 45.0, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, gps4, 0, 8, 0, 0, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
// when
|
||||
String stopTime = "2020-02-02T02:02:12Z";
|
||||
trackPointCreator.setClock(stopTime);
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, stopTime, 0, 10, 0, 0, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps1))
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps2))
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps3))
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps4))
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_gpsOnly_ignore_inaccurate() {
|
||||
// given
|
||||
String startTime = "2020-02-02T02:02:02Z";
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 100, 15);
|
||||
|
||||
// then
|
||||
TrackStatistics startStatistics = new TrackStatistics(startTime, startTime, 0, 0, 0, 0, null, null);
|
||||
assertEquals(startStatistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
|
||||
// when
|
||||
String gps2 = "2020-02-02T02:02:06Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.1, 35.0, 100, 15);
|
||||
|
||||
// then
|
||||
assertEquals(startStatistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
|
||||
// when
|
||||
String stopTime = "2020-02-02T02:02:12Z";
|
||||
trackPointCreator.setClock(stopTime);
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, stopTime, 0, 10, 0, 0, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
|
||||
// then
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_gpsOnly_segment() {
|
||||
// given
|
||||
String startTime = "2020-02-02T02:02:02Z";
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f), contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
// when
|
||||
String gps2 = "2020-02-02T02:02:06Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.1, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, gps2, 0, 1, 0, 0, 0f, 0f), contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
|
||||
// when
|
||||
String stopTime = "2020-02-02T02:02:12Z";
|
||||
trackPointCreator.setClock(stopTime);
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, stopTime, 0, 7, 0, 0, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
|
||||
// then
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps1))
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse(gps2))
|
||||
.setLatitude(45.1)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that GPS-based TrackPoints are stored, if the distance to the previous GPS-based TrackPoint is greater than recordingDistanceInterval.
|
||||
*/
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_gpsAndSensor_gpsIdleMoving_sensorMoving() {
|
||||
// TODO Check TrackStatistics
|
||||
AltitudeSumManager altitudeSumManager = new AltitudeSumManager();
|
||||
|
||||
// given
|
||||
String startTime = "2020-02-02T02:02:02Z";
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
BluetoothRemoteSensorManager remoteSensorManager = trackPointCreator.getRemoteSensorManager();
|
||||
|
||||
// when
|
||||
String sensor1 = "2020-02-02T02:02:03Z";
|
||||
trackPointCreator.setClock(sensor1);
|
||||
remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(0))); //Should be ignored
|
||||
|
||||
// when
|
||||
String sensor2 = "2020-02-02T02:02:04Z";
|
||||
trackPointCreator.setClock(sensor2);
|
||||
remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(2))); //TODO Should be ignored; distance should be added to the next TrackPoint
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:05Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||
|
||||
// when
|
||||
String sensor3 = "2020-02-02T02:02:06Z";
|
||||
trackPointCreator.setClock(sensor3);
|
||||
remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(12)));
|
||||
|
||||
// when
|
||||
String sensor4 = "2020-02-02T02:02:07Z";
|
||||
trackPointCreator.setClock(sensor4);
|
||||
remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(14))); //Should be ignored
|
||||
|
||||
// when
|
||||
String gps2 = "2020-02-02T02:02:08Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0, 35.0, 4, 15); //Should be ignored
|
||||
|
||||
// when
|
||||
String sensor5 = "2020-02-02T02:02:10Z";
|
||||
trackPointCreator.setClock(sensor5);
|
||||
remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(16)));
|
||||
|
||||
// when
|
||||
String gps3 = "2020-02-02T02:02:12Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.001, 35.0, 1, 15);
|
||||
|
||||
// when
|
||||
String gps4 = "2020-02-02T02:02:14Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps4, 45.001, 35.0, 1, 15);
|
||||
|
||||
|
||||
// when
|
||||
String stopTime = "2020-02-02T02:02:16Z";
|
||||
trackPointCreator.setClock(stopTime);
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.SENSORPOINT, Instant.parse(sensor2)) // TODO Should be ignored; is stored as it assumed to be first in current segment.
|
||||
.setSpeed(Speed.of(5))
|
||||
.setSensorDistance(Distance.of(2)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps1))
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(5))
|
||||
.setSensorDistance(Distance.of(0)),
|
||||
new TrackPoint(TrackPoint.Type.SENSORPOINT, Instant.parse(sensor3))
|
||||
.setSpeed(Speed.of(5))
|
||||
.setSensorDistance(Distance.of(10)),
|
||||
/*
|
||||
//TODO BUG Should be stored, but sensorDistance is used instead of distance to previous TRACKPOINT.
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps3))
|
||||
.setLatitude(45.001)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(5))
|
||||
.setSensorDistance(Distance.of(2)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps4))
|
||||
.setLatitude(45.001)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(5))
|
||||
.setSensorDistance(Distance.of(2)),
|
||||
*/
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps4))
|
||||
.setLatitude(45.001)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(5))
|
||||
.setSensorDistance(Distance.of(4)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setSensorDistance(Distance.of(11))
|
||||
.setSpeed(Speed.of(5))
|
||||
.setSensorDistance(Distance.of(0))
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright 2010 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.services;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.MediumTest;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.services.handlers.GpsStatusValue;
|
||||
|
||||
/**
|
||||
* Testing the states of TrackRecordingService.
|
||||
* As states are checked against MutableLiveData that are updated via `postValue()` (is asynchronous to be inform other Threads), Thread.sleep() is required.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TrackRecordingServiceTestStateMachine {
|
||||
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
private TrackRecordingService service;
|
||||
|
||||
@BeforeClass
|
||||
public static void preSetUp() {
|
||||
// Prepare looper for Android's message queue
|
||||
if (Looper.myLooper() == null) Looper.prepare();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void finalTearDown() {
|
||||
if (Looper.myLooper() != null) Looper.myLooper().quit();
|
||||
}
|
||||
|
||||
|
||||
private TrackRecordingService startService() throws TimeoutException {
|
||||
Intent startIntent = new Intent(context, TrackRecordingService.class);
|
||||
return ((TrackRecordingService.Binder) mServiceRule.bindService(startIntent))
|
||||
.getService();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws TimeoutException {
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
service = startService();
|
||||
tearDown();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws TimeoutException {
|
||||
TrackRecordingServiceTestUtils.resetService(mServiceRule, context);
|
||||
// Ensure that the database is empty after every test
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void initialState() {
|
||||
// given
|
||||
List<Track> tracks = contentProviderUtils.getTracks();
|
||||
assertTrue(tracks.isEmpty());
|
||||
|
||||
// when
|
||||
// noop
|
||||
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
||||
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void gps_startStop() throws InterruptedException {
|
||||
// given
|
||||
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
||||
|
||||
// when
|
||||
service.tryStartGps();
|
||||
Thread.sleep(1000);
|
||||
|
||||
// then
|
||||
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
||||
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
||||
|
||||
// when
|
||||
service.stopGps(true);
|
||||
Thread.sleep(1000);
|
||||
|
||||
// then
|
||||
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
||||
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
// assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue()); TODO BUG: GPS can only be stopped if a service is recording.
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void recording_startPauseStopResume_no_data() throws InterruptedException {
|
||||
// given
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
// when
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
Thread.sleep(1000);
|
||||
|
||||
// then
|
||||
assertTrue(service.isRecording());
|
||||
assertEquals(new TrackRecordingService.RecordingStatus(trackId, false), service.getRecordingStatusObservable().getValue());
|
||||
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
||||
|
||||
|
||||
// when
|
||||
service.pauseCurrentTrack();
|
||||
Thread.sleep(1000);
|
||||
|
||||
// then
|
||||
assertTrue(service.isRecording());
|
||||
assertTrue(service.isPaused());
|
||||
assertEquals(new TrackRecordingService.RecordingStatus(trackId, true), service.getRecordingStatusObservable().getValue());
|
||||
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue()); //TODO The GPS is kept in pause, right? Might be a bug.
|
||||
|
||||
|
||||
// when
|
||||
service.resumeCurrentTrack();
|
||||
Thread.sleep(1000);
|
||||
|
||||
// then
|
||||
assertTrue(service.isRecording());
|
||||
assertFalse(service.isPaused());
|
||||
assertEquals(new TrackRecordingService.RecordingStatus(trackId, false), service.getRecordingStatusObservable().getValue());
|
||||
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
||||
|
||||
|
||||
// when
|
||||
service.endCurrentTrack();
|
||||
Thread.sleep(1000);
|
||||
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
assertFalse(service.isPaused());
|
||||
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
||||
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
||||
|
||||
|
||||
// when
|
||||
service.resumeTrack(trackId);
|
||||
Thread.sleep(1000);
|
||||
|
||||
// then
|
||||
assertTrue(service.isRecording());
|
||||
assertFalse(service.isPaused());
|
||||
assertEquals(new TrackRecordingService.RecordingStatus(trackId, false), service.getRecordingStatusObservable().getValue());
|
||||
Thread.sleep(1000); //TODO Figure out why we need to wait here until the update is happening
|
||||
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void invalidStateChanges() throws InterruptedException {
|
||||
// given
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
// when
|
||||
service.pauseCurrentTrack();
|
||||
Thread.sleep(1000);
|
||||
|
||||
// then
|
||||
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
||||
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
||||
|
||||
|
||||
// when
|
||||
service.resumeCurrentTrack();
|
||||
Thread.sleep(1000);
|
||||
|
||||
// then
|
||||
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
||||
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
||||
|
||||
|
||||
// when
|
||||
service.endCurrentTrack();
|
||||
Thread.sleep(1000);
|
||||
|
||||
// then
|
||||
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
||||
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
||||
}
|
||||
|
||||
@Ignore("TODO Bug: GPS can be stopped although the current track is recording")
|
||||
@MediumTest
|
||||
@Test
|
||||
public void recording_stopGPS_noop() throws InterruptedException {
|
||||
// given
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
||||
|
||||
// when
|
||||
service.stopGps(false); //TODO Should be ignored as service is recording
|
||||
Thread.sleep(1000);
|
||||
|
||||
// then
|
||||
assertTrue(service.isRecording());
|
||||
assertFalse(service.isPaused());
|
||||
assertEquals(new TrackRecordingService.RecordingStatus(trackId, false), service.getRecordingStatusObservable().getValue());
|
||||
Thread.sleep(1000); //TODO Figure out why we need to wait here until the update is happening
|
||||
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void recording_startRecording_alreadyRecording() throws InterruptedException {
|
||||
// given
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
assertTrue(service.isRecording());
|
||||
|
||||
// when
|
||||
Track.Id newTrackId = service.startNewTrack();
|
||||
Thread.sleep(1000);
|
||||
|
||||
// then
|
||||
assertNotNull(trackId);
|
||||
assertNull(newTrackId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package de.dennisguse.opentracks.services;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.location.Location;
|
||||
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
public class TrackRecordingServiceTestUtils {
|
||||
|
||||
|
||||
//TODO Workaround as service is not stopped on API23; thus sharedpreferences are not reset between tests.
|
||||
//TODO Anyhow, the service should re-create all it's resources if a recording starts and makes sure that there is no leftovers from previous recordings.
|
||||
@Deprecated
|
||||
public static void resetService(ServiceTestRule mServiceRule, Context context) throws TimeoutException {
|
||||
// Let's use default values.
|
||||
PreferencesUtils.clear();
|
||||
|
||||
// Reset service (if some previous test failed)
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)))
|
||||
.getService();
|
||||
|
||||
service.getTrackPointCreator().setRemoteSensorManager(new BluetoothRemoteSensorManager(context, service.getTrackPointCreator()));
|
||||
service.getTrackPointCreator().setClock(Clock.systemUTC());
|
||||
service.endCurrentTrack();
|
||||
service.sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
||||
}
|
||||
|
||||
static void sendGPSLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, long speed) {
|
||||
Location location = new Location("mock");
|
||||
location.setTime(1L); // Should be ignored anyhow.
|
||||
location.setLatitude(latitude);
|
||||
location.setLongitude(longitude);
|
||||
location.setAccuracy(accuracy);
|
||||
location.setSpeed(speed);
|
||||
|
||||
trackPointCreator.setClock(time);
|
||||
trackPointCreator.getGpsHandler().onLocationChanged(location);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package de.dennisguse.opentracks.services.handlers;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.location.Location;
|
||||
import android.os.Looper;
|
||||
|
||||
@@ -13,27 +16,25 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import java.time.Instant;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import de.dennisguse.opentracks.content.data.Distance;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class LocationHandlerTest {
|
||||
public class GPSHandlerTest {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private final SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
|
||||
@Mock
|
||||
private HandlerServer handlerServer;
|
||||
private TrackPointCreator trackPointCreator;
|
||||
|
||||
@InjectMocks
|
||||
private LocationHandler locationHandler;
|
||||
private GPSHandler locationHandler;
|
||||
|
||||
@BeforeClass
|
||||
public static void preSetUp() {
|
||||
@@ -44,10 +45,11 @@ public class LocationHandlerTest {
|
||||
@Before
|
||||
public void setUp() {
|
||||
// Let's use default values.
|
||||
sharedPreferences.edit().clear().commit();
|
||||
PreferencesUtils.clear();
|
||||
|
||||
Mockito.when(trackPointCreator.createNow())
|
||||
.thenReturn(Instant.now());
|
||||
|
||||
locationHandler.onSharedPreferenceChanged(context, sharedPreferences, context.getString(R.string.recording_gps_accuracy_key));
|
||||
locationHandler.onSharedPreferenceChanged(context, sharedPreferences, context.getString(R.string.min_recording_interval_key));
|
||||
locationHandler.onStart(context);
|
||||
}
|
||||
|
||||
@@ -60,7 +62,7 @@ public class LocationHandlerTest {
|
||||
locationHandler.onLocationChanged(createLocation(45f, 35f, 3, 5, System.currentTimeMillis()));
|
||||
|
||||
// then
|
||||
verify(handlerServer, times(1)).sendTrackPoint(any(TrackPoint.class), any(Integer.class));
|
||||
verify(trackPointCreator, times(1)).onNewTrackPoint(any(TrackPoint.class));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,7 +78,7 @@ public class LocationHandlerTest {
|
||||
locationHandler.onLocationChanged(createLocation(latitude, 35f, 3, 5, System.currentTimeMillis()));
|
||||
|
||||
// then
|
||||
verify(handlerServer, times(0)).sendTrackPoint(any(TrackPoint.class), any(Integer.class));
|
||||
verify(trackPointCreator, times(0)).onNewTrackPoint(any(TrackPoint.class));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,14 +87,14 @@ public class LocationHandlerTest {
|
||||
@Test
|
||||
public void testOnLocationChanged_poorAccuracy() {
|
||||
// given
|
||||
int prefAccuracy = PreferencesUtils.getRecordingGPSAccuracy(sharedPreferences, context);
|
||||
Distance prefAccuracy = PreferencesUtils.getThresholdHorizontalAccuracy();
|
||||
|
||||
// when
|
||||
locationHandler.onLocationChanged(createLocation(45f, 35f, prefAccuracy + 1, 5, System.currentTimeMillis()));
|
||||
locationHandler.onLocationChanged(createLocation(45f, 35f, (float) (prefAccuracy.toM() + 1), 5, System.currentTimeMillis()));
|
||||
|
||||
// then
|
||||
// no newTrackPoint called
|
||||
verify(handlerServer, times(0)).sendTrackPoint(any(TrackPoint.class), any(Integer.class));
|
||||
verify(trackPointCreator, times(0)).onNewTrackPoint(any(TrackPoint.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -105,7 +107,7 @@ public class LocationHandlerTest {
|
||||
locationHandler.onLocationChanged(createLocation(99.0, 35.0, Long.MAX_VALUE, 15, System.currentTimeMillis()));
|
||||
|
||||
// then
|
||||
verify(handlerServer, times(1)).sendTrackPoint(any(TrackPoint.class), any(Integer.class));
|
||||
verify(trackPointCreator, times(1)).onNewTrackPoint(any(TrackPoint.class));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1,67 +1,57 @@
|
||||
package de.dennisguse.opentracks.services.handlers;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.Distance;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class HandlerServerTest {
|
||||
public class TrackPointCreatorTest {
|
||||
|
||||
@Mock
|
||||
private Context context;
|
||||
|
||||
@Mock
|
||||
private HandlerServer.HandlerServerInterface server;
|
||||
private TrackPointCreator.Callback server;
|
||||
|
||||
@Mock
|
||||
private LocationHandler locationHandler;
|
||||
private GPSHandler locationHandler;
|
||||
|
||||
@Mock
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private HandlerServer subject;
|
||||
private TrackPointCreator subject;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
subject = new HandlerServer(locationHandler, server);
|
||||
subject = new TrackPointCreator(locationHandler, server);
|
||||
subject.start(context);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
subject.stop(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSharedPreferenceChanged() {
|
||||
// when
|
||||
subject.onSharedPreferenceChanged(context, sharedPreferences, null);
|
||||
|
||||
// then
|
||||
verify(locationHandler).onSharedPreferenceChanged(context, sharedPreferences, null);
|
||||
subject.stop();
|
||||
}
|
||||
|
||||
@Ignore("ServiceExecutor disabled for #822")
|
||||
@Test
|
||||
public void sendTrackPoint() throws InterruptedException {
|
||||
// given
|
||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT);
|
||||
int accuracy = 50;
|
||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, null);
|
||||
Distance horizontalAccuracyThreshold = Distance.of(50);
|
||||
|
||||
// when
|
||||
subject.sendTrackPoint(trackPoint, accuracy);
|
||||
subject.onNewTrackPoint(trackPoint);
|
||||
|
||||
// then
|
||||
Thread.sleep(10); // Wait for executor service
|
||||
verify(server).newTrackPoint(trackPoint, accuracy);
|
||||
verify(server).newTrackPoint(trackPoint, horizontalAccuracyThreshold);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.dennisguse.opentracks.util;
|
||||
package de.dennisguse.opentracks.services.tasks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Pair;
|
||||
@@ -6,6 +6,7 @@ import android.util.Pair;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -18,7 +19,9 @@ import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.services.tasks.AnnouncementUtils;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -68,7 +71,7 @@ public class AnnouncementUtilsTest {
|
||||
String announcement = AnnouncementUtils.getAnnouncement(context, stats, true, true, lastInterval);
|
||||
|
||||
// then
|
||||
assertEquals(
|
||||
Assert.assertEquals(
|
||||
"total distance " +
|
||||
StringUtils.getDistanceParts(context, stats.getTotalDistance(), true).first +
|
||||
" kilometers in " + buildAndGetTimeText(stats.getTotalTime(), false) + " at " +
|
||||
@@ -15,6 +15,7 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
@@ -25,7 +26,7 @@ import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PeriodicTaskExecutorTest {
|
||||
public class VoiceAnnouncementManagerTest {
|
||||
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
@@ -53,17 +54,38 @@ public class PeriodicTaskExecutorTest {
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)))
|
||||
.getService();
|
||||
|
||||
PeriodicTaskExecutor periodicTaskExecutor = new PeriodicTaskExecutor(service, new AnnouncementPeriodicTask.Factory());
|
||||
periodicTaskExecutor.setMetricUnits(true);
|
||||
periodicTaskExecutor.setTaskFrequency(-5);
|
||||
VoiceAnnouncementManager voiceAnnouncementManager = new VoiceAnnouncementManager(service);
|
||||
voiceAnnouncementManager.setFrequency(Distance.ofKilometer(5));
|
||||
|
||||
// when
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
statistics.setTotalDistance(Distance.of(13000));
|
||||
assertEquals(Distance.of(15000), periodicTaskExecutor.calculateNextTaskDistance(statistics));
|
||||
statistics.setTotalDistance(Distance.ofKilometer(13));
|
||||
voiceAnnouncementManager.restore(statistics);
|
||||
assertEquals(Distance.of(15000), voiceAnnouncementManager.getNextTotalDistance());
|
||||
|
||||
statistics.setTotalDistance(Distance.of(15100));
|
||||
assertEquals(Distance.of(20000), periodicTaskExecutor.calculateNextTaskDistance(statistics));
|
||||
voiceAnnouncementManager.restore(statistics);
|
||||
assertEquals(Distance.of(20000), voiceAnnouncementManager.getNextTotalDistance());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void calculateNextTotalTime() throws TimeoutException {
|
||||
// given
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)))
|
||||
.getService();
|
||||
|
||||
VoiceAnnouncementManager voiceAnnouncementManager = new VoiceAnnouncementManager(service);
|
||||
voiceAnnouncementManager.setFrequency(Duration.ofSeconds(5));
|
||||
|
||||
// when
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
statistics.setTotalTime(Duration.ofSeconds(91));
|
||||
voiceAnnouncementManager.restore(statistics);
|
||||
assertEquals(Duration.ofSeconds(95), voiceAnnouncementManager.getNextTotalTime());
|
||||
|
||||
statistics.setTotalTime(Duration.ofSeconds(95));
|
||||
voiceAnnouncementManager.restore(statistics);
|
||||
assertEquals(Duration.ofSeconds(100), voiceAnnouncementManager.getNextTotalTime());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Layout;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PreferencesOpenHelperTest {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private final Resources resources = ApplicationProvider.getApplicationContext().getResources();
|
||||
|
||||
@Test
|
||||
public void test_upgradeFrom0To1_withoutStatsCustomLayouts() {
|
||||
// given the version 0
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.remove(context.getString(R.string.stats_custom_layouts_key));
|
||||
editor.putInt(context.getString(R.string.prefs_last_version_key), 0);
|
||||
editor.commit();
|
||||
|
||||
// when update to version 1
|
||||
PreferencesOpenHelper.newInstance(1).check();
|
||||
|
||||
// then there should be one layout with old custom layout that has the new CSV value.
|
||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
||||
assertNotNull(layouts);
|
||||
assertEquals(layouts.size(), 1);
|
||||
assertEquals(layouts.get(0).toCsv(), PreferencesUtils.getCustomLayout().toCsv());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_upgradeFrom1To2_withOldVersion() {
|
||||
// given the version 1
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
|
||||
String oldCustomLayoutCsv = context.getString(R.string.activity_type_unknown) + ";distance,1,1,0;speed,1,1,0;";
|
||||
editor.putString(context.getString(R.string.stats_custom_layouts_key), oldCustomLayoutCsv);
|
||||
editor.putInt(context.getString(R.string.prefs_last_version_key), 1);
|
||||
|
||||
editor.commit();
|
||||
|
||||
// when update to version 2
|
||||
PreferencesOpenHelper.newInstance(2).check();
|
||||
|
||||
String updatedOldCustomLayoutCsv = context.getString(R.string.activity_type_unknown) + ";"
|
||||
+ PreferencesUtils.getLayoutColumnsByDefault() + ";distance,1,1,0;speed,1,1,0;";
|
||||
|
||||
// then there should be one layout with old custom layout that has the new CSV value.
|
||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
||||
assertNotNull(layouts);
|
||||
assertEquals(layouts.size(), 1);
|
||||
assertEquals(layouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_upgradeFrom1To2_withNewVersion() {
|
||||
// given the version 1
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
|
||||
String oldCustomLayoutCsv = "whatever;3;distance,1,1,0;speed,1,1,0;";
|
||||
editor.putString(context.getString(R.string.stats_custom_layouts_key), oldCustomLayoutCsv);
|
||||
editor.putInt(context.getString(R.string.prefs_last_version_key), 1);
|
||||
|
||||
editor.commit();
|
||||
|
||||
// when update to version 2
|
||||
PreferencesOpenHelper.newInstance(2).check();
|
||||
|
||||
String updatedOldCustomLayoutCsv = "whatever;3;distance,1,1,0;speed,1,1,0;";
|
||||
|
||||
// then there should be one layout with old custom layout that has the new CSV value.
|
||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
||||
assertNotNull(layouts);
|
||||
assertEquals(layouts.size(), 1);
|
||||
assertEquals(layouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_downgrade() {
|
||||
// given version 2
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
|
||||
String customLayoutCsv = "whatever;3;distance,1,1,0;speed,1,1,0;";
|
||||
editor.putString(context.getString(R.string.stats_custom_layouts_key), customLayoutCsv);
|
||||
editor.putInt(context.getString(R.string.prefs_last_version_key), 2);
|
||||
|
||||
editor.commit();
|
||||
|
||||
// when downgrade to version 1
|
||||
PreferencesOpenHelper.newInstance(1).check();
|
||||
|
||||
// then custom layout should be equals to default layout.
|
||||
Layout defaultLayout = Layout.fromCsv(PreferencesUtils.buildDefaultLayout(), resources);
|
||||
List<Layout> customLayout = PreferencesUtils.getAllCustomLayouts();
|
||||
|
||||
assertEquals(customLayout.size(), 1);
|
||||
assertEquals(defaultLayout.toCsv(), customLayout.get(0).toCsv());
|
||||
}
|
||||
}
|
||||