Compare commits
183 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ae2a97576b | |||
| 3e830a086e | |||
| 618a1df3ea | |||
| 90cfc67c59 | |||
| cecc586868 | |||
| f956e7fab3 | |||
| 128b95cd83 | |||
| 895a5c2b2a | |||
| 91ce5cf8d7 | |||
| a283464331 | |||
| 1c3a2bd2e4 | |||
| 0b326a77c2 | |||
| d68008941e | |||
| b822d05a78 | |||
| 74bfed768c | |||
| ebfcce6d8d | |||
| d97d8f85e4 | |||
| 37c2c5029e | |||
| 105ff89fd8 | |||
| 0ac033106c | |||
| 21ec9fc568 | |||
| 2e3a7a31d4 | |||
| ecd7405a8c | |||
| 28abe360cb | |||
| 9cf88c00b0 | |||
| 8bbde4aacd | |||
| 00351136f2 | |||
| 30b85bd215 | |||
| 14b705b43e | |||
| 31fc0adabc | |||
| 1714da5d3b | |||
| 6ae47b5e01 | |||
| df8e4c2e2e | |||
| 6f9fa3719a | |||
| b9014c7219 | |||
| d44ab44620 | |||
| bcffe05744 | |||
| ed60734868 | |||
| e314ff8f43 | |||
| 1108684d06 | |||
| 6c0033c107 | |||
| 8a3e50ef9b | |||
| 05b572253e | |||
| f755a086cd | |||
| b0605323da | |||
| 585c2234eb | |||
| c5d7ea4245 | |||
| 281cd8a861 | |||
| 36400c8ad0 | |||
| fb00f697c5 | |||
| f529726218 | |||
| 059f3d8f43 | |||
| cae716925f | |||
| d29ee6da3d | |||
| 033b5211ea | |||
| 5f4f5baba4 | |||
| 4421046556 | |||
| 30482d5a34 | |||
| f12094c953 | |||
| c5147e7664 | |||
| 03b66bc6a2 | |||
| 7b40f2c949 | |||
| 785a922d03 | |||
| 5d2414977b | |||
| f9b8e46b32 | |||
| c79fc2f353 | |||
| e283e993bb | |||
| 3f1e74e71e | |||
| 300453e3e6 | |||
| ac8eea377e | |||
| cc44e8e9bf | |||
| 3860472e6b | |||
| 69029486b6 | |||
| b407f86d5e | |||
| 731adc7e31 | |||
| 858149e718 | |||
| 34e796f73c | |||
| 3ac71361b8 | |||
| e409b315b4 | |||
| 24eb768707 | |||
| 17ad13d559 | |||
| 3b71212905 | |||
| 16a29c5e31 | |||
| e421164282 | |||
| de01157964 | |||
| 8ef51ec318 | |||
| d2139d70ad | |||
| d1a37366be | |||
| 52d7c8a6cb | |||
| c62e56e5a3 | |||
| 6b89092adf | |||
| 118a56b8d6 | |||
| 7fb993014c | |||
| 09f72af66c | |||
| 815f111ca6 | |||
| 40d2bf0c22 | |||
| a77b7a0626 | |||
| 1a00b4b779 | |||
| 6c5c47240c | |||
| 3c32da970b | |||
| 9cff62c5ba | |||
| 424f93b4da | |||
| 3a23a412c8 | |||
| 9d64989ef1 | |||
| da62d26409 | |||
| a350077e5c | |||
| 36b6cd3c98 | |||
| 203b457ad0 | |||
| 0873b3bd12 | |||
| a7f7e3fca1 | |||
| a52beffef5 | |||
| e6f6d61b84 | |||
| 4a167c9335 | |||
| 16857a9968 | |||
| 094a20c379 | |||
| 0d3cf4ce0d | |||
| 8a2d1c42a8 | |||
| a5b44175a3 | |||
| db5c9479df | |||
| 16f238f34b | |||
| a57b5cd6ee | |||
| 5e54cb8fda | |||
| 0934e7f10c | |||
| 6741ef4d9f | |||
| f2ac1edc4b | |||
| b44bab1ab5 | |||
| 574bd38a35 | |||
| 374959f340 | |||
| 3815ee6c59 | |||
| 5e4924a5e1 | |||
| 8123104f39 | |||
| a9e0c4916d | |||
| 58803c975a | |||
| d7dc59e547 | |||
| 5cf8b67954 | |||
| fb70486bdc | |||
| 18b21a6a3e | |||
| 71288cd9be | |||
| 220545da9f | |||
| 43e9c42915 | |||
| 28944eff07 | |||
| 92f9f94423 | |||
| 8ae268fab2 | |||
| 346264025b | |||
| 0a810e7123 | |||
| 02f3fc1940 | |||
| dd1c5667a0 | |||
| 0f489f1cc9 | |||
| a07f11589b | |||
| 209a320433 | |||
| 8ab9ed0e31 | |||
| 80ca4e647a | |||
| a8de9cb216 | |||
| 12c21b7940 | |||
| 4c5ff546a4 | |||
| 2e0835b2ec | |||
| 18ea8fea87 | |||
| 02a69c8817 | |||
| cab70d665c | |||
| c42f833703 | |||
| 39838232b5 | |||
| 2b346b3f18 | |||
| 7c5115fc56 | |||
| cb71c40350 | |||
| 22bde4af3c | |||
| ecfcc180b9 | |||
| 9c1af67e7e | |||
| 873422540f | |||
| 6cf0b26058 | |||
| a94b227767 | |||
| c820a76102 | |||
| e61b065f2b | |||
| 24d8e976da | |||
| 79e45f7aea | |||
| 72f7b12da6 | |||
| 9973dd05bd | |||
| 41072a8981 | |||
| b1dcdf326c | |||
| c271dd32ac | |||
| 5dfa867034 | |||
| ada0f88538 | |||
| 9e7e8c60a6 | |||
| 1075021e21 |
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
liberapay: OpenTracks
|
liberapay: OpenTracks
|
||||||
github: [rgmf, pstorch, dennisguse]
|
github: [rgmf, pstorch, dennisguse]
|
||||||
custom: https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore
|
custom: [ https://liberapay.com/OpenTracks, https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore ]
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v2.4.0
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: actions/setup-java@v2
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
java-version: '11'
|
java-version: '11'
|
||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Archive
|
- name: Archive
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: OpenTracks-nightly.apk
|
name: OpenTracks-nightly.apk
|
||||||
path: ./build/outputs/apk/nightly/*.apk
|
path: ./build/outputs/apk/nightly/*.apk
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2.4.0
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: actions/setup-java@v2
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
java-version: '11'
|
java-version: '11'
|
||||||
|
|||||||
@@ -10,20 +10,20 @@ jobs:
|
|||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
api-level: [ 23, 30 ]
|
api-level: [ 24, 30 ]
|
||||||
target: [ default ]
|
target: [ default ]
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v2.4.0
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: actions/setup-java@v2
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
java-version: '11'
|
java-version: '11'
|
||||||
|
|
||||||
- name: run tests
|
- name: run tests
|
||||||
uses: reactivecircus/android-emulator-runner@v2.23.0
|
uses: reactivecircus/android-emulator-runner@v2.24.0
|
||||||
with:
|
with:
|
||||||
api-level: ${{ matrix.api-level }}
|
api-level: ${{ matrix.api-level }}
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Archive
|
- name: Archive
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: code-coverage-report
|
name: code-coverage-report
|
||||||
path: build/reports/androidTests/
|
path: build/reports/androidTests/
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ captures/
|
|||||||
.idea/dictionaries
|
.idea/dictionaries
|
||||||
.idea/libraries
|
.idea/libraries
|
||||||
.idea/caches
|
.idea/caches
|
||||||
|
.idea/misc.xml
|
||||||
|
|
||||||
|
|
||||||
# IntelliJ IDEA
|
# IntelliJ IDEA
|
||||||
@@ -59,7 +60,6 @@ captures/
|
|||||||
.idea/encodings.xml
|
.idea/encodings.xml
|
||||||
.idea/jsLibraryMappings.xml
|
.idea/jsLibraryMappings.xml
|
||||||
.idea/libraries/
|
.idea/libraries/
|
||||||
.idea/misc.xml
|
|
||||||
.idea/modules.xml
|
.idea/modules.xml
|
||||||
.idea/scopes/scope_settings.xml
|
.idea/scopes/scope_settings.xml
|
||||||
.idea/shelf/
|
.idea/shelf/
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="DesignSurface">
|
|
||||||
<option name="filePathToZoomLevelMap">
|
|
||||||
<map>
|
|
||||||
<entry key="src/main/res/drawable/ic_activity_bike_24dp.xml" value="0.1765" />
|
|
||||||
<entry key="src/main/res/drawable/ic_activity_sailing_24dp.xml" value="0.1935" />
|
|
||||||
<entry key="src/main/res/drawable/ic_activity_snowboarding_24dp.xml" value="0.1935" />
|
|
||||||
<entry key="src/main/res/drawable/ic_activity_swimming_24dp.xml" value="0.1935" />
|
|
||||||
<entry key="src/main/res/drawable/ic_activity_swimming_open_24dp.xml" value="0.1935" />
|
|
||||||
<entry key="src/main/res/drawable/ic_activity_workout_24dp.xml" value="0.1935" />
|
|
||||||
<entry key="src/main/res/menu/aggregated_statistics.xml" value="0.10208333333333333" />
|
|
||||||
<entry key="src/main/res/xml/settings_gps.xml" value="0.22604166666666667" />
|
|
||||||
<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>
|
|
||||||
<component name="ProjectType">
|
|
||||||
<option name="id" value="Android" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -9,7 +9,6 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
|||||||
<th>Free (F-Droid)</th>
|
<th>Free (F-Droid)</th>
|
||||||
<th>Free (Nightly for F-Droid)</th>
|
<th>Free (Nightly for F-Droid)</th>
|
||||||
<th>Donations</th>
|
<th>Donations</th>
|
||||||
<th>Translations</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
@@ -27,47 +26,26 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
|||||||
<img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg" height="45" align="middle">
|
<img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg" height="45" align="middle">
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
|
||||||
<a href="https://weblate.bubu1.eu/engage/OpenTracks/">
|
|
||||||
<img src="https://weblate.bubu1.eu/widgets/OpenTracks/-/opentracks/svg-badge.svg" alt="Translation status" />
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<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" >
|
<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>
|
<td></td>
|
||||||
<td align="center">
|
|
||||||
<a href="bitcoin:bc1qckpls3lpjlcpyxzm66hcy65hnesas9zgacy6yc">
|
|
||||||
<img alt="bitcoin:bc1qckpls3lpjlcpyxzm66hcy65hnesas9zgacy6yc" src="promotion/donations-bitcoin.png" height="90" align="middle">
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">
|
<a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">
|
||||||
<img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" height="60" align="middle">
|
<img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" height="60" align="middle">
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</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>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<a href="https://weblate.bubu1.eu/engage/OpenTracks/">
|
||||||
|
<img src="https://weblate.bubu1.eu/widgets/OpenTracks/-/opentracks/horizontal-auto.svg" alt="Translation status" />
|
||||||
|
</a>
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<img width="23%" src="fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot1.png">
|
<img width="23%" src="fastlane/metadata/android/en-US/images/phoneScreenshots/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/screenshot2.png">
|
||||||
@@ -130,6 +108,7 @@ The API can be invoked by sending an explicit Intent to start an activity.
|
|||||||
|
|
||||||
`Class`:
|
`Class`:
|
||||||
* **Start a recording:** `de.dennisguse.opentracks.publicapi.StartRecording`
|
* **Start a recording:** `de.dennisguse.opentracks.publicapi.StartRecording`
|
||||||
|
* **Stop a recording:** `de.dennisguse.opentracks.publicapi.StopRecording`
|
||||||
|
|
||||||
For testing via adb: `adb shell am start -n "package/class"`
|
For testing via adb: `adb shell am start -n "package/class"`
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ We do not support type of movement (i.e., walking vs running).
|
|||||||
|
|
||||||
* Polar Stride
|
* Polar Stride
|
||||||
* Stryd (incl. power)
|
* Stryd (incl. power)
|
||||||
|
* Wahoo Tickr X (Cadence appears to be reported in SPM rather than RPM)
|
||||||
|
|
||||||
## Tested Barometric Sensor Smartphones
|
## Tested Barometric Sensor Smartphones
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ set -e
|
|||||||
# Folder where svg screenshots are expected.
|
# Folder where svg screenshots are expected.
|
||||||
SRC_DIR="screenshots-svg/"
|
SRC_DIR="screenshots-svg/"
|
||||||
# Folder where png files will be saved.
|
# Folder where png files will be saved.
|
||||||
DST_DIR="fastlane/metadata/android/en-US/phoneScreenshots/"
|
DST_DIR="fastlane/metadata/android/en-US/images/phoneScreenshots/"
|
||||||
|
|
||||||
echo "Exporting svg files to png..."
|
echo "Exporting svg files to png..."
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.0.4'
|
classpath 'com.android.tools.build:gradle:7.2.1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ android {
|
|||||||
targetCompatibility JavaVersion.VERSION_11
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
}
|
}
|
||||||
|
|
||||||
compileSdkVersion 31
|
compileSdkVersion 32
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding true
|
viewBinding true
|
||||||
@@ -59,13 +59,13 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "de.dennisguse.opentracks"
|
applicationId "de.dennisguse.opentracks"
|
||||||
versionCode 4755
|
versionCode 4938
|
||||||
versionName "v3.25.1"
|
versionName "v4.0.2"
|
||||||
|
|
||||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||||
|
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 31
|
targetSdkVersion 32
|
||||||
|
|
||||||
testInstrumentationRunner "de.dennisguse.opentracks.TestRunner"
|
testInstrumentationRunner "de.dennisguse.opentracks.TestRunner"
|
||||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||||
@@ -119,18 +119,18 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||||
|
|
||||||
implementation 'androidx.appcompat:appcompat:1.4.1'
|
implementation 'androidx.appcompat:appcompat:1.4.2'
|
||||||
implementation 'androidx.preference:preference:1.2.0'
|
implementation 'androidx.preference:preference:1.2.0'
|
||||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||||
implementation 'com.google.android.material:material:1.5.0'
|
implementation 'com.google.android.material:material:1.6.1'
|
||||||
implementation 'com.android.support.constraint:constraint-layout:2.0.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
|
|
||||||
androidTestImplementation 'androidx.test:core:1.4.0'
|
androidTestImplementation 'androidx.test:core:1.4.0'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||||
androidTestImplementation 'androidx.test:rules:1.4.0'
|
androidTestImplementation 'androidx.test:rules:1.4.0'
|
||||||
androidTestImplementation 'androidx.test:runner:1.4.0'
|
androidTestImplementation 'androidx.test:runner:1.4.0'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||||
androidTestImplementation 'org.mockito:mockito-android:4.3.1'
|
androidTestImplementation 'org.mockito:mockito-android:4.6.1'
|
||||||
|
|
||||||
androidTestUtil 'androidx.test:orchestrator:1.4.1'
|
androidTestUtil 'androidx.test:orchestrator:1.4.1'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,4 +29,26 @@
|
|||||||
</xsd:documentation>
|
</xsd:documentation>
|
||||||
</xsd:annotation>
|
</xsd:annotation>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
|
<xsd:element name="accuracy_horizontal"
|
||||||
|
type="xsd:float">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation xml:lang="en">
|
||||||
|
Horizontal accuracy in meters of the current TrackPoint; 68% chance that the actual
|
||||||
|
location is
|
||||||
|
within this radius around the measured location.
|
||||||
|
Only used in GPX.
|
||||||
|
</xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="accuracy_vertical"
|
||||||
|
type="xsd:float">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation xml:lang="en">
|
||||||
|
Vertical accuracy in meters of the current TrackPoint; 68% chance that the actual
|
||||||
|
location is
|
||||||
|
within this radius around the measured location.
|
||||||
|
Only used in GPX.
|
||||||
|
</xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:element>
|
||||||
</xsd:schema>
|
</xsd:schema>
|
||||||
@@ -1,37 +1,119 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg xmlns:cc="http://creativecommons.org/ns#"
|
<svg xmlns:cc="http://creativecommons.org/ns#"
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
enable-background="new 0 0 95.334 100"
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
height="100"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
id="Ebene_1"
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
version="1.1"
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
viewBox="0 0 100 100"
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
width="100"
|
enable-background="new 0 0 95.334 100"
|
||||||
x="0px"
|
height="100"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
id="Ebene_1"
|
||||||
y="0px"
|
version="1.1"
|
||||||
xml:space="preserve"><metadata
|
viewBox="0 0 100 100"
|
||||||
|
width="100"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
xml:space="preserve"
|
||||||
|
sodipodi:docname="LOGO.svg"
|
||||||
|
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1011"
|
||||||
|
id="namedview10"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="7.94"
|
||||||
|
inkscape:cx="50"
|
||||||
|
inkscape:cy="50"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="Ebene_1" /><metadata
|
||||||
id="metadata16"><rdf:RDF><cc:Work
|
id="metadata16"><rdf:RDF><cc:Work
|
||||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata>
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
<defs id="defs14" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata>
|
||||||
<g id="g9"
|
<defs
|
||||||
transform="translate(-1.0593197,1.9067775)">
|
id="defs14"><linearGradient
|
||||||
<linearGradient gradientUnits="userSpaceOnUse"
|
inkscape:collect="always"
|
||||||
id="SVGID_1_"
|
id="linearGradient862"><stop
|
||||||
x1="28.7554"
|
style="stop-color:#ff6d00;stop-opacity:1;"
|
||||||
x2="82.789497"
|
offset="0"
|
||||||
y1="81.535202"
|
id="stop858" /><stop
|
||||||
y2="9.2979002">
|
style="stop-color:#ff6d00;stop-opacity:0;"
|
||||||
<stop style="stop-color:#FBB22F"
|
offset="1"
|
||||||
id="stop2"
|
id="stop860" /></linearGradient><linearGradient
|
||||||
offset="0" />
|
gradientUnits="userSpaceOnUse"
|
||||||
<stop style="stop-color:#F58820"
|
id="SVGID_1_"
|
||||||
id="stop4"
|
x1="28.7554"
|
||||||
offset="1" />
|
x2="82.789497"
|
||||||
|
y1="81.535202"
|
||||||
|
y2="9.2979002"
|
||||||
|
style="fill-opacity:1;fill:url(#linearGradient849)">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#FBB22F;fill-opacity:1;fill:url(#linearGradient849)"
|
||||||
|
id="stop853"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#F58820;fill-opacity:1;fill:url(#linearGradient849)"
|
||||||
|
id="stop855"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient847"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop843" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop845" /></linearGradient><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient847"
|
||||||
|
id="linearGradient849"
|
||||||
|
x1="83.161415"
|
||||||
|
y1="8.9067831"
|
||||||
|
x2="10.194695"
|
||||||
|
y2="92.068085"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient862"
|
||||||
|
id="linearGradient866"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="62.43222"
|
||||||
|
y1="35.349697"
|
||||||
|
x2="9.7942638"
|
||||||
|
y2="93.02298" /></defs>
|
||||||
|
<g
|
||||||
|
id="g9"
|
||||||
|
transform="translate(-1.0593197,1.9067775)"
|
||||||
|
style="fill-opacity:1;fill:url(#linearGradient849)">
|
||||||
|
<linearGradient
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="SVGID_1_-3"
|
||||||
|
x1="28.7554"
|
||||||
|
x2="82.789497"
|
||||||
|
y1="81.535202"
|
||||||
|
y2="9.2979002"
|
||||||
|
style="fill-opacity:1;fill:url(#linearGradient849)">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#FBB22F;fill-opacity:1;fill:url(#linearGradient849)"
|
||||||
|
id="stop2"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#F58820;fill-opacity:1;fill:url(#linearGradient849)"
|
||||||
|
id="stop4"
|
||||||
|
offset="1" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<path style="fill:url(#SVGID_1_)"
|
<path
|
||||||
d="M 83.548,10.927 C 84.255,8.511 83.102,7.649 80.985,9.01 L 11.146,53.921 c -2.117,1.361 -1.795,2.628 0.714,2.815 L 40.865,58.9 c 2.51,0.188 5.291,2.267 6.18,4.621 l 10.282,27.208 c 0.89,2.354 2.195,2.304 2.902,-0.111 z"
|
style="fill:url(#linearGradient866);fill-opacity:1"
|
||||||
id="path7" />
|
d="M 83.548,10.927 C 84.255,8.511 83.102,7.649 80.985,9.01 L 11.146,53.921 c -2.117,1.361 -1.795,2.628 0.714,2.815 L 40.865,58.9 c 2.51,0.188 5.291,2.267 6.18,4.621 l 10.282,27.208 c 0.89,2.354 2.195,2.304 2.902,-0.111 z"
|
||||||
|
id="path7" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
enable-background="new 0 0 95.334 100"
|
||||||
|
height="100"
|
||||||
|
id="Ebene_1"
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 100 100"
|
||||||
|
width="100"
|
||||||
|
x="0px"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
y="0px"
|
||||||
|
xml:space="preserve"><metadata
|
||||||
|
id="metadata16"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata>
|
||||||
|
<defs id="defs14" />
|
||||||
|
<g id="g9"
|
||||||
|
transform="translate(-1.0593197,1.9067775)">
|
||||||
|
<linearGradient gradientUnits="userSpaceOnUse"
|
||||||
|
id="SVGID_1_"
|
||||||
|
x1="28.7554"
|
||||||
|
x2="82.789497"
|
||||||
|
y1="81.535202"
|
||||||
|
y2="9.2979002">
|
||||||
|
<stop style="stop-color:#FBB22F"
|
||||||
|
id="stop2"
|
||||||
|
offset="0" />
|
||||||
|
<stop style="stop-color:#F58820"
|
||||||
|
id="stop4"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<path style="fill:url(#SVGID_1_)"
|
||||||
|
d="M 83.548,10.927 C 84.255,8.511 83.102,7.649 80.985,9.01 L 11.146,53.921 c -2.117,1.361 -1.795,2.628 0.714,2.815 L 40.865,58.9 c 2.51,0.188 5.291,2.267 6.18,4.621 l 10.282,27.208 c 0.89,2.354 2.195,2.304 2.902,-0.111 z"
|
||||||
|
id="path7" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
height="125"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 125 125"
|
||||||
|
width="125"
|
||||||
|
sodipodi:docname="ic_button_done.svg"
|
||||||
|
inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1011"
|
||||||
|
id="namedview9"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.2457711"
|
||||||
|
inkscape:cx="104.26334"
|
||||||
|
inkscape:cy="71.321036"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<defs
|
||||||
|
id="defs6" />
|
||||||
|
<circle
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#ff6d00;stroke-width:5.03185;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
cx="62.5"
|
||||||
|
cy="62.5"
|
||||||
|
id="path12"
|
||||||
|
r="59.984074" />
|
||||||
|
<metadata
|
||||||
|
id="metadata8">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:title></dc:title>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<circle
|
||||||
|
style="fill:#ff6d00;fill-opacity:1;stroke:none;stroke-width:6.91728;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
cx="62.5"
|
||||||
|
cy="62.5"
|
||||||
|
id="path835"
|
||||||
|
r="50" />
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:6.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 38.620483,63.865643 52.899055,78.144215 86.379516,44.663754"
|
||||||
|
id="path1000" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,79 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
height="125"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 125 125"
|
||||||
|
width="125"
|
||||||
|
sodipodi:docname="ic_button_resume.svg"
|
||||||
|
inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1011"
|
||||||
|
id="namedview9"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="3.176"
|
||||||
|
inkscape:cx="85.245821"
|
||||||
|
inkscape:cy="70.9649"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<defs
|
||||||
|
id="defs6" />
|
||||||
|
<circle
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#ff6d00;stroke-width:5.03185;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
cx="62.5"
|
||||||
|
cy="62.5"
|
||||||
|
id="path12"
|
||||||
|
r="59.984074" />
|
||||||
|
<circle
|
||||||
|
style="fill:#ff6d00;fill-opacity:1;stroke:none;stroke-width:6.91728;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
cx="62.5"
|
||||||
|
cy="62.5"
|
||||||
|
id="path835"
|
||||||
|
r="50" />
|
||||||
|
<metadata
|
||||||
|
id="metadata8">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:title></dc:title>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<path
|
||||||
|
sodipodi:type="star"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:6.67087;stroke-linecap:round;stroke-opacity:1"
|
||||||
|
id="path837"
|
||||||
|
sodipodi:sides="3"
|
||||||
|
sodipodi:cx="61.453945"
|
||||||
|
sodipodi:cy="62.5"
|
||||||
|
sodipodi:r1="26.146271"
|
||||||
|
sodipodi:r2="13.073136"
|
||||||
|
sodipodi:arg1="0"
|
||||||
|
sodipodi:arg2="1.0471976"
|
||||||
|
inkscape:flatsided="false"
|
||||||
|
inkscape:rounded="0"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
d="M 87.600216,62.5 67.990513,73.821668 48.38081,85.143335 l -10e-7,-22.643336 10e-7,-22.643334 19.609704,11.321667 z"
|
||||||
|
inkscape:transform-center-x="-6.5365678" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,8 @@
|
|||||||
|
v3.26.0: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Voice announcements are configurable
|
||||||
|
- Voice announcements of heart rate
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Crash due to race condition
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
v3.26.1: OpenTracks
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Contextual action bar didn't overlap toolbar in night theme.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
v3.27.0: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Voice annoucements: add 100m, 200m, and 500m
|
||||||
|
- Interval: add 100m and 500m
|
||||||
|
- Clock shows now also seconds
|
||||||
|
- GPX, KML, KMZ: export and import horizontal accuracy
|
||||||
|
- GPX: export cumulative distance via clustrust extension
|
||||||
|
- CSV: basic export (TrackPoints only; not for backup)
|
||||||
|
- Workaround for Tickr X as it reports SPM instead of RPM
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
- Adhere to recording distance interval;
|
||||||
|
TrackPoints were recorded although they were too close
|
||||||
|
- Prevent crash on stop recording (race condition)
|
||||||
|
- Prevent crash when using multiple sensors (race condition)
|
||||||
|
- Prevent crash when opening OpenTracks via notification
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
v3.28.0: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Configurable filename for export/sharing (default: {starttime}_{name}.{suffix})
|
||||||
|
- Vertical GPS accuracy is now stored and can be exported/imported
|
||||||
|
(API level >= 26).
|
||||||
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
v4.0.0: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- UI refactoring with a Bottom App Bar.
|
||||||
|
- Public API: implemented STOP properly.
|
||||||
|
- Cleanup: use explicit preference identifiers for TrackFileFormat.
|
||||||
|
- Improvements on BLE handler.
|
||||||
|
- Add exception handler and show error activity so users can send reports about bugs.
|
||||||
|
- Android 13: launcher icon, provide a themed icon.
|
||||||
|
- Change track title capitalization to sentences.
|
||||||
|
- Translations updated.
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Sharing track added file suffix twice.
|
||||||
|
- Unit system not loaded correctly.
|
||||||
|
- File format template was not loaded (NPE).
|
||||||
|
- Public API: respect post-workout export.
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- Move to androix.constraintlayout.
|
||||||
|
- UI tests slightly more robust.
|
||||||
|
- Refactor permission request.
|
||||||
|
- Upgrade to gradlew 7.4.2
|
||||||
|
- Bump material from 1.5.0 to 1.6.0
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
v4.0.1: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Voice announcements are now configurable
|
||||||
|
- Fullscreen mode now also hides Android's navigation bar
|
||||||
|
- Connection to BLE sensors should now be faster
|
||||||
|
- Allow to reset settings while recording
|
||||||
|
- Allow to change import/export settings while recording
|
||||||
|
- KML/KMZ: do not use icons to be loaded from Google
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- TrackStoppedActivity now works on smaller screens
|
||||||
|
- Requests permissions on track list
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
v4.0.2: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Add nautical miles
|
||||||
|
- Settings can be reset while recording
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Voice announcement rounding
|
||||||
|
- Chart: elevation was weirdly smoothed
|
||||||
|
- Export was not checking if file exists
|
||||||
|
Before Width: | Height: | Size: 303 KiB After Width: | Height: | Size: 280 KiB |
|
Before Width: | Height: | Size: 574 KiB After Width: | Height: | Size: 560 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 812 KiB |
|
Before Width: | Height: | Size: 456 KiB After Width: | Height: | Size: 462 KiB |
|
Before Width: | Height: | Size: 630 KiB After Width: | Height: | Size: 695 KiB |
|
Before Width: | Height: | Size: 416 KiB After Width: | Height: | Size: 467 KiB |
|
Before Width: | Height: | Size: 508 KiB After Width: | Height: | Size: 509 KiB |
|
Before Width: | Height: | Size: 798 KiB After Width: | Height: | Size: 872 KiB |
@@ -0,0 +1,11 @@
|
|||||||
|
v3.20.2 : OpenTracks
|
||||||
|
|
||||||
|
Changements :
|
||||||
|
- Le gain et la perte d'altitude sont des entiers au lieu de floats
|
||||||
|
- Demander la permission avant d'utiliser Bluetooth par un message Toast
|
||||||
|
|
||||||
|
Correction d'un bug :
|
||||||
|
- Définir FLAG_IMMUTABLE pour PendingIntents pour éviter un crash sur Android 12
|
||||||
|
- Le capteur de cadence renvoie 0 au lieu de None lorsque l'utilisateur ne pédale pas.
|
||||||
|
- Le capteur de vitesse renvoie 0 au lieu de la dernière vitesse connue quand c'est arrêté
|
||||||
|
- Avec les capteurs, le temps de déplacement s'arrête lorsqu'il n'est pas en mouvement
|
||||||
@@ -7,5 +7,11 @@ Fonctionnalités :
|
|||||||
* Dénivelé positif et négatif: via capteur de pression barométrique
|
* Dénivelé positif et négatif: via capteur de pression barométrique
|
||||||
* Synthèse vocale
|
* Synthèse vocale
|
||||||
* Prise en charge des capteurs Bluetooth LE : rythme cardiaque, vitesse et distance (vélo), cadence de pédalage (vélo), et capteur de puissance (vélo)
|
* Prise en charge des capteurs Bluetooth LE : rythme cardiaque, vitesse et distance (vélo), cadence de pédalage (vélo), et capteur de puissance (vélo)
|
||||||
* Export : export des traces au format KMZ (photos incluses), KML ou GPX
|
* Export : export des traces au format KMZ (photos incluses), KML ou GPX
|
||||||
* Pas d'accès internet requis : Internet n'est pas nécessaire pour le fonctionnement de l'application
|
* Pas d'accès internet requis : Internet n'est pas nécessaire pour le fonctionnement de l'application
|
||||||
|
* Thème sombre ou claire, avec le respect des paramètres du système
|
||||||
|
* Pas de publicités
|
||||||
|
|
||||||
|
Logiciel Open Source
|
||||||
|
Vous pouvez utiliser, étudier, changer et partager le code source.
|
||||||
|
Licence Apache 2.0
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
OpenTracks - Suivi d'activités sportives respectueux de votre vie privée.
|
Appli de suivi des activités sportives respectueuse de votre vie privée.
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
v3.18.6: OpenTracks
|
||||||
|
|
||||||
|
Wijzigingen:
|
||||||
|
- "Toon op kaart"-functionaliteit via KML in plaats van KMZ
|
||||||
|
- Spraakaankondigingen bevatten geen app naam
|
||||||
|
- Tijdens opname: hoogtewinst/-verlies wordt elke seconde bijgewerkt
|
||||||
|
- Winkel beschrijvingen vertaald
|
||||||
|
|
||||||
|
Bufixes:
|
||||||
|
- Ongeldige vertalingen van stemboodschappen opgelost
|
||||||
|
|
||||||
|
Ontwikkelaar:
|
||||||
|
- Android12-toestemmingen (BLUETOOTH_SCAN)
|
||||||
|
- Android12-toestemmingen (ACCESS_COARSE_LOCATION)
|
||||||
|
- Assertiviteit voor TrackPoints (testen)
|
||||||
|
- Ongebruikte vertalingen verwijderd
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
v3.19.0: OpenTracks
|
||||||
|
|
||||||
|
Wijzigingen:
|
||||||
|
- Ondersteuning voor Running Speed en Cadence (RCS) sensoren
|
||||||
|
- Bij het delen van een track wordt een afbeelding gemaakt
|
||||||
|
- Niet trillen bij starten van opnemen
|
||||||
|
- Bij opnemen: UI toont recentst ontvangen hoogte, snelheid en richting (ook al is deze niet opgeslagen)
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Berekening van totale tijd mag geen pauze-tijd bevatten (vereist opnieuw importeren)
|
||||||
|
- (opnieuw) Gebruik aangepaste overzichten indien aanwezig
|
||||||
|
|
||||||
|
Ontwikkelaar:
|
||||||
|
- Upgrade naar SDK31 en AGP7.0.0 (vereist JDK11)
|
||||||
|
- TrackRecordingService: sensorfusie (TrackPointCreator) en TrackPoint-opslag (TrackRecordingManager) geëxtraheerd
|
||||||
|
- ExportImportTest is nu deterministisch/constant
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
v3.25.0: OpenTracks
|
||||||
|
|
||||||
|
Veranderingen:
|
||||||
|
- GPS is nu optioneel incl. waarschuwing GPS uitgeschakeld
|
||||||
|
- PublicAPI: andere apps kunnen nu opgenomen gegevens ontvangen als ze een opname starten (geïmplementeerd voor Gadgetbridge)
|
||||||
|
- Nieuwe activiteiten: zwemmen, buiten zwemmen, en training
|
||||||
|
- Verwijderd: share pic functie
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- UI: snelheid/hoogte met wisselingen tussen 0,0 en de werkelijke waarde
|
||||||
|
- Tracks met emoji's kunnen worden geëxporteerd
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
v3.25.1: OpenTracks
|
||||||
|
|
||||||
|
Verbetering:
|
||||||
|
- Automatische export: hopelijk voorkomen van stille mislukkingen
|
||||||
|
- Automatische export: voorkom crash
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
v3.26.0: OpenTracks
|
||||||
|
|
||||||
|
Wijzigingen:
|
||||||
|
- Gesproken meldingen zijn instelbaar
|
||||||
|
- Gesproken meldingen van hartslag
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Crash due to race condition
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
v3.26.1: OpenTracks
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Contextuele actie balk overlapte de werkbalk niet in het nachtthema.
|
||||||
@@ -1,11 +1,22 @@
|
|||||||
OpenTracks is een sport tracking applicatie die uw privacy volledig respecteert.
|
Een sport tracker maatje met respect voor privacy.
|
||||||
|
|
||||||
Kenmerken:
|
Sport en outdoor activiteiten geven geluk, vaardigheid en zelfvertrouwen.
|
||||||
* Sport tracking: volg uw sport en outdoor activiteiten
|
|
||||||
* Foto's en markeringen: markeer interessante locaties tijdens het volgen
|
Geef je gezondheid waarde door de conditie bij te houden.
|
||||||
* Hoogte winst en verlies: via barometrische sensor
|
Registreert terwijl je hardloopt of wandelt, en geeft je een fietscomputer met een groter scherm voor op de fiets.
|
||||||
* Hoogte weergegeven in EGM2008 (boven gemiddeld zeeniveau); geëxporteerd als WGS84
|
Markeer interessante locaties langs de weg met foto's.
|
||||||
* Spraakaankondigingen
|
Bewaar geregistreerde statistieken tot in detail voor analyse.
|
||||||
* Ondersteunt Bluetooth LE-sensoren: hartslag, snelheid en afstand (fietsen), cadans (fietsen), en vermogensmeter (fietsen)
|
Deel alleen de gegevens die je aan anderen wilt laten zien.
|
||||||
* Exporteer gegevens: exporteer tracks als KMZ (incl. foto's), KML, of GPX
|
|
||||||
* Geen toegang tot internet: Internet wordt niet gebruikt
|
* Spraakaankondigingen.
|
||||||
|
* Ondersteunt Bluetooth LE sensoren: hartslag, snelheid en afstand (fietsen), cadans (fietsen), en vermogensmeter (fietsen).
|
||||||
|
* Hoogtewinst en -verlies: via barometrische sensor.
|
||||||
|
* Hoogte weergegeven in EGM2008 (boven gemiddeld zeeniveau); geëxporteerd als WGS84.
|
||||||
|
* Gegevens exporteren als tracks, hetzij als KMZ (incl. foto's), KML, of GPX.
|
||||||
|
* Geen Internet toegang of extra toegangsrechten.
|
||||||
|
* Donker en licht thema, met inachtneming van de systeeminstellingen.
|
||||||
|
* Geen advertenties.
|
||||||
|
|
||||||
|
Libre software / Vrije Software / Open Source
|
||||||
|
wat betekent dat je de broncode mag gebruiken, bestuderen, veranderen en delen.
|
||||||
|
Gelicenseerd Apache 2.0
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
Een sport tracker die uw privacy volledig respecteert.
|
Een sport tracker maatje met respect voor privacy.
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
v3.27.0: OpenTracks
|
||||||
|
|
||||||
|
Mudanças:
|
||||||
|
- Anúncios de voz: adicionado 100m, 200m e 500m
|
||||||
|
- Intervalo: adicionado 100m e 500m
|
||||||
|
- O relógio mostra agora também os segundos
|
||||||
|
- GPX, KML, KMZ: uma maior precisão horizontal de exportação e importação
|
||||||
|
- GPX: exporta a distância acumulada via extensão clustrust
|
||||||
|
- CSV: exportação básica (apenas TrackPoints; não para backup)
|
||||||
|
- Solução alternativa para o Tickr X, pois relata SPM em vez de RPM
|
||||||
|
|
||||||
|
Correções de bugs:
|
||||||
|
- Foi Aderido ao intervalo de distância de gravação; Os TrackPoints foram registrados embora estivessem muito próximos
|
||||||
|
- Prevenir travamento ao parar de gravar (condição de corrida)
|
||||||
|
- Evite acidentes ao usar vários sensores (condição de corrida)
|
||||||
|
- Evite travamentos ao abrir o OpenTracks via notificação
|
||||||
@@ -1 +1 @@
|
|||||||
Um rastreador esportivo que respeita totalmente a sua privacidade.
|
Um rastreador esportivo que respeita a sua privacidade.
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
v3.26.0: OpenTracks
|
||||||
|
|
||||||
|
Изменения:
|
||||||
|
- Голосовые оповещения теперь настраиваются
|
||||||
|
- Голосовые оповещения о частоте сердечных сокращений
|
||||||
|
|
||||||
|
Исправление:
|
||||||
|
- Вылет из-за состояния гонки
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
v3.26.1: OpenTracks
|
||||||
|
|
||||||
|
Исправление:
|
||||||
|
- Контекстная панель действий не перекрывала панель инструментов в ночной теме.
|
||||||
@@ -1,11 +1,22 @@
|
|||||||
OpenTracks - это приложение для спортивного слежения, которое полностью уважает вашу конфиденциальность.
|
Спортивный трекер уважающий вашу приватность.
|
||||||
|
|
||||||
Особенности:
|
Спорт и иные активности дарят радость, мастерство и уверенность в себе.
|
||||||
* Спортивный трекинг: отслеживайте свои занятия спортом и активный отдых.
|
|
||||||
* Фотографии и маркеры: отмечайте интересные места во время отслеживания.
|
Цените свое здоровье, следя за тренировками.
|
||||||
* Набор и потеря высоты: через барометрический датчик.
|
Записывает, как вы бегаете или идете, и предоставляет вам функционал велокомпьютера с большим экраном для езды на велосипеде.
|
||||||
* Высота отображается в EGM2008 (над средним уровнем моря); экспортируется в WGS84.
|
Отмечайте интересные места на своем пути с помощью фотографий.
|
||||||
* Голосовые уведомления
|
Храните записанные данные в мельчайших подробностях для анализа.
|
||||||
* Поддержка датчиков Bluetooth LE: пульс, скорость и расстояние (велоспорт), каденция (велоспорт) и измеритель мощности (велоспорт)
|
Делитесь только теми данными, которые хотите, чтобы другие имели доступ к ним.
|
||||||
* Экспорт данных: экспорт треков в формате KMZ (включая фотографии), KML или GPX.
|
|
||||||
* Нет доступа к Интернету: Интернет не используется
|
* Голосовые уведомления.
|
||||||
|
* Поддержка датчиков Bluetooth LE: пульс, скорость и дистанция (для велосипеда), частота вращения педалей (для велосипеда) и измеритель мощности (для велосипеда).
|
||||||
|
* Набор и потеря высоты: с помощью барометрического датчика.
|
||||||
|
* Высота указана в EGM2008 (над средним уровнем моря); экспортируется как WGS84.
|
||||||
|
* Экспорт данных в виде треков формата KMZ (включая фотографии), KML или GPX.
|
||||||
|
* Не требует соединения с Интернетом и лишних разрешений.
|
||||||
|
* Темная и светлая темы с учетом системных настроек.
|
||||||
|
* Без рекламы.
|
||||||
|
|
||||||
|
Libre software / Свободное программное обеспечение / Открытый исходный код
|
||||||
|
означает, что вы можете использовать, изучать, изменять и делиться исходным кодом.
|
||||||
|
Лицензия Apache 2.0
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
Спортивный трекер, который полностью соблюдает вашу конфиденциальность.
|
Спортивный трекер, уважающий вашу конфиденциальность.
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
Gizliliğinize saygı duyan bir spor takipçisi.
|
||||||
|
|
||||||
|
Spor ve açık hava etkinlikleri mutluluk, beceri ve güven verir.
|
||||||
|
|
||||||
|
Antrenmanınızı takip ederek sağlığınıza değer verin.
|
||||||
|
Koşarken veya yürürken kaydeder ve size bisiklet sürmek için daha büyük ekranlı bir bisiklet bilgisayarı verir.
|
||||||
|
Yol boyunca ilginç yerleri resimlerle işaretleyin.
|
||||||
|
Analiz için kaydedilen istatistikleri çok ayrıntılı tutun.
|
||||||
|
Yalnızca başkalarının sahip olmasını istediğiniz verileri paylaşın.
|
||||||
|
|
||||||
|
* Sesli duyurular.
|
||||||
|
* Bluetooth LE sensörlerini destekler: kalp atış hızı, hız ve mesafe (bisiklet), tempo (bisiklet) ve güç ölçer (bisiklet).
|
||||||
|
* İrtifa kazancı ve kaybı: barometrik sensör aracılığıyla.
|
||||||
|
* EGM2008'de gösterilen rakım (ortalama deniz seviyesinin üzerinde); WGS84 olarak dışa aktarılır.
|
||||||
|
* Verileri KMZ (fotoğraflar dahil), KML veya GPX olarak parça olarak dışa aktarın.
|
||||||
|
* İnternet erişimi veya ekstra izin yok.
|
||||||
|
* Sistem ayarlarına uygun olarak koyu ve açık tema.
|
||||||
|
* Reklamsız.
|
||||||
|
|
||||||
|
Libre yazılımı / Özgür Yazılım / Açık Kaynak
|
||||||
|
yani kaynak kodunu kullanabilir, inceleyebilir, değiştirebilir ve paylaşabilirsiniz.
|
||||||
|
Lisanslı Apache 2.0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Gizliliğinize saygı duyan bir spor takipçisi.
|
||||||
@@ -1,2 +1 @@
|
|||||||
android.enableJetifier=true
|
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
#Wed Jul 28 23:10:13 CEST 2021
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStorePath=wrapper/dists
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env sh
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright 2015 the original author or authors.
|
# Copyright © 2015-2021 the original authors.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -17,67 +17,101 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
#
|
||||||
## Gradle start up script for UN*X
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
##
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
PRG="$0"
|
app_path=$0
|
||||||
# Need this for relative symlinks.
|
|
||||||
while [ -h "$PRG" ] ; do
|
# Need this for daisy-chained symlinks.
|
||||||
ls=`ls -ld "$PRG"`
|
while
|
||||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
if expr "$link" : '/.*' > /dev/null; then
|
[ -h "$app_path" ]
|
||||||
PRG="$link"
|
do
|
||||||
else
|
ls=$( ls -ld "$app_path" )
|
||||||
PRG=`dirname "$PRG"`"/$link"
|
link=${ls#*' -> '}
|
||||||
fi
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
SAVED="`pwd`"
|
|
||||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >/dev/null
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
APP_NAME="Gradle"
|
||||||
APP_BASE_NAME=`basename "$0"`
|
APP_BASE_NAME=${0##*/}
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD=maximum
|
||||||
|
|
||||||
warn () {
|
warn () {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
die () {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
# OS specific support (must be 'true' or 'false').
|
||||||
cygwin=false
|
cygwin=false
|
||||||
msys=false
|
msys=false
|
||||||
darwin=false
|
darwin=false
|
||||||
nonstop=false
|
nonstop=false
|
||||||
case "`uname`" in
|
case "$( uname )" in #(
|
||||||
CYGWIN* )
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
cygwin=true
|
Darwin* ) darwin=true ;; #(
|
||||||
;;
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
Darwin* )
|
NONSTOP* ) nonstop=true ;;
|
||||||
darwin=true
|
|
||||||
;;
|
|
||||||
MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
NONSTOP* )
|
|
||||||
nonstop=true
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
|||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
else
|
else
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
fi
|
fi
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD="java"
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
@@ -106,80 +140,95 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
case $MAX_FD in #(
|
||||||
if [ $? -eq 0 ] ; then
|
max*)
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
MAX_FD="$MAX_FD_LIMIT"
|
warn "Could not query maximum file descriptor limit"
|
||||||
fi
|
esac
|
||||||
ulimit -n $MAX_FD
|
case $MAX_FD in #(
|
||||||
if [ $? -ne 0 ] ; then
|
'' | soft) :;; #(
|
||||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
*)
|
||||||
fi
|
ulimit -n "$MAX_FD" ||
|
||||||
else
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Darwin, add options to specify how the application appears in the dock
|
|
||||||
if $darwin; then
|
|
||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
|
||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
|
||||||
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
|
||||||
SEP=""
|
|
||||||
for dir in $ROOTDIRSRAW ; do
|
|
||||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
|
||||||
SEP="|"
|
|
||||||
done
|
|
||||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
|
||||||
# Add a user-defined pattern to the cygpath arguments
|
|
||||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
|
||||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
|
||||||
fi
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
i=0
|
|
||||||
for arg in "$@" ; do
|
|
||||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
|
||||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
|
||||||
|
|
||||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
|
||||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
|
||||||
else
|
|
||||||
eval `echo args$i`="\"$arg\""
|
|
||||||
fi
|
|
||||||
i=`expr $i + 1`
|
|
||||||
done
|
|
||||||
case $i in
|
|
||||||
0) set -- ;;
|
|
||||||
1) set -- "$args0" ;;
|
|
||||||
2) set -- "$args0" "$args1" ;;
|
|
||||||
3) set -- "$args0" "$args1" "$args2" ;;
|
|
||||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
|
||||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
|
||||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
|
||||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
|
||||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
|
||||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape application args
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
save () {
|
# * args from the command line
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
# * the main class name
|
||||||
echo " "
|
# * -classpath
|
||||||
}
|
# * -D...appname settings
|
||||||
APP_ARGS=`save "$@"`
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if "%ERRORLEVEL%" == "0" goto init
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
@@ -54,7 +54,7 @@ goto fail
|
|||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto init
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
@@ -64,21 +64,6 @@ echo location of your Java installation.
|
|||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:init
|
|
||||||
@rem Get command-line arguments, handling Windows variants
|
|
||||||
|
|
||||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
|
||||||
|
|
||||||
:win9xME_args
|
|
||||||
@rem Slurp the command line arguments.
|
|
||||||
set CMD_LINE_ARGS=
|
|
||||||
set _SKIP=2
|
|
||||||
|
|
||||||
:win9xME_args_slurp
|
|
||||||
if "x%~1" == "x" goto execute
|
|
||||||
|
|
||||||
set CMD_LINE_ARGS=%*
|
|
||||||
|
|
||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|
||||||
@@ -86,7 +71,7 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 862 KiB After Width: | Height: | Size: 474 KiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 240 KiB |
|
Before Width: | Height: | Size: 338 KiB After Width: | Height: | Size: 298 KiB |
|
Before Width: | Height: | Size: 665 KiB After Width: | Height: | Size: 565 KiB |
|
Before Width: | Height: | Size: 248 KiB After Width: | Height: | Size: 237 KiB |
|
Before Width: | Height: | Size: 661 KiB After Width: | Height: | Size: 588 KiB |
|
Before Width: | Height: | Size: 1001 KiB After Width: | Height: | Size: 1012 KiB |
@@ -5,6 +5,7 @@ import static androidx.test.espresso.Espresso.onData;
|
|||||||
import static androidx.test.espresso.Espresso.onView;
|
import static androidx.test.espresso.Espresso.onView;
|
||||||
import static androidx.test.espresso.Espresso.pressBack;
|
import static androidx.test.espresso.Espresso.pressBack;
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
import static androidx.test.espresso.action.ViewActions.click;
|
||||||
|
import static androidx.test.espresso.action.ViewActions.longClick;
|
||||||
import static androidx.test.espresso.action.ViewActions.scrollTo;
|
import static androidx.test.espresso.action.ViewActions.scrollTo;
|
||||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||||
@@ -13,22 +14,19 @@ import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
|||||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
import static org.hamcrest.Matchers.anything;
|
import static org.hamcrest.Matchers.anything;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.childAtPosition;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.withListSize;
|
||||||
|
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.Instrumentation;
|
import android.app.Instrumentation;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.ViewParent;
|
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
|
|
||||||
import androidx.test.espresso.Espresso;
|
import androidx.test.espresso.IdlingRegistry;
|
||||||
import androidx.test.espresso.IdlingResource;
|
import androidx.test.espresso.IdlingResource;
|
||||||
import androidx.test.espresso.UiController;
|
|
||||||
import androidx.test.espresso.ViewAction;
|
|
||||||
import androidx.test.espresso.ViewInteraction;
|
import androidx.test.espresso.ViewInteraction;
|
||||||
import androidx.test.espresso.action.MotionEvents;
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions;
|
import androidx.test.espresso.assertion.ViewAssertions;
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
@@ -37,7 +35,6 @@ import androidx.test.platform.app.InstrumentationRegistry;
|
|||||||
import androidx.test.rule.GrantPermissionRule;
|
import androidx.test.rule.GrantPermissionRule;
|
||||||
|
|
||||||
import org.hamcrest.Description;
|
import org.hamcrest.Description;
|
||||||
import org.hamcrest.Matcher;
|
|
||||||
import org.hamcrest.TypeSafeMatcher;
|
import org.hamcrest.TypeSafeMatcher;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -58,231 +55,33 @@ public class EspressoDeleteTrackTest {
|
|||||||
@Rule
|
@Rule
|
||||||
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||||
|
|
||||||
private MyIdlingResource idlingResource;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void registerIntentServiceIdlingResource() {
|
|
||||||
Instrumentation instrumentation
|
|
||||||
= InstrumentationRegistry.getInstrumentation();
|
|
||||||
idlingResource = new MyIdlingResource(instrumentation.getTargetContext());
|
|
||||||
Espresso.registerIdlingResources(idlingResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void unregisterIntentServiceIdlingResource() {
|
|
||||||
Espresso.unregisterIdlingResources(idlingResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Ignore("Test fails permanently")
|
|
||||||
@Deprecated
|
|
||||||
@Test
|
@Test
|
||||||
public void espressoDeleteTrackTest() {
|
public void espressoDeleteTrackTest() {
|
||||||
int countBefore;
|
// TrackListActivity: start recording
|
||||||
|
onView(withId(R.id.track_list_fab_action)).perform(click());
|
||||||
|
|
||||||
{
|
// TrackRecordingActivity
|
||||||
// TrackListActivity: start recording
|
onView(withId(R.id.track_recording_fab_action))
|
||||||
ViewInteraction trackControllerRecordButton = onView(withId(R.id.controller_record));
|
// wait; stay recording
|
||||||
trackControllerRecordButton.perform(click());
|
.perform(waitFor(5000))
|
||||||
}
|
// stop;
|
||||||
|
.perform(longClick());
|
||||||
|
|
||||||
{
|
// TrackStoppedActivity
|
||||||
// TrackRecordingActivity
|
onView(withId(R.id.finish_button)).perform(click());
|
||||||
ViewInteraction trackControllerRecordButton = onView(withId(R.id.controller_record));
|
|
||||||
ViewInteraction trackControllerStopButton = onView(withId(R.id.controller_stop));
|
|
||||||
|
|
||||||
// wait; stay recording
|
|
||||||
trackControllerRecordButton.perform(waitFor(5000));
|
|
||||||
|
|
||||||
// stop;
|
|
||||||
trackControllerStopButton.perform(veryLongTouch(1600));
|
|
||||||
}
|
|
||||||
|
|
||||||
// back
|
|
||||||
pressBack();
|
|
||||||
|
|
||||||
// get number of items before deleting a track
|
|
||||||
countBefore = numberOfItemsListView();
|
|
||||||
|
|
||||||
// select track
|
// select track
|
||||||
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(veryLongTouch(5000));
|
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(longClick());
|
||||||
|
|
||||||
// open menu and delete selected track
|
// open menu and delete selected track
|
||||||
ViewInteraction overflowMenuButton = onView(
|
onView(allOf(withContentDescription("More options"), isDisplayed()))
|
||||||
allOf(withContentDescription("More options"),
|
.perform(click());
|
||||||
childAtPosition(
|
|
||||||
childAtPosition(
|
|
||||||
withId(R.id.toolbar),
|
|
||||||
2),
|
|
||||||
2),
|
|
||||||
isDisplayed()));
|
|
||||||
overflowMenuButton.perform(click());
|
|
||||||
|
|
||||||
ViewInteraction appCompatTextView = onView(
|
onView(withText("Delete")).perform(click());
|
||||||
allOf(withId(R.id.title), withText("Delete"),
|
|
||||||
childAtPosition(
|
|
||||||
childAtPosition(
|
|
||||||
withId(R.id.content),
|
|
||||||
0),
|
|
||||||
0),
|
|
||||||
isDisplayed()));
|
|
||||||
appCompatTextView.perform(click());
|
|
||||||
|
|
||||||
ViewInteraction appCompatButton = onView(
|
onView(withText("OK")).perform(click());
|
||||||
allOf(withId(android.R.id.button1), withText("Yes"),
|
|
||||||
childAtPosition(
|
|
||||||
childAtPosition(
|
|
||||||
withId(R.id.buttonPanel),
|
|
||||||
0),
|
|
||||||
3)));
|
|
||||||
appCompatButton.perform(scrollTo(), click());
|
|
||||||
|
|
||||||
// check number of items after delete
|
// tracklist is empty now
|
||||||
onView(withId(R.id.track_list)).check(ViewAssertions.matches(withListSize(countBefore - 1)));
|
onView(allOf(withText("Start recording your next adventure here"), isDisplayed()));
|
||||||
}
|
|
||||||
|
|
||||||
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 Matcher<View> withListSize (final int 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");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private int numberOfItemsListView() {
|
|
||||||
final int[] counts = new int[1];
|
|
||||||
onView(withId(R.id.track_list)).check(matches(new TypeSafeMatcher<View>() {
|
|
||||||
@Override
|
|
||||||
public boolean matchesSafely(View view) {
|
|
||||||
ListView listView = (ListView) view;
|
|
||||||
|
|
||||||
counts[0] = listView.getCount();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void describeTo(Description description) {
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
return counts[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static class MyIdlingResource implements IdlingResource {
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private ResourceCallback resourceCallback;
|
|
||||||
|
|
||||||
public MyIdlingResource(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return MyIdlingResource.class.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void registerIdleTransitionCallback(IdlingResource.ResourceCallback resourceCallback) {
|
|
||||||
this.resourceCallback = resourceCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isIdleNow() {
|
|
||||||
boolean idle = !isDeleteServiceRunning();
|
|
||||||
if (idle && resourceCallback != null) {
|
|
||||||
resourceCallback.onTransitionToIdle();
|
|
||||||
}
|
|
||||||
return idle;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isDeleteServiceRunning() {
|
|
||||||
ActivityManager manager =
|
|
||||||
(ActivityManager) context.getSystemService(
|
|
||||||
Context.ACTIVITY_SERVICE);
|
|
||||||
for (ActivityManager.RunningServiceInfo info :
|
|
||||||
manager.getRunningServices(Integer.MAX_VALUE)) {
|
|
||||||
if (TrackDeleteService.class.getName().equals(
|
|
||||||
info.service.getClassName())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,52 +2,38 @@ package de.dennisguse.opentracks;
|
|||||||
|
|
||||||
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
import static androidx.test.espresso.Espresso.onView;
|
||||||
|
import static androidx.test.espresso.Espresso.openContextualActionModeOverflowMenu;
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
import static androidx.test.espresso.action.ViewActions.click;
|
||||||
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
|
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
|
||||||
|
import static androidx.test.espresso.action.ViewActions.longClick;
|
||||||
import static androidx.test.espresso.action.ViewActions.replaceText;
|
import static androidx.test.espresso.action.ViewActions.replaceText;
|
||||||
import static androidx.test.espresso.action.ViewActions.scrollTo;
|
import static androidx.test.espresso.action.ViewActions.scrollTo;
|
||||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
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.withId;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||||
|
|
||||||
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.ViewInteraction;
|
||||||
import androidx.test.espresso.action.MotionEvents;
|
|
||||||
import androidx.test.espresso.util.HumanReadables;
|
|
||||||
import androidx.test.espresso.util.TreeIterables;
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.LargeTest;
|
import androidx.test.filters.LargeTest;
|
||||||
import androidx.test.rule.GrantPermissionRule;
|
import androidx.test.rule.GrantPermissionRule;
|
||||||
|
|
||||||
import org.hamcrest.Description;
|
|
||||||
import org.hamcrest.Matcher;
|
|
||||||
import org.hamcrest.TypeSafeMatcher;
|
|
||||||
import org.hamcrest.core.IsInstanceOf;
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.Locale;
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class EspressoEditTrackRecordingTest {
|
public class EspressoEditTrackRecordingTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final LocaleRule mLocaleRule = new LocaleRule(Locale.ENGLISH);
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||||
|
|
||||||
@@ -59,200 +45,41 @@ public class EspressoEditTrackRecordingTest {
|
|||||||
public void espressoEditTrackRecordingTest() {
|
public void espressoEditTrackRecordingTest() {
|
||||||
{
|
{
|
||||||
// TrackListActivity: start recording
|
// TrackListActivity: start recording
|
||||||
ViewInteraction trackControllerRecordButton = onView(withId(R.id.controller_record));
|
ViewInteraction fabRecordButton = onView(withId(R.id.track_list_fab_action));
|
||||||
trackControllerRecordButton.perform(click());
|
fabRecordButton.perform(click());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// TrackRecordingActivity
|
// TrackRecordingActivity
|
||||||
ViewInteraction trackControllerRecordButton = onView(withId(R.id.controller_record));
|
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
|
||||||
ViewInteraction trackControllerStopButton = onView(withId(R.id.controller_stop));
|
|
||||||
|
|
||||||
// wait; stay recording
|
// wait; stay recording
|
||||||
trackControllerRecordButton.perform(waitFor(15000));
|
trackControllerStopButton.perform(waitFor(4000));
|
||||||
|
|
||||||
// open menu
|
// open menu
|
||||||
ViewInteraction overflowMenuButton = onView(
|
openContextualActionModeOverflowMenu();
|
||||||
allOf(withContentDescription("More options"),
|
|
||||||
childAtPosition(
|
|
||||||
childAtPosition(
|
|
||||||
withId(R.id.toolbar),
|
|
||||||
2),
|
|
||||||
2),
|
|
||||||
isDisplayed()));
|
|
||||||
overflowMenuButton.perform(click());
|
|
||||||
|
|
||||||
// click on edit
|
// Click the item.
|
||||||
ViewInteraction appCompatTextView = onView(
|
onView(withText(R.string.menu_edit)).perform(click());
|
||||||
allOf(withId(R.id.title), withText("Edit"),
|
|
||||||
childAtPosition(
|
|
||||||
childAtPosition(
|
|
||||||
withId(R.id.content),
|
|
||||||
0),
|
|
||||||
0),
|
|
||||||
isDisplayed()));
|
|
||||||
appCompatTextView.perform(click());
|
|
||||||
|
|
||||||
// change name for "New Name"
|
// change name for "New Name"
|
||||||
ViewInteraction textInputEditText = onView(
|
ViewInteraction textInputEditText = onView(withId(R.id.track_edit_name));
|
||||||
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"));
|
textInputEditText.perform(scrollTo(), replaceText("New Name"));
|
||||||
|
|
||||||
ViewInteraction textInputEditText2 = onView(
|
ViewInteraction textInputEditText2 = onView(allOf(withId(R.id.track_edit_name), withText("New Name"), isDisplayed()));
|
||||||
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());
|
textInputEditText2.perform(closeSoftKeyboard());
|
||||||
|
|
||||||
// save edition
|
// save edition
|
||||||
ViewInteraction appCompatButton = onView(
|
ViewInteraction appCompatButton = onView(withId(R.id.track_edit_save));
|
||||||
allOf(withId(R.id.track_edit_save),
|
|
||||||
childAtPosition(
|
|
||||||
childAtPosition(
|
|
||||||
withClassName(is("android.widget.LinearLayout")),
|
|
||||||
3),
|
|
||||||
1),
|
|
||||||
isDisplayed()));
|
|
||||||
appCompatButton.perform(click());
|
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;
|
// stop;
|
||||||
trackControllerStopButton.perform(veryLongTouch(1600));
|
trackControllerStopButton.perform(longClick());
|
||||||
|
|
||||||
// check track's name is "New Name"
|
// it's on track stopped activity and there are two buttons
|
||||||
ViewInteraction textView = onView(
|
ViewInteraction resumeButton = onView(withId(R.id.resume_button));
|
||||||
allOf(withId(R.id.stats_name_value),
|
resumeButton.check(matches(isDisplayed()));
|
||||||
withParent(withParent(IsInstanceOf.instanceOf(android.widget.ScrollView.class))),
|
ViewInteraction finishButton = onView(withId(R.id.finish_button));
|
||||||
isDisplayed()));
|
finishButton.check(matches(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,32 +1,29 @@
|
|||||||
package de.dennisguse.opentracks;
|
package de.dennisguse.opentracks;
|
||||||
|
|
||||||
import android.view.MotionEvent;
|
import static androidx.test.espresso.Espresso.onData;
|
||||||
import android.view.View;
|
import static androidx.test.espresso.Espresso.onView;
|
||||||
|
import static androidx.test.espresso.action.ViewActions.click;
|
||||||
|
import static androidx.test.espresso.action.ViewActions.longClick;
|
||||||
|
import static androidx.test.espresso.action.ViewActions.scrollTo;
|
||||||
|
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||||
|
import static org.hamcrest.Matchers.allOf;
|
||||||
|
import static org.hamcrest.Matchers.anything;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.selectTabAtIndex;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||||
|
|
||||||
import androidx.test.espresso.UiController;
|
|
||||||
import androidx.test.espresso.ViewAction;
|
|
||||||
import androidx.test.espresso.ViewInteraction;
|
import androidx.test.espresso.ViewInteraction;
|
||||||
import androidx.test.espresso.action.MotionEvents;
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.LargeTest;
|
import androidx.test.filters.LargeTest;
|
||||||
import androidx.test.rule.GrantPermissionRule;
|
import androidx.test.rule.GrantPermissionRule;
|
||||||
|
|
||||||
import com.google.android.material.tabs.TabLayout;
|
|
||||||
|
|
||||||
import org.hamcrest.Matcher;
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import 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.assertion.ViewAssertions.matches;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
import static org.hamcrest.Matchers.anything;
|
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class EspressoUITest {
|
public class EspressoUITest {
|
||||||
@@ -39,34 +36,30 @@ public class EspressoUITest {
|
|||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test
|
@Test
|
||||||
public void record_pause_resume_stop() {
|
public void record_stop_resume_stop_finish() {
|
||||||
{
|
{
|
||||||
// TrackListActivity: start recording
|
// TrackListActivity: start recording
|
||||||
ViewInteraction trackControllerRecordButton = onView(withId(R.id.controller_record));
|
ViewInteraction trackControllerRecordButton = onView(withId(R.id.track_list_fab_action));
|
||||||
trackControllerRecordButton.perform(click());
|
trackControllerRecordButton.perform(click());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// TrackRecordingActivity
|
// TrackRecordingActivity: wait to record some time and then stop
|
||||||
ViewInteraction trackControllerRecordButton = onView(withId(R.id.controller_record));
|
onView(withId(R.id.track_recording_fab_action))
|
||||||
ViewInteraction trackControllerStopButton = onView(withId(R.id.controller_stop));
|
.perform(waitFor(5000))
|
||||||
|
.perform(longClick());
|
||||||
|
|
||||||
// wait; stay recording
|
// TrackStoppedActivity: resume
|
||||||
trackControllerRecordButton.perform(waitFor(5000));
|
onView(allOf(withId(R.id.resume_button), isClickable()))
|
||||||
|
.perform(click());
|
||||||
|
|
||||||
//pause
|
// TrackRecordingActivity: wait and then stop
|
||||||
trackControllerRecordButton.perform(veryLongTouch(1600));
|
onView(withId(R.id.track_recording_fab_action))
|
||||||
|
.perform(waitFor(5000))
|
||||||
|
.perform(longClick());
|
||||||
|
|
||||||
// wait; stay paused
|
// TrackStoppedActivity
|
||||||
trackControllerRecordButton.perform(waitFor(1000));
|
onView(withId(R.id.finish_button))
|
||||||
|
.perform(click());
|
||||||
// resume
|
|
||||||
trackControllerRecordButton.perform(click());
|
|
||||||
|
|
||||||
// wait; stay recording
|
|
||||||
trackControllerRecordButton.perform(waitFor(1000));
|
|
||||||
|
|
||||||
// stop;
|
|
||||||
trackControllerStopButton.perform(veryLongTouch(1600));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,13 +68,13 @@ public class EspressoUITest {
|
|||||||
public void record_move_through_tabs() {
|
public void record_move_through_tabs() {
|
||||||
{
|
{
|
||||||
// TrackListActivity: start recording
|
// TrackListActivity: start recording
|
||||||
ViewInteraction trackControllerRecordButton = onView(withId(R.id.controller_record));
|
ViewInteraction trackControllerRecordButton = onView(withId(R.id.track_list_fab_action));
|
||||||
trackControllerRecordButton.perform(click());
|
trackControllerRecordButton.perform(click());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// TrackRecordingActivity
|
// TrackRecordingActivity
|
||||||
ViewInteraction tabLayout = onView(withId(R.id.track_detail_activity_tablayout));
|
ViewInteraction tabLayout = onView(withId(R.id.track_detail_activity_tablayout));
|
||||||
ViewInteraction trackControllerStopButton = onView(withId(R.id.controller_stop));
|
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
|
||||||
|
|
||||||
tabLayout.perform(selectTabAtIndex(1));
|
tabLayout.perform(selectTabAtIndex(1));
|
||||||
tabLayout.perform(waitFor(1000));
|
tabLayout.perform(waitFor(1000));
|
||||||
@@ -96,7 +89,7 @@ public class EspressoUITest {
|
|||||||
tabLayout.perform(waitFor(1000));
|
tabLayout.perform(waitFor(1000));
|
||||||
|
|
||||||
// stop
|
// stop
|
||||||
trackControllerStopButton.perform(veryLongTouch(1600));
|
trackControllerStopButton.perform(longClick());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,75 +97,6 @@ public class EspressoUITest {
|
|||||||
@Test
|
@Test
|
||||||
public void selectAndDeleteTrack() {
|
public void selectAndDeleteTrack() {
|
||||||
onView(withId(R.id.track_list)).check(matches(isDisplayed()));
|
onView(withId(R.id.track_list)).check(matches(isDisplayed()));
|
||||||
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(veryLongTouch(2000));
|
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(longClick());
|
||||||
}
|
|
||||||
|
|
||||||
private static ViewAction veryLongTouch(final int duration_ms) {
|
|
||||||
return new ViewAction() {
|
|
||||||
@Override
|
|
||||||
public String getDescription() {
|
|
||||||
return "Perform long touch.";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Matcher<View> getConstraints() {
|
|
||||||
return isDisplayed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void perform(UiController uiController, final View view) {
|
|
||||||
// Get view absolute position
|
|
||||||
int[] location = new int[2];
|
|
||||||
view.getLocationOnScreen(location);
|
|
||||||
|
|
||||||
// Offset coordinates by view position
|
|
||||||
float[] coordinates = new float[]{location[0] + 1, location[1] + 1};
|
|
||||||
|
|
||||||
// Send down event, pause, and send up
|
|
||||||
MotionEvent down = MotionEvents.sendDown(uiController, coordinates, new float[]{1f, 1f}).down;
|
|
||||||
uiController.loopMainThreadForAtLeast(duration_ms);
|
|
||||||
MotionEvents.sendUp(uiController, down, coordinates);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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 selectTabAtIndex(final int index) {
|
|
||||||
return new ViewAction() {
|
|
||||||
@Override
|
|
||||||
public String getDescription() {
|
|
||||||
return "Selecting tab.";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Matcher<View> getConstraints() {
|
|
||||||
return isDisplayed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void perform(UiController uiController, View view) {
|
|
||||||
TabLayout tabLayout = (TabLayout) view;
|
|
||||||
tabLayout.getTabAt(index).select();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package de.dennisguse.opentracks;
|
||||||
|
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import org.junit.rules.TestRule;
|
||||||
|
import org.junit.runner.Description;
|
||||||
|
import org.junit.runners.model.Statement;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class LocaleRule implements TestRule {
|
||||||
|
|
||||||
|
private final Locale[] mLocales;
|
||||||
|
private Locale mDeviceLocale;
|
||||||
|
|
||||||
|
|
||||||
|
public LocaleRule(Locale... locales) {
|
||||||
|
assert locales != null && locales.length > 0;
|
||||||
|
mLocales = locales;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Statement apply(Statement base, Description description) {
|
||||||
|
return new Statement() {
|
||||||
|
@Override
|
||||||
|
public void evaluate() throws Throwable {
|
||||||
|
try {
|
||||||
|
mDeviceLocale = Locale.getDefault();
|
||||||
|
for (Locale locale : mLocales) {
|
||||||
|
setLocale(locale);
|
||||||
|
base.evaluate();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (mDeviceLocale != null) {
|
||||||
|
setLocale(mDeviceLocale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setLocale(Locale locale) {
|
||||||
|
Resources resources = ApplicationProvider.getApplicationContext().getResources();
|
||||||
|
Locale.setDefault(locale);
|
||||||
|
Configuration config = resources.getConfiguration();
|
||||||
|
config.setLocale(locale);
|
||||||
|
DisplayMetrics displayMetrics = resources.getDisplayMetrics();
|
||||||
|
resources.updateConfiguration(config, displayMetrics);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,12 +5,13 @@ import org.junit.runner.Description;
|
|||||||
import org.junit.runners.model.Statement;
|
import org.junit.runners.model.Statement;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
|
|
||||||
public class PreferenceMetricUnitRule implements TestRule {
|
public class PreferenceUnitSystemRule implements TestRule {
|
||||||
private final boolean metricUnits;
|
private final UnitSystem unit;
|
||||||
|
|
||||||
public PreferenceMetricUnitRule(boolean metricUnits) {
|
public PreferenceUnitSystemRule(UnitSystem unit) {
|
||||||
this.metricUnits = metricUnits;
|
this.unit = unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -18,13 +19,13 @@ public class PreferenceMetricUnitRule implements TestRule {
|
|||||||
return new Statement() {
|
return new Statement() {
|
||||||
@Override
|
@Override
|
||||||
public void evaluate() throws Throwable {
|
public void evaluate() throws Throwable {
|
||||||
final boolean previousMetricUnits = PreferencesUtils.isMetricUnits();
|
final UnitSystem previousUnitSystem = PreferencesUtils.getUnitSystem();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
PreferencesUtils.setMetricUnits(metricUnits);
|
PreferencesUtils.setUnit(unit);
|
||||||
base.evaluate();
|
base.evaluate();
|
||||||
} finally {
|
} finally {
|
||||||
PreferencesUtils.setMetricUnits(previousMetricUnits);
|
PreferencesUtils.setUnit(previousUnitSystem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package de.dennisguse.opentracks;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.junit.rules.TestRule;
|
||||||
|
import org.junit.runner.Description;
|
||||||
|
import org.junit.runners.model.Statement;
|
||||||
|
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
public class TimezoneRule implements TestRule {
|
||||||
|
|
||||||
|
private final TimeZone mTimeZone;
|
||||||
|
private TimeZone mDeviceTimeZone;
|
||||||
|
|
||||||
|
|
||||||
|
public TimezoneRule(@NonNull TimeZone timeZone) {
|
||||||
|
mTimeZone = timeZone;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Statement apply(Statement base, Description description) {
|
||||||
|
return new Statement() {
|
||||||
|
@Override
|
||||||
|
public void evaluate() throws Throwable {
|
||||||
|
try {
|
||||||
|
mDeviceTimeZone = TimeZone.getDefault();
|
||||||
|
TimeZone.setDefault(mTimeZone);
|
||||||
|
base.evaluate();
|
||||||
|
} finally {
|
||||||
|
TimeZone.setDefault(mDeviceTimeZone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ import de.dennisguse.opentracks.data.models.Altitude;
|
|||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
import de.dennisguse.opentracks.data.models.Speed;
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@@ -26,7 +27,7 @@ public class ChartPointTest {
|
|||||||
statistics.setTotalTime(Duration.ofSeconds(1000));
|
statistics.setTotalTime(Duration.ofSeconds(1000));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), Altitude.EGM2008.of(0), false, false);
|
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), false, UnitSystem.IMPERIAL);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(1000000, (long) point.getTimeOrDistance());
|
assertEquals(1000000, (long) point.getTimeOrDistance());
|
||||||
@@ -39,7 +40,7 @@ public class ChartPointTest {
|
|||||||
statistics.setTotalDistance(Distance.of(1000));
|
statistics.setTotalDistance(Distance.of(1000));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), Altitude.EGM2008.of(0), true, true);
|
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), true, UnitSystem.METRIC);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(1, (long) point.getTimeOrDistance());
|
assertEquals(1, (long) point.getTimeOrDistance());
|
||||||
@@ -49,9 +50,11 @@ public class ChartPointTest {
|
|||||||
public void create_get_altitude_speed_and_pace() {
|
public void create_get_altitude_speed_and_pace() {
|
||||||
// given
|
// given
|
||||||
TrackStatistics statistics = new TrackStatistics();
|
TrackStatistics statistics = new TrackStatistics();
|
||||||
|
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
|
||||||
|
.setAltitude(Altitude.EGM2008.of(50));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), Altitude.EGM2008.of(50), false, true);
|
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(50, point.getAltitude(), 0.01);
|
assertEquals(50, point.getAltitude(), 0.01);
|
||||||
@@ -63,9 +66,10 @@ public class ChartPointTest {
|
|||||||
public void create_sensorNotAvailable() {
|
public void create_sensorNotAvailable() {
|
||||||
// given
|
// given
|
||||||
TrackStatistics statistics = new TrackStatistics();
|
TrackStatistics statistics = new TrackStatistics();
|
||||||
|
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
|
||||||
|
.setAltitude(Altitude.EGM2008.of(50));
|
||||||
// when
|
// when
|
||||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), Altitude.EGM2008.of(50), false, true);
|
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertNull(point.getHeartRate());
|
assertNull(point.getHeartRate());
|
||||||
@@ -76,15 +80,16 @@ public class ChartPointTest {
|
|||||||
@Test
|
@Test
|
||||||
public void create_sensorAvailable() {
|
public void create_sensorAvailable() {
|
||||||
// given
|
// given
|
||||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
|
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
|
||||||
trackPoint.setHeartRate(100f);
|
.setAltitude(Altitude.EGM2008.of(50))
|
||||||
trackPoint.setCadence(101f);
|
.setHeartRate(100f)
|
||||||
trackPoint.setPower(102f);
|
.setCadence(101f)
|
||||||
|
.setPower(102f);
|
||||||
|
|
||||||
TrackStatistics statistics = new TrackStatistics();
|
TrackStatistics statistics = new TrackStatistics();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), Altitude.EGM2008.of(50), false, true);
|
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(100.0, point.getHeartRate(), 0.01);
|
assertEquals(100.0, point.getHeartRate(), 0.01);
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ public class ChartValueSeriesTest {
|
|||||||
new int[]{1, 100, 1000},
|
new int[]{1, 100, 1000},
|
||||||
R.string.description_altitude_metric,
|
R.string.description_altitude_metric,
|
||||||
R.string.description_altitude_imperial,
|
R.string.description_altitude_imperial,
|
||||||
|
R.string.description_altitude_imperial,
|
||||||
R.color.chart_altitude_fill,
|
R.color.chart_altitude_fill,
|
||||||
R.color.chart_altitude_border,
|
R.color.chart_altitude_border,
|
||||||
15,
|
15,
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ public class TestDataUtil {
|
|||||||
trackPoint.setAltitude(i * ALTITUDE_INTERVAL);
|
trackPoint.setAltitude(i * ALTITUDE_INTERVAL);
|
||||||
trackPoint.setSpeed(Speed.of(5f + (i / 10f)));
|
trackPoint.setSpeed(Speed.of(5f + (i / 10f)));
|
||||||
|
|
||||||
trackPoint.setHeartRate(100f + i);
|
trackPoint.setHeartRate(100f + i % 80);
|
||||||
trackPoint.setCadence(300f + i);
|
trackPoint.setCadence(300f + i);
|
||||||
trackPoint.setPower(400f + i);
|
trackPoint.setPower(400f + i);
|
||||||
trackPoint.setAltitudeGain(ALTITUDE_GAIN);
|
trackPoint.setAltitudeGain(ALTITUDE_GAIN);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class ShareContentProviderTest {
|
|||||||
trackIds.add(new Track.Id(3));
|
trackIds.add(new Track.Id(3));
|
||||||
trackIds.add(new Track.Id(5));
|
trackIds.add(new Track.Id(5));
|
||||||
|
|
||||||
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(trackIds, "TrackName", TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA);
|
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(trackIds, "filename.suffix", TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA);
|
||||||
|
|
||||||
assertEquals(trackIds, ShareContentProvider.parseURI(shareURIandMIME.first));
|
assertEquals(trackIds, ShareContentProvider.parseURI(shareURIandMIME.first));
|
||||||
}
|
}
|
||||||
@@ -40,6 +40,6 @@ public class ShareContentProviderTest {
|
|||||||
trackIds.add(new Track.Id(1));
|
trackIds.add(new Track.Id(1));
|
||||||
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(trackIds, "../../&1=1", TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA);
|
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(trackIds, "../../&1=1", TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA);
|
||||||
|
|
||||||
assertEquals(Uri.parse("content://de.dennisguse.opentracks.debug.content/tracks/kml_with_trackdetail_and_sensordata/1/..%2F..%2F%261%3D1.kml"), shareURIandMIME.first);
|
assertEquals(Uri.parse("content://de.dennisguse.opentracks.debug.content/tracks/KML_WITH_TRACKDETAIL_AND_SENSORDATA/1/..%2F..%2F%261%3D1"), shareURIandMIME.first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package de.dennisguse.opentracks.data.models;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
|
|
||||||
|
//TODO Parametrized tests
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class DistanceFormatterTest {
|
||||||
|
|
||||||
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFormatDistance_metric() {
|
||||||
|
DistanceFormatter formatter = DistanceFormatter.Builder()
|
||||||
|
.setDecimalCount(2)
|
||||||
|
.setUnit(UnitSystem.METRIC)
|
||||||
|
.build(context);
|
||||||
|
|
||||||
|
// A large number in metric
|
||||||
|
assertEquals("5.00 km", formatter.formatDistance(Distance.of(5000)));
|
||||||
|
// A small number in metric
|
||||||
|
assertEquals("100.00 m", formatter.formatDistance(Distance.of(100)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFormatDistance_imperial() {
|
||||||
|
DistanceFormatter formatter = DistanceFormatter.Builder()
|
||||||
|
.setDecimalCount(2)
|
||||||
|
.setUnit(UnitSystem.IMPERIAL)
|
||||||
|
.build(context);
|
||||||
|
|
||||||
|
// A large number in imperial
|
||||||
|
assertEquals("3.11 mi", formatter.formatDistance(Distance.of(5000)));
|
||||||
|
// A small number in imperial
|
||||||
|
assertEquals("328.08 ft", formatter.formatDistance(Distance.of(100)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFormatDistance_nautical() {
|
||||||
|
DistanceFormatter formatter = DistanceFormatter.Builder()
|
||||||
|
.setDecimalCount(2)
|
||||||
|
.setUnit(UnitSystem.NAUTICAL_IMPERIAL)
|
||||||
|
.build(context);
|
||||||
|
|
||||||
|
// A large number in nautical
|
||||||
|
assertEquals("2.70 NM", formatter.formatDistance(Distance.of(5000)));
|
||||||
|
// A small number in nautical
|
||||||
|
assertEquals("328.08 ft", formatter.formatDistance(Distance.of(100)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package de.dennisguse.opentracks.data.models;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
|
|
||||||
|
//TODO Use make parametrized tests?
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class SpeedFormatterTest {
|
||||||
|
|
||||||
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSpeedParts_pace_metric() {
|
||||||
|
SpeedFormatter formatter = SpeedFormatter.Builder()
|
||||||
|
.setDecimalCount(2)
|
||||||
|
.setUnit(UnitSystem.METRIC)
|
||||||
|
.setReportSpeedOrPace(false)
|
||||||
|
.build(context);
|
||||||
|
|
||||||
|
assertEquals("4:59", formatter.getSpeedParts(Speed.of(3.34)).first);
|
||||||
|
assertEquals("5:00", formatter.getSpeedParts(Speed.of(3.33)).first);
|
||||||
|
|
||||||
|
assertEquals("min/km", formatter.getSpeedParts(Speed.zero()).second);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSpeedParts_pace_imperial() {
|
||||||
|
SpeedFormatter formatter = SpeedFormatter.Builder()
|
||||||
|
.setDecimalCount(2)
|
||||||
|
.setUnit(UnitSystem.IMPERIAL)
|
||||||
|
.setReportSpeedOrPace(false)
|
||||||
|
.build(context);
|
||||||
|
|
||||||
|
assertEquals("8:02", formatter.getSpeedParts(Speed.of(3.34)).first);
|
||||||
|
assertEquals("min/mi", formatter.getSpeedParts(Speed.zero()).second);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSpeedParts_pace_nautical() {
|
||||||
|
SpeedFormatter formatter = SpeedFormatter.Builder()
|
||||||
|
.setDecimalCount(2)
|
||||||
|
.setUnit(UnitSystem.NAUTICAL_IMPERIAL)
|
||||||
|
.setReportSpeedOrPace(false)
|
||||||
|
.build(context);
|
||||||
|
|
||||||
|
assertEquals("9:14", formatter.getSpeedParts(Speed.of(3.34)).first);
|
||||||
|
assertEquals("min/NM", formatter.getSpeedParts(Speed.zero()).second);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSpeedParts_speed_metric() {
|
||||||
|
SpeedFormatter formatter = SpeedFormatter.Builder()
|
||||||
|
.setDecimalCount(2)
|
||||||
|
.setUnit(UnitSystem.METRIC)
|
||||||
|
.setReportSpeedOrPace(true)
|
||||||
|
.build(context);
|
||||||
|
|
||||||
|
assertEquals("11.9", formatter.getSpeedParts(Speed.of(3.31)).first);
|
||||||
|
assertEquals("km/h", formatter.getSpeedParts(Speed.zero()).second);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSpeedParts_speed_imperial() {
|
||||||
|
SpeedFormatter formatter = SpeedFormatter.Builder()
|
||||||
|
.setDecimalCount(2)
|
||||||
|
.setUnit(UnitSystem.IMPERIAL)
|
||||||
|
.setReportSpeedOrPace(true)
|
||||||
|
.build(context);
|
||||||
|
|
||||||
|
assertEquals("7.5", formatter.getSpeedParts(Speed.of(3.34)).first);
|
||||||
|
assertEquals("mph", formatter.getSpeedParts(Speed.zero()).second);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSpeedParts_speed_nautical() {
|
||||||
|
SpeedFormatter formatter = SpeedFormatter.Builder()
|
||||||
|
.setDecimalCount(2)
|
||||||
|
.setUnit(UnitSystem.NAUTICAL_IMPERIAL)
|
||||||
|
.setReportSpeedOrPace(true)
|
||||||
|
.build(context);
|
||||||
|
|
||||||
|
assertEquals("6.5", formatter.getSpeedParts(Speed.of(3.34)).first);
|
||||||
|
assertEquals("kn", formatter.getSpeedParts(Speed.zero()).second);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFormatSpeed() {
|
||||||
|
SpeedFormatter formatter = SpeedFormatter.Builder()
|
||||||
|
.setDecimalCount(2)
|
||||||
|
.setUnit(UnitSystem.METRIC)
|
||||||
|
.setReportSpeedOrPace(false)
|
||||||
|
.build(context);
|
||||||
|
|
||||||
|
assertEquals("4:59 min/km", formatter.formatSpeed(Speed.of(3.34)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package de.dennisguse.opentracks.io.file;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.TimezoneRule;
|
||||||
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
|
|
||||||
|
@RunWith(Parameterized.class)
|
||||||
|
public class TrackFilenameGeneratorTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public TimezoneRule timezoneRule = new TimezoneRule(TimeZone.getTimeZone("Europe/Berlin"));
|
||||||
|
|
||||||
|
@Parameterized.Parameters
|
||||||
|
public static Collection<String[]> data() {
|
||||||
|
return Arrays.asList(new String[][]{
|
||||||
|
{"{uuid}_{name}", "0000fee0_Best Track.gpx"},
|
||||||
|
{"{name}_{uuid}", "Best Track_0000fee0.gpx"},
|
||||||
|
{"{time}_{name}", "02_02_02_Best Track.gpx"},
|
||||||
|
{"{date}_{name}", "2020-02-02_Best Track.gpx"},
|
||||||
|
{ApplicationProvider.getApplicationContext().getString(R.string.export_filename_format_default), "2020-02-02_02_02_02_Best Track.gpx"},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private final TrackFilenameGenerator subject;
|
||||||
|
private final String expected;
|
||||||
|
|
||||||
|
public TrackFilenameGeneratorTest(String template, String expected) {
|
||||||
|
this.subject = new TrackFilenameGenerator(template);
|
||||||
|
this.expected = expected;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilenameTemplate() {
|
||||||
|
// given
|
||||||
|
Track track = new Track();
|
||||||
|
track.setName("Best Track");
|
||||||
|
track.setUuid(UUID.fromString("0000fee0-0000-1000-8000-00805f9b34fb"));
|
||||||
|
track.getTrackStatistics().setStartTime(Instant.parse("2020-02-02T02:02:02Z"));
|
||||||
|
|
||||||
|
// when
|
||||||
|
String filename = subject.format(track, TrackFileFormat.GPX);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(expected, filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package de.dennisguse.opentracks.io.file;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
|
|
||||||
|
//TODO Merge with TrackFilenameGeneratorTest whenever Junit5 gets available.
|
||||||
|
//https://github.com/android/android-test/issues/224
|
||||||
|
@RunWith(Parameterized.class)
|
||||||
|
public class TrackFilenameGeneratorTest2 {
|
||||||
|
|
||||||
|
@Parameterized.Parameters
|
||||||
|
public static Collection<String> data() {
|
||||||
|
return Arrays.asList(
|
||||||
|
"{name}_{starime}",
|
||||||
|
"{name",
|
||||||
|
"name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private final TrackFilenameGenerator subject;
|
||||||
|
|
||||||
|
public TrackFilenameGeneratorTest2(String template) {
|
||||||
|
this.subject = new TrackFilenameGenerator(template);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = TrackFilenameGenerator.TemplateInvalidException.class)
|
||||||
|
public void testFilenameTemplate() {
|
||||||
|
// given
|
||||||
|
Track track = new Track();
|
||||||
|
track.setName("Best Track");
|
||||||
|
track.setUuid(UUID.fromString("0000fee0-0000-1000-8000-00805f9b34fb"));
|
||||||
|
track.getTrackStatistics().setStartTime(Instant.parse("2020-02-02T02:02:02Z"));
|
||||||
|
|
||||||
|
// when
|
||||||
|
String filename = subject.format(track, TrackFileFormat.GPX);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import android.content.Intent;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -16,28 +17,36 @@ import androidx.preference.PreferenceManager;
|
|||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.LargeTest;
|
import androidx.test.filters.LargeTest;
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
import androidx.test.rule.GrantPermissionRule;
|
import androidx.test.rule.GrantPermissionRule;
|
||||||
import androidx.test.rule.ServiceTestRule;
|
import androidx.test.rule.ServiceTestRule;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.TimeZone;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.TimezoneRule;
|
||||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.data.models.Cadence;
|
import de.dennisguse.opentracks.data.models.Cadence;
|
||||||
@@ -67,12 +76,18 @@ import de.dennisguse.opentracks.stats.TrackStatistics;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class ExportImportTest {
|
public class ExportImportTest {
|
||||||
|
|
||||||
|
private static final String TAG = ExportImportTest.class.getSimpleName();
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||||
|
|
||||||
|
//For csv_export_only() as we the timezone is hardcoded in the expectation.
|
||||||
|
@Rule
|
||||||
|
public TimezoneRule timezoneRule = new TimezoneRule(TimeZone.getTimeZone("Europe/Berlin"));
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void preSetUp() {
|
public static void preSetUp() {
|
||||||
// Prepare looper for Android's message queue
|
// Prepare looper for Android's message queue
|
||||||
@@ -132,7 +147,7 @@ public class ExportImportTest {
|
|||||||
|
|
||||||
Distance sensorDistance = Distance.of(10); // recording distance interval
|
Distance sensorDistance = Distance.of(10); // recording distance interval
|
||||||
|
|
||||||
sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3, 14, 10, 15, 10, 1);
|
sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3, 14, 10, 13, 15, 10, 1);
|
||||||
service.insertMarker("Marker 1", "Marker 1 category", "Marker 1 desc", null);
|
service.insertMarker("Marker 1", "Marker 1 category", "Marker 1 desc", null);
|
||||||
|
|
||||||
// A sensor-only TrackPoint
|
// A sensor-only TrackPoint
|
||||||
@@ -143,24 +158,24 @@ public class ExportImportTest {
|
|||||||
trackPointCreator.setClock("2020-02-02T02:02:05Z");
|
trackPointCreator.setClock("2020-02-02T02:02:05Z");
|
||||||
mockBLESensorData(trackPointCreator, 5f, Distance.of(2), 66f, 3f, 50f); // Distance will be added to next TrackPoint
|
mockBLESensorData(trackPointCreator, 5f, Distance.of(2), 66f, 3f, 50f); // Distance will be added to next TrackPoint
|
||||||
|
|
||||||
sendLocation(trackPointCreator, "2020-02-02T02:02:05Z", 3, 14.001, 10, 15, 10, 0);
|
sendLocation(trackPointCreator, "2020-02-02T02:02:05Z", 3, 14.001, 10, 13, 15, 10, 0);
|
||||||
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
|
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
|
||||||
|
|
||||||
trackPointCreator.setClock("2020-02-02T02:02:06Z");
|
trackPointCreator.setClock("2020-02-02T02:02:06Z");
|
||||||
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, trackPointCreator));
|
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator));
|
||||||
service.pauseCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
trackPointCreator.setClock("2020-02-02T02:02:20Z");
|
trackPointCreator.setClock("2020-02-02T02:02:20Z");
|
||||||
service.resumeCurrentTrack();
|
service.resumeTrack(trackId);
|
||||||
|
|
||||||
sendLocation(trackPointCreator, "2020-02-02T02:02:21Z", 3, 14.002, 10, 15, 10, 0);
|
sendLocation(trackPointCreator, "2020-02-02T02:02:21Z", 3, 14.002, 10, 13, 15, 10, 0);
|
||||||
|
|
||||||
sendLocation(trackPointCreator, "2020-02-02T02:02:22Z", 3, 16, 10, 15, 10, 0);
|
sendLocation(trackPointCreator, "2020-02-02T02:02:22Z", 3, 16, 10, 13, 15, 10, 0);
|
||||||
|
|
||||||
sendLocation(trackPointCreator, "2020-02-02T02:02:23Z", 3, 16.001, 10, 15, 10, 0);
|
sendLocation(trackPointCreator, "2020-02-02T02:02:23Z", 3, 16.001, 10, 27, 15, 10, 0);
|
||||||
|
|
||||||
trackPointCreator.setClock("2020-02-02T02:02:24Z");
|
trackPointCreator.setClock("2020-02-02T02:02:24Z");
|
||||||
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, trackPointCreator));
|
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator));
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
@@ -172,7 +187,7 @@ public class ExportImportTest {
|
|||||||
track = contentProviderUtils.getTrack(trackId);
|
track = contentProviderUtils.getTrack(trackId);
|
||||||
trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
markers = contentProviderUtils.getMarkers(trackId);
|
markers = contentProviderUtils.getMarkers(trackId);
|
||||||
assertEquals(11, trackPoints.size());
|
assertEquals(10, trackPoints.size());
|
||||||
assertEquals(2, markers.size());
|
assertEquals(2, markers.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,8 +221,7 @@ public class ExportImportTest {
|
|||||||
assertEquals(track.getIcon(), importedTrack.getIcon());
|
assertEquals(track.getIcon(), importedTrack.getIcon());
|
||||||
|
|
||||||
// 2. trackpoints
|
// 2. trackpoints
|
||||||
TrackPointAssert a = new TrackPointAssert()
|
TrackPointAssert a = new TrackPointAssert();
|
||||||
.noAccuracy();
|
|
||||||
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||||
a.assertEquals(trackPoints, actual);
|
a.assertEquals(trackPoints, actual);
|
||||||
|
|
||||||
@@ -320,8 +334,7 @@ public class ExportImportTest {
|
|||||||
// Therefore, the track segmentation is changes.
|
// Therefore, the track segmentation is changes.
|
||||||
|
|
||||||
TrackPointAssert a = new TrackPointAssert()
|
TrackPointAssert a = new TrackPointAssert()
|
||||||
.setDelta(0.05)
|
.setDelta(0.05); // speed is not fully
|
||||||
.noAccuracy(); // speed is not fully
|
|
||||||
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||||
a.assertEquals(List.of(
|
a.assertEquals(List.of(
|
||||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:03Z"))
|
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:03Z"))
|
||||||
@@ -330,7 +343,8 @@ public class ExportImportTest {
|
|||||||
.setAltitude(10)
|
.setAltitude(10)
|
||||||
.setSpeed(Speed.of(15))
|
.setSpeed(Speed.of(15))
|
||||||
.setAltitudeLoss(1f)
|
.setAltitudeLoss(1f)
|
||||||
.setAltitudeGain(1f),
|
.setAltitudeGain(1f)
|
||||||
|
.setHorizontalAccuracy(Distance.of(10)),
|
||||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:05Z"))
|
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:05Z"))
|
||||||
.setLatitude(3)
|
.setLatitude(3)
|
||||||
.setLongitude(14.001)
|
.setLongitude(14.001)
|
||||||
@@ -341,21 +355,24 @@ public class ExportImportTest {
|
|||||||
.setSensorDistance(Distance.of(12))
|
.setSensorDistance(Distance.of(12))
|
||||||
.setHeartRate(66f)
|
.setHeartRate(66f)
|
||||||
.setPower(50f)
|
.setPower(50f)
|
||||||
.setCadence(3f),
|
.setCadence(3f)
|
||||||
|
.setHorizontalAccuracy(Distance.of(10)),
|
||||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:21Z"))
|
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:21Z"))
|
||||||
.setLatitude(3)
|
.setLatitude(3)
|
||||||
.setLongitude(14.002)
|
.setLongitude(14.002)
|
||||||
.setAltitude(10)
|
.setAltitude(10)
|
||||||
.setAltitudeLoss(0f)
|
.setAltitudeLoss(0f)
|
||||||
.setAltitudeGain(0f)
|
.setAltitudeGain(0f)
|
||||||
.setSpeed(Speed.of(15)),
|
.setSpeed(Speed.of(15))
|
||||||
|
.setHorizontalAccuracy(Distance.of(10)),
|
||||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:22Z"))
|
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:22Z"))
|
||||||
.setLatitude(3)
|
.setLatitude(3)
|
||||||
.setLongitude(16)
|
.setLongitude(16)
|
||||||
.setAltitude(10)
|
.setAltitude(10)
|
||||||
.setAltitudeLoss(0f)
|
.setAltitudeLoss(0f)
|
||||||
.setAltitudeGain(0f)
|
.setAltitudeGain(0f)
|
||||||
.setSpeed(Speed.of(15)),
|
.setSpeed(Speed.of(15))
|
||||||
|
.setHorizontalAccuracy(Distance.of(10)),
|
||||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:23Z"))
|
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:23Z"))
|
||||||
.setLatitude(3)
|
.setLatitude(3)
|
||||||
.setLongitude(16.001)
|
.setLongitude(16.001)
|
||||||
@@ -363,6 +380,7 @@ public class ExportImportTest {
|
|||||||
.setAltitudeLoss(0f)
|
.setAltitudeLoss(0f)
|
||||||
.setAltitudeGain(0f)
|
.setAltitudeGain(0f)
|
||||||
.setSpeed(Speed.of(15))
|
.setSpeed(Speed.of(15))
|
||||||
|
.setHorizontalAccuracy(Distance.of(10))
|
||||||
), actual);
|
), actual);
|
||||||
|
|
||||||
// 3. trackstatistics
|
// 3. trackstatistics
|
||||||
@@ -423,6 +441,37 @@ public class ExportImportTest {
|
|||||||
assertNull(trackImported);
|
assertNull(trackImported);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Ignore(value = "TODO Fails on CI; works on API24 and API30 locally")
|
||||||
|
@LargeTest
|
||||||
|
@Test
|
||||||
|
public void csv_export_only() throws TimeoutException, IOException {
|
||||||
|
setUp();
|
||||||
|
|
||||||
|
// given
|
||||||
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
|
TrackExporter trackExporter = TrackFileFormat.CSV.createTrackExporter(context);
|
||||||
|
|
||||||
|
// when
|
||||||
|
// 1. export
|
||||||
|
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
||||||
|
contentProviderUtils.deleteTrack(context, trackId);
|
||||||
|
|
||||||
|
// then
|
||||||
|
InputStream expected = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.csv_export);
|
||||||
|
String expectedText = new BufferedReader(new InputStreamReader(expected, StandardCharsets.UTF_8))
|
||||||
|
.lines()
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
|
||||||
|
|
||||||
|
InputStream actual = context.getContentResolver().openInputStream(tmpFileUri);
|
||||||
|
String actualText = new BufferedReader(new InputStreamReader(actual, StandardCharsets.UTF_8))
|
||||||
|
.lines()
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
|
||||||
|
assertEquals(expectedText, actualText);
|
||||||
|
}
|
||||||
|
|
||||||
private void assertMarkers() {
|
private void assertMarkers() {
|
||||||
assertEquals(markers.size(), contentProviderUtils.getMarkers(importTrackId).size());
|
assertEquals(markers.size(), contentProviderUtils.getMarkers(importTrackId).size());
|
||||||
|
|
||||||
@@ -443,7 +492,7 @@ public class ExportImportTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) {
|
private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) {
|
||||||
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, trackPointCreator) {
|
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator) {
|
||||||
@Override
|
@Override
|
||||||
public SensorDataSet fill(@NonNull TrackPoint trackPoint) {
|
public SensorDataSet fill(@NonNull TrackPoint trackPoint) {
|
||||||
SensorDataSet sensorDataSet = new SensorDataSet();
|
SensorDataSet sensorDataSet = new SensorDataSet();
|
||||||
@@ -469,7 +518,7 @@ public class ExportImportTest {
|
|||||||
return sensorDataSet;
|
return sensorDataSet;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
trackPointCreator.onChange(null);
|
trackPointCreator.onChange(new SensorDataSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) {
|
private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) {
|
||||||
@@ -478,11 +527,14 @@ public class ExportImportTest {
|
|||||||
altitudeSumManager.setAltitudeLoss_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) {
|
private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, float altitude, float altitudeGain) {
|
||||||
Location location = new Location("mock");
|
Location location = new Location("mock");
|
||||||
location.setLatitude(latitude);
|
location.setLatitude(latitude);
|
||||||
location.setLongitude(longitude);
|
location.setLongitude(longitude);
|
||||||
location.setAccuracy(accuracy);
|
location.setAccuracy(accuracy);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
location.setVerticalAccuracyMeters(verticalAccuracy);
|
||||||
|
}
|
||||||
location.setSpeed(speed);
|
location.setSpeed(speed);
|
||||||
location.setAltitude(altitude);
|
location.setAltitude(altitude);
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import de.dennisguse.opentracks.stats.TrackStatistics;
|
|||||||
* Test that legacy KML/GPX formats can still be imported.
|
* Test that legacy KML/GPX formats can still be imported.
|
||||||
*/
|
*/
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
public class GPXImportTest {
|
public class GPXTrackImporterTest {
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
||||||
@@ -31,7 +31,7 @@ import de.dennisguse.opentracks.data.models.TrackPoint;
|
|||||||
* Test that legacy KML/GPX formats can still be imported.
|
* Test that legacy KML/GPX formats can still be imported.
|
||||||
*/
|
*/
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
public class KMLImportTest {
|
public class KMLTrackImporterTest {
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
||||||
@@ -8,8 +8,6 @@ import de.dennisguse.opentracks.data.models.TrackPoint;
|
|||||||
|
|
||||||
public class TrackPointAssert {
|
public class TrackPointAssert {
|
||||||
|
|
||||||
private boolean assertAccuracy = true;
|
|
||||||
|
|
||||||
private double delta = 0.001;
|
private double delta = 0.001;
|
||||||
|
|
||||||
public TrackPointAssert() {
|
public TrackPointAssert() {
|
||||||
@@ -45,13 +43,13 @@ public class TrackPointAssert {
|
|||||||
Assert.assertEquals(expected.getSpeed().toMPS(), actual.getSpeed().toMPS(), delta);
|
Assert.assertEquals(expected.getSpeed().toMPS(), actual.getSpeed().toMPS(), delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (assertAccuracy) {
|
Assert.assertEquals(expected.hasHorizontalAccuracy(), actual.hasHorizontalAccuracy());
|
||||||
Assert.assertEquals(expected.hasHorizontalAccuracy(), actual.hasHorizontalAccuracy());
|
if (expected.hasHorizontalAccuracy()) {
|
||||||
if (expected.hasHorizontalAccuracy()) {
|
Assert.assertEquals(expected.getHorizontalAccuracy().toM(), actual.getHorizontalAccuracy().toM(), delta);
|
||||||
Assert.assertEquals(expected.getHorizontalAccuracy().toM(), actual.getHorizontalAccuracy().toM(), delta);
|
}
|
||||||
}
|
Assert.assertEquals(expected.hasVerticalAccuracy(), actual.hasVerticalAccuracy());
|
||||||
} else {
|
if (expected.hasVerticalAccuracy()) {
|
||||||
Assert.assertFalse(actual.hasHorizontalAccuracy());
|
Assert.assertEquals(expected.getVerticalAccuracy().toM(), actual.getVerticalAccuracy().toM(), delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.assertEquals(expected.hasSensorDistance(), actual.hasSensorDistance());
|
Assert.assertEquals(expected.hasSensorDistance(), actual.hasSensorDistance());
|
||||||
@@ -96,11 +94,6 @@ public class TrackPointAssert {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TrackPointAssert noAccuracy() {
|
|
||||||
this.assertAccuracy = false;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TrackPointAssert setDelta(double delta) {
|
public TrackPointAssert setDelta(double delta) {
|
||||||
this.delta = delta;
|
this.delta = delta;
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
|||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
import de.dennisguse.opentracks.data.models.Speed;
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
@@ -48,7 +49,7 @@ public class TrackRecordingServiceNotificationManagerTest {
|
|||||||
.thenReturn(notificationCompatBuilder);
|
.thenReturn(notificationCompatBuilder);
|
||||||
|
|
||||||
TrackRecordingServiceNotificationManager subject = new TrackRecordingServiceNotificationManager(notificationManager, notificationCompatBuilder);
|
TrackRecordingServiceNotificationManager subject = new TrackRecordingServiceNotificationManager(notificationManager, notificationCompatBuilder);
|
||||||
subject.setMetricUnits(true);
|
subject.setUnitSystem(UnitSystem.METRIC);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, Distance.of(100));
|
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, Distance.of(100));
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
// when
|
// when
|
||||||
String pauseTime = "2020-02-02T02:02:03Z";
|
String pauseTime = "2020-02-02T02:02:03Z";
|
||||||
trackPointCreator.setClock(pauseTime);
|
trackPointCreator.setClock(pauseTime);
|
||||||
service.pauseCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(new TrackStatistics(startTime, pauseTime, 0, 1, 0, 0, 0f, 0f)
|
assertEquals(new TrackStatistics(startTime, pauseTime, 0, 1, 0, 0, 0f, 0f)
|
||||||
@@ -205,7 +205,7 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
|
|
||||||
String pauseTime = "2020-02-02T02:02:03Z";
|
String pauseTime = "2020-02-02T02:02:03Z";
|
||||||
trackPointCreator.setClock(pauseTime);
|
trackPointCreator.setClock(pauseTime);
|
||||||
service.pauseCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
trackPointCreator.setClock("2020-02-02T02:02:04Z");
|
trackPointCreator.setClock("2020-02-02T02:02:04Z");
|
||||||
@@ -612,6 +612,55 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
@Test
|
||||||
|
public void testRecording_gpsOnly_recordingDistance_movement_non_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, 15);
|
||||||
|
|
||||||
|
// when - will be ignored
|
||||||
|
String gps2 = "2020-02-02T02:02:04Z";
|
||||||
|
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0, 35.0, 1, 15);
|
||||||
|
|
||||||
|
// when
|
||||||
|
String gps3 = "2020-02-02T02:02:05Z";
|
||||||
|
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.0001, 35.0, 1, 15);
|
||||||
|
|
||||||
|
// when
|
||||||
|
String stopTime = "2020-02-02T02:02:12Z";
|
||||||
|
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.TRACKPOINT, Instant.parse(gps1))
|
||||||
|
.setLatitude(45)
|
||||||
|
.setLongitude(35)
|
||||||
|
.setHorizontalAccuracy(Distance.of(1))
|
||||||
|
.setAltitudeGain(0f)
|
||||||
|
.setAltitudeLoss(0f)
|
||||||
|
.setSpeed(Speed.of(15)),
|
||||||
|
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps3))
|
||||||
|
.setLatitude(45)
|
||||||
|
.setLongitude(35)
|
||||||
|
.setHorizontalAccuracy(Distance.of(1))
|
||||||
|
.setAltitudeGain(0f)
|
||||||
|
.setAltitudeLoss(0f)
|
||||||
|
.setSpeed(Speed.of(15)),
|
||||||
|
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||||
|
.setAltitudeGain(0f)
|
||||||
|
.setAltitudeLoss(0f)
|
||||||
|
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||||
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
@@ -796,15 +845,12 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
new TrackPoint(TrackPoint.Type.SENSORPOINT, Instant.parse(sensor3))
|
new TrackPoint(TrackPoint.Type.SENSORPOINT, Instant.parse(sensor3))
|
||||||
.setSpeed(Speed.of(5))
|
.setSpeed(Speed.of(5))
|
||||||
.setSensorDistance(Distance.of(10)),
|
.setSensorDistance(Distance.of(10)),
|
||||||
new TrackPoint(TrackPoint.Type.SENSORPOINT, Instant.parse(sensor5)) //TODO No need to store this TrackPoint, data could be merged into the next one
|
|
||||||
.setSpeed(Speed.of(5))
|
|
||||||
.setSensorDistance(Distance.of(4)),
|
|
||||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps3))
|
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps3))
|
||||||
.setLatitude(45.001)
|
.setLatitude(45.001)
|
||||||
.setLongitude(35)
|
.setLongitude(35)
|
||||||
.setHorizontalAccuracy(Distance.of(1))
|
.setHorizontalAccuracy(Distance.of(1))
|
||||||
.setSpeed(Speed.of(5))
|
.setSpeed(Speed.of(5))
|
||||||
.setSensorDistance(Distance.of(0)),
|
.setSensorDistance(Distance.of(4.0)),
|
||||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps4))
|
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps4))
|
||||||
.setLatitude(45.001)
|
.setLatitude(45.001)
|
||||||
.setLongitude(35)
|
.setLongitude(35)
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ public class TrackRecordingServiceTestStateMachine {
|
|||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void recording_startPauseStopResume_no_data() throws InterruptedException {
|
public void recording_startStopResume_no_data() throws InterruptedException {
|
||||||
// given
|
// given
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
@@ -153,31 +153,19 @@ public class TrackRecordingServiceTestStateMachine {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assertTrue(service.isRecording());
|
assertTrue(service.isRecording());
|
||||||
assertEquals(new RecordingStatus(trackId, false), service.getRecordingStatusObservable().getValue());
|
assertEquals(new RecordingStatus(trackId), service.getRecordingStatusObservable().getValue());
|
||||||
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||||
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
||||||
|
|
||||||
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
service.pauseCurrentTrack();
|
service.resumeTrack(trackId);
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertTrue(service.isRecording());
|
assertTrue(service.isRecording());
|
||||||
assertTrue(service.isPaused());
|
assertEquals(new RecordingStatus(trackId), service.getRecordingStatusObservable().getValue());
|
||||||
assertEquals(new RecordingStatus(trackId, true), service.getRecordingStatusObservable().getValue());
|
|
||||||
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
|
||||||
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue()); //TODO The GPS is kept in pause, right? Might be a bug.
|
|
||||||
|
|
||||||
|
|
||||||
// when
|
|
||||||
service.resumeCurrentTrack();
|
|
||||||
Thread.sleep(1000);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertTrue(service.isRecording());
|
|
||||||
assertFalse(service.isPaused());
|
|
||||||
assertEquals(new RecordingStatus(trackId, false), service.getRecordingStatusObservable().getValue());
|
|
||||||
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||||
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
||||||
|
|
||||||
@@ -188,7 +176,6 @@ public class TrackRecordingServiceTestStateMachine {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
assertFalse(service.isPaused());
|
|
||||||
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
||||||
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||||
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
||||||
@@ -200,8 +187,7 @@ public class TrackRecordingServiceTestStateMachine {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assertTrue(service.isRecording());
|
assertTrue(service.isRecording());
|
||||||
assertFalse(service.isPaused());
|
assertEquals(new RecordingStatus(trackId), service.getRecordingStatusObservable().getValue());
|
||||||
assertEquals(new RecordingStatus(trackId, false), service.getRecordingStatusObservable().getValue());
|
|
||||||
Thread.sleep(1000); //TODO Figure out why we need to wait here until the update is happening
|
Thread.sleep(1000); //TODO Figure out why we need to wait here until the update is happening
|
||||||
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||||
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
||||||
@@ -209,29 +195,31 @@ public class TrackRecordingServiceTestStateMachine {
|
|||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void invalidStateChanges() throws InterruptedException {
|
public void cannotResume_non_existing_track() throws InterruptedException {
|
||||||
// given
|
// given
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
// when
|
|
||||||
service.pauseCurrentTrack();
|
|
||||||
Thread.sleep(1000);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
||||||
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||||
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
||||||
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
service.resumeCurrentTrack();
|
service.resumeTrack(new Track.Id(-1));
|
||||||
Thread.sleep(1000);
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
assertFalse(service.isRecording());
|
||||||
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
||||||
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||||
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
@Test
|
||||||
|
public void cannotEnd_without_starting() throws InterruptedException {
|
||||||
|
// given
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
||||||
|
assertEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||||
|
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
@@ -257,8 +245,7 @@ public class TrackRecordingServiceTestStateMachine {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assertTrue(service.isRecording());
|
assertTrue(service.isRecording());
|
||||||
assertFalse(service.isPaused());
|
assertEquals(new RecordingStatus(trackId), service.getRecordingStatusObservable().getValue());
|
||||||
assertEquals(new RecordingStatus(trackId, false), service.getRecordingStatusObservable().getValue());
|
|
||||||
Thread.sleep(1000); //TODO Figure out why we need to wait here until the update is happening
|
Thread.sleep(1000); //TODO Figure out why we need to wait here until the update is happening
|
||||||
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
assertNotEquals(TrackRecordingService.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||||
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
assertEquals(GpsStatusValue.GPS_ENABLED, service.getGpsStatusObservable().getValue());
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class TrackRecordingServiceTestUtils {
|
|||||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)))
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)))
|
||||||
.getService();
|
.getService();
|
||||||
|
|
||||||
service.getTrackPointCreator().setRemoteSensorManager(new BluetoothRemoteSensorManager(context, service.getTrackPointCreator()));
|
service.getTrackPointCreator().setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, service.getTrackPointCreator()));
|
||||||
service.getTrackPointCreator().setClock(Clock.systemUTC());
|
service.getTrackPointCreator().setClock(Clock.systemUTC());
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,32 +8,47 @@ import android.util.Pair;
|
|||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.LocaleRule;
|
||||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.data.TrackPointIterator;
|
import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
|
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
import de.dennisguse.opentracks.data.models.Speed;
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
|
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
import de.dennisguse.opentracks.ui.intervals.IntervalStatistics;
|
import de.dennisguse.opentracks.ui.intervals.IntervalStatistics;
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class VoiceAnnouncementUtilsTest {
|
public class VoiceAnnouncementUtilsTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final LocaleRule mLocaleRule = new LocaleRule(Locale.ENGLISH);
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
private ContentProviderUtils contentProviderUtils;
|
private ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
contentProviderUtils = new ContentProviderUtils(context);
|
contentProviderUtils = new ContentProviderUtils(context);
|
||||||
|
|
||||||
|
PreferencesUtils.setVoiceAnnounceLapHeartRate(false);
|
||||||
|
PreferencesUtils.setVoiceAnnounceAverageHeartRate(false);
|
||||||
|
PreferencesUtils.setVoiceAnnounceTotalDistance(true);
|
||||||
|
PreferencesUtils.setVoiceAnnounceMovingTime(true);
|
||||||
|
PreferencesUtils.setVoiceAnnounceAverageSpeedPace(true);
|
||||||
|
PreferencesUtils.setVoiceAnnounceLapSpeedPace(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -46,10 +61,58 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
stats.setTotalAltitudeGain(6000f);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, true, null);
|
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("total distance 20.00 kilometers in 1 hour 5 minutes 10 seconds at 18.4 kilometers per hour", announcement);
|
assertEquals("Total distance 20.00 kilometers. 1 hour 5 minutes 10 seconds. Speed 18.4 kilometers per hour.", announcement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAnnouncement_metric_speed_rounding_check() {
|
||||||
|
TrackStatistics stats = new TrackStatistics();
|
||||||
|
stats.setTotalDistance(Distance.of(20000));
|
||||||
|
stats.setTotalTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||||
|
stats.setMovingTime(Duration.ofHours(1).plusSeconds(1));
|
||||||
|
stats.setMaxSpeed(Speed.of(100));
|
||||||
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
|
// when
|
||||||
|
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals("Total distance 20.00 kilometers. 1 hour 1 second. Speed 20.0 kilometers per hour.", announcement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAnnouncement_metric_distance_rounding_check() {
|
||||||
|
TrackStatistics stats = new TrackStatistics();
|
||||||
|
stats.setTotalDistance(Distance.of(19999));
|
||||||
|
stats.setTotalTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||||
|
stats.setMovingTime(Duration.ofHours(1));
|
||||||
|
stats.setMaxSpeed(Speed.of(100));
|
||||||
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
|
// when
|
||||||
|
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals("Total distance 20.00 kilometers. 1 hour. Speed 20.0 kilometers per hour.", announcement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAnnouncement_metric_distance_rounding_check_two() {
|
||||||
|
TrackStatistics stats = new TrackStatistics();
|
||||||
|
stats.setTotalDistance(Distance.of(19990));
|
||||||
|
stats.setTotalTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||||
|
stats.setMovingTime(Duration.ofHours(1));
|
||||||
|
stats.setMaxSpeed(Speed.of(100));
|
||||||
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
|
// when
|
||||||
|
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals("Total distance 19.99 kilometers. 1 hour. Speed 20.0 kilometers per hour.", announcement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -63,22 +126,15 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||||
assertEquals(trackPointIterator.getCount(), numberOfPoints);
|
assertEquals(trackPointIterator.getCount(), numberOfPoints);
|
||||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||||
|
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, true, lastInterval);
|
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
Assert.assertEquals(
|
assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour.", announcement);
|
||||||
"total distance " +
|
|
||||||
StringUtils.getDistanceParts(context, stats.getTotalDistance(), true).first +
|
|
||||||
" kilometers in " + buildAndGetTimeText(stats.getTotalTime(), false) + " at " +
|
|
||||||
StringUtils.getSpeedParts(context, stats.getAverageMovingSpeed(), true, true).first +
|
|
||||||
" kilometers per hour Lap speed of " +
|
|
||||||
StringUtils.getSpeedParts(context, lastInterval.getSpeed(), true, true).first +
|
|
||||||
" kilometers per hour",
|
|
||||||
announcement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -91,10 +147,10 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
stats.setTotalAltitudeGain(6000f);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, false, null);
|
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, false, null, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("total distance 20.00 kilometers in 1 hour 5 minutes 10 seconds at 3 minutes 15 seconds per kilometer", announcement);
|
assertEquals("Total distance 20.00 kilometers. 1 hour 5 minutes 10 seconds. Pace 3 minutes 15 seconds per kilometer.", announcement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -108,22 +164,15 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||||
assertEquals(trackPointIterator.getCount(), numberOfPoints);
|
assertEquals(trackPointIterator.getCount(), numberOfPoints);
|
||||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||||
|
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, false, lastInterval);
|
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, false, lastInterval, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(
|
assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Pace 1 minute 10 seconds per kilometer. Lap time 1 minute 10 seconds per kilometer.", announcement);
|
||||||
"total distance " +
|
|
||||||
StringUtils.getDistanceParts(context, stats.getTotalDistance(), true).first +
|
|
||||||
" kilometers in " + buildAndGetTimeText(stats.getTotalTime(), false) + " at " +
|
|
||||||
buildAndGetTimeText(stats.getAverageMovingSpeed().toPace(true), true) +
|
|
||||||
" per kilometer Lap time of " +
|
|
||||||
buildAndGetTimeText(lastInterval.getSpeed().toPace(true), true) +
|
|
||||||
" per kilometer",
|
|
||||||
announcement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -136,10 +185,10 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
stats.setTotalAltitudeGain(6000f);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, true, null);
|
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, true, null, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("total distance 12.43 miles in 1 hour 5 minutes 10 seconds at 11.4 miles per hour", announcement);
|
assertEquals("Total distance 12.43 miles. 1 hour 5 minutes 10 seconds. Speed 11.4 miles per hour.", announcement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -153,22 +202,15 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||||
assertEquals(trackPointIterator.getCount(), numberOfPoints);
|
assertEquals(trackPointIterator.getCount(), numberOfPoints);
|
||||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||||
|
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, true, lastInterval);
|
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, true, lastInterval, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(
|
assertEquals("Total distance 8.83 miles. 16 minutes 39 seconds. Speed 31.8 miles per hour. Lap speed 31.8 miles per hour.", announcement);
|
||||||
"total distance " +
|
|
||||||
StringUtils.getDistanceParts(context, stats.getTotalDistance(), false).first +
|
|
||||||
" miles in " + buildAndGetTimeText(stats.getTotalTime(), false) + " at " +
|
|
||||||
StringUtils.getSpeedParts(context, stats.getAverageMovingSpeed(), false, true).first +
|
|
||||||
" miles per hour Lap speed of " +
|
|
||||||
StringUtils.getSpeedParts(context, lastInterval.getSpeed(), false, true).first +
|
|
||||||
" miles per hour",
|
|
||||||
announcement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -181,10 +223,10 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
stats.setTotalAltitudeGain(6000f);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, false, null);
|
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, false, null, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("total distance 12.43 miles in 1 hour 5 minutes 10 seconds at 5 minutes 15 seconds per mile", announcement);
|
assertEquals("Total distance 12.43 miles. 1 hour 5 minutes 10 seconds. Pace 5 minutes 15 seconds per mile.", announcement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -198,51 +240,70 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||||
assertEquals(trackPointIterator.getCount(), numberOfPoints);
|
assertEquals(trackPointIterator.getCount(), numberOfPoints);
|
||||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||||
|
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, false, lastInterval);
|
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, false, lastInterval, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
//assertEquals("total distance 12.43 miles in 1 hour 5 minutes 10 seconds at 5 minutes 15 seconds per mile Lap time of 1 minute 53 seconds per mile", announcement);
|
assertEquals("Total distance 8.83 miles. 16 minutes 39 seconds. Pace 1 minute 53 seconds per mile. Lap time 1 minute 53 seconds per mile.", announcement);
|
||||||
assertEquals(
|
|
||||||
"total distance " +
|
|
||||||
StringUtils.getDistanceParts(context, stats.getTotalDistance(), false).first +
|
|
||||||
" miles in " + buildAndGetTimeText(stats.getTotalTime(), false) + " at " +
|
|
||||||
buildAndGetTimeText(stats.getAverageMovingSpeed().toPace(false), true) +
|
|
||||||
" per mile Lap time of " +
|
|
||||||
buildAndGetTimeText(lastInterval.getSpeed().toPace(false), true) +
|
|
||||||
" per mile",
|
|
||||||
announcement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Test
|
||||||
* Builds an returns the text representing the duration's time.
|
public void getAnnouncement_heart_rate_and_sensor_statistics() {
|
||||||
*
|
PreferencesUtils.setVoiceAnnounceAverageHeartRate(true);
|
||||||
* @param duration Duration object.
|
PreferencesUtils.setVoiceAnnounceLapHeartRate(true);
|
||||||
* @param showFromMinutes show minutes tough it's 0.
|
|
||||||
* @return text representing the duratin's time.
|
|
||||||
*/
|
|
||||||
private String buildAndGetTimeText(Duration duration, boolean showFromMinutes) {
|
|
||||||
long hours = Math.abs(duration.getSeconds()) / 3600;
|
|
||||||
long minutes = (Math.abs(duration.getSeconds()) % 3600) / 60;
|
|
||||||
long seconds = Math.abs(duration.getSeconds()) % 60;
|
|
||||||
String hUnit = hours > 1 || hours == 0 ? "hours" : "hour";
|
|
||||||
String mUnit = minutes > 1 || minutes == 0 ? "minutes" : "minute";
|
|
||||||
String sUnit = seconds > 1 || seconds == 0 ? "seconds" : "second";
|
|
||||||
|
|
||||||
String res = hours > 0 ? hours + " " + hUnit : "";
|
int numberOfPoints = 1000;
|
||||||
if (hours > 0) {
|
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
|
||||||
res += minutes > 0 || showFromMinutes ? " " + minutes + " " + mUnit : "";
|
Track.Id trackId = trackWithStats.first;
|
||||||
} else {
|
TrackStatistics stats = trackWithStats.second;
|
||||||
res += minutes > 0 || showFromMinutes ? minutes + " " + mUnit : "";
|
IntervalStatistics.Interval lastInterval;
|
||||||
|
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||||
|
assertEquals(trackPointIterator.getCount(), numberOfPoints);
|
||||||
|
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||||
|
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||||
|
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||||
}
|
}
|
||||||
if (hours > 0 || minutes > 0 || showFromMinutes) {
|
|
||||||
res += " " + seconds + " " + sUnit;
|
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null);
|
||||||
} else {
|
|
||||||
res += seconds + " " + sUnit;
|
// when
|
||||||
}
|
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
|
||||||
return res;
|
|
||||||
|
// then
|
||||||
|
assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour. Average heart rate 180 bpm. Current heart rate 133 bpm.", announcement);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Test
|
||||||
|
public void getAnnouncement_only_lap_heart_rate() {
|
||||||
|
PreferencesUtils.setVoiceAnnounceLapHeartRate(true);
|
||||||
|
PreferencesUtils.setVoiceAnnounceAverageHeartRate(false);
|
||||||
|
PreferencesUtils.setVoiceAnnounceTotalDistance(false);
|
||||||
|
PreferencesUtils.setVoiceAnnounceMovingTime(false);
|
||||||
|
PreferencesUtils.setVoiceAnnounceAverageSpeedPace(false);
|
||||||
|
PreferencesUtils.setVoiceAnnounceLapSpeedPace(false);
|
||||||
|
|
||||||
|
int numberOfPoints = 1000;
|
||||||
|
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
|
||||||
|
Track.Id trackId = trackWithStats.first;
|
||||||
|
TrackStatistics stats = trackWithStats.second;
|
||||||
|
IntervalStatistics.Interval lastInterval;
|
||||||
|
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||||
|
assertEquals(trackPointIterator.getCount(), numberOfPoints);
|
||||||
|
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||||
|
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||||
|
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null);
|
||||||
|
|
||||||
|
// when
|
||||||
|
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(" Current heart rate 133 bpm.", announcement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import org.mockito.junit.MockitoJUnitRunner;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
@@ -62,7 +61,7 @@ public class GPSHandlerTest {
|
|||||||
locationHandler.onLocationChanged(createLocation(45f, 35f, 3, 5, System.currentTimeMillis()));
|
locationHandler.onLocationChanged(createLocation(45f, 35f, 3, 5, System.currentTimeMillis()));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(trackPointCreator, times(1)).onNewTrackPoint(any(TrackPoint.class));
|
verify(trackPointCreator, times(1)).onChange(any(Location.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,7 +77,7 @@ public class GPSHandlerTest {
|
|||||||
locationHandler.onLocationChanged(createLocation(latitude, 35f, 3, 5, System.currentTimeMillis()));
|
locationHandler.onLocationChanged(createLocation(latitude, 35f, 3, 5, System.currentTimeMillis()));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(trackPointCreator, times(0)).onNewTrackPoint(any(TrackPoint.class));
|
verify(trackPointCreator, times(0)).onChange(any(Location.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,7 +93,7 @@ public class GPSHandlerTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
// no newTrackPoint called
|
// no newTrackPoint called
|
||||||
verify(trackPointCreator, times(0)).onNewTrackPoint(any(TrackPoint.class));
|
verify(trackPointCreator, times(0)).onChange(any(Location.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -107,7 +106,7 @@ public class GPSHandlerTest {
|
|||||||
locationHandler.onLocationChanged(createLocation(99.0, 35.0, Long.MAX_VALUE, 15, System.currentTimeMillis()));
|
locationHandler.onLocationChanged(createLocation(99.0, 35.0, Long.MAX_VALUE, 15, System.currentTimeMillis()));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(trackPointCreator, times(1)).onNewTrackPoint(any(TrackPoint.class));
|
verify(trackPointCreator, times(1)).onChange(any(Location.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public class TrackPointCreatorTest {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
subject = new TrackPointCreator(locationHandler, server);
|
subject = new TrackPointCreator(locationHandler, server);
|
||||||
subject.start(context);
|
subject.start(context, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class PreferencesUtilsTest {
|
|||||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat();
|
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
|
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES, trackFileFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -73,7 +73,7 @@ public class PreferencesUtilsTest {
|
|||||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat();
|
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
|
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES, trackFileFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import androidx.test.core.app.ApplicationProvider;
|
|||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
@@ -31,7 +32,9 @@ import java.time.Duration;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.LocaleRule;
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
import de.dennisguse.opentracks.data.models.Speed;
|
||||||
@@ -47,6 +50,9 @@ import de.dennisguse.opentracks.util.StringUtils;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class DescriptionGeneratorTest {
|
public class DescriptionGeneratorTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final LocaleRule mLocaleRule = new LocaleRule(Locale.ENGLISH);
|
||||||
|
|
||||||
private static final Instant START_TIME = Instant.ofEpochMilli(1288721514000L);
|
private static final Instant START_TIME = Instant.ofEpochMilli(1288721514000L);
|
||||||
private DescriptionGenerator descriptionGenerator;
|
private DescriptionGenerator descriptionGenerator;
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import java.time.Duration;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
|
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
import de.dennisguse.opentracks.data.models.Speed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,6 +68,7 @@ public class TrackStatisticsTest {
|
|||||||
assertEquals(0.0, statistics.getMaxSpeed().toMPS(), 0.0);
|
assertEquals(0.0, statistics.getMaxSpeed().toMPS(), 0.0);
|
||||||
assertEquals(0.0, statistics.getAverageSpeed().toMPS(), 0.0);
|
assertEquals(0.0, statistics.getAverageSpeed().toMPS(), 0.0);
|
||||||
assertEquals(0.0, statistics.getAverageMovingSpeed().toMPS(), 0.0);
|
assertEquals(0.0, statistics.getAverageMovingSpeed().toMPS(), 0.0);
|
||||||
|
assertNull(statistics.getAverageHeartRate());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -91,6 +93,8 @@ public class TrackStatisticsTest {
|
|||||||
statistics.setMinAltitude(1200.0); // Resulting min altitude
|
statistics.setMinAltitude(1200.0); // Resulting min altitude
|
||||||
statistics2.setMaxAltitude(3575.0); // Resulting max altitude
|
statistics2.setMaxAltitude(3575.0); // Resulting max altitude
|
||||||
statistics2.setMinAltitude(2800.0);
|
statistics2.setMinAltitude(2800.0);
|
||||||
|
statistics.setAverageHeartRate(HeartRate.of(100f));
|
||||||
|
statistics2.setAverageHeartRate(HeartRate.of(200f));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
statistics.merge(statistics2);
|
statistics.merge(statistics2);
|
||||||
@@ -105,6 +109,7 @@ public class TrackStatisticsTest {
|
|||||||
assertEquals(Speed.of(statistics.getTotalDistance(), statistics.getMovingTime()).toMPS(), statistics.getMaxSpeed().toMPS(), 0.001);
|
assertEquals(Speed.of(statistics.getTotalDistance(), statistics.getMovingTime()).toMPS(), statistics.getMaxSpeed().toMPS(), 0.001);
|
||||||
assertEquals(1200.0, statistics.getMinAltitude(), 0.001);
|
assertEquals(1200.0, statistics.getMinAltitude(), 0.001);
|
||||||
assertEquals(3575.0, statistics.getMaxAltitude(), 0.001);
|
assertEquals(3575.0, statistics.getMaxAltitude(), 0.001);
|
||||||
|
assertEquals(150.0, statistics.getAverageHeartRate().getBPM(), 0.001);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ public class TrackStatisticsUpdaterTest {
|
|||||||
|
|
||||||
assertNull(statistics.getTotalAltitudeGain());
|
assertNull(statistics.getTotalAltitudeGain());
|
||||||
assertNull(statistics.getTotalAltitudeLoss());
|
assertNull(statistics.getTotalAltitudeLoss());
|
||||||
|
assertNull(statistics.getAverageHeartRate());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -66,6 +67,7 @@ public class TrackStatisticsUpdaterTest {
|
|||||||
|
|
||||||
assertNull(statistics.getTotalAltitudeGain());
|
assertNull(statistics.getTotalAltitudeGain());
|
||||||
assertNull(statistics.getTotalAltitudeLoss());
|
assertNull(statistics.getTotalAltitudeLoss());
|
||||||
|
assertNull(statistics.getAverageHeartRate());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -84,13 +86,14 @@ public class TrackStatisticsUpdaterTest {
|
|||||||
assertEquals(Duration.ofSeconds(9), statistics.getMovingTime());
|
assertEquals(Duration.ofSeconds(9), statistics.getMovingTime());
|
||||||
|
|
||||||
assertEquals(2.5, statistics.getMinAltitude(), 0.01);
|
assertEquals(2.5, statistics.getMinAltitude(), 0.01);
|
||||||
assertEquals(28.75, statistics.getMaxAltitude(), 0.01);
|
assertEquals(32.5, statistics.getMaxAltitude(), 0.01);
|
||||||
assertEquals(36, statistics.getTotalAltitudeGain(), 0.01);
|
assertEquals(36, statistics.getTotalAltitudeGain(), 0.01);
|
||||||
assertEquals(36, statistics.getTotalAltitudeLoss(), 0.01);
|
assertEquals(36, statistics.getTotalAltitudeLoss(), 0.01);
|
||||||
|
|
||||||
assertEquals(14.226, statistics.getMaxSpeed().toMPS(), 0.01);
|
assertEquals(14.226, statistics.getMaxSpeed().toMPS(), 0.01);
|
||||||
assertEquals(14.226, statistics.getAverageMovingSpeed().toMPS(), 0.01);
|
assertEquals(14.226, statistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||||
assertEquals(9.84, statistics.getAverageSpeed().toMPS(), 0.01);
|
assertEquals(9.84, statistics.getAverageSpeed().toMPS(), 0.01);
|
||||||
|
assertEquals(106.85f, statistics.getAverageHeartRate().getBPM(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
|||||||
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
|
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||||
|
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -58,13 +59,13 @@ public class EspressoAggregatedFilterTest {
|
|||||||
@Test
|
@Test
|
||||||
public void espressoAggregatedFilterTest() {
|
public void espressoAggregatedFilterTest() {
|
||||||
// open AggregatedStatisticsActivity through toolbar's menu item
|
// open AggregatedStatisticsActivity through toolbar's menu item
|
||||||
onView(ViewMatchers.withId(R.id.toolbar)).check(matches(isDisplayed()));
|
onView(ViewMatchers.withId(R.id.bottom_app_bar)).check(matches(isDisplayed()));
|
||||||
onView(withId(R.id.toolbar)).check(matches(hasDescendant(withId(R.id.track_list_aggregated_stats))));
|
onView(withId(R.id.bottom_app_bar)).check(matches(hasDescendant(withId(R.id.track_list_aggregated_stats))));
|
||||||
onView(withId(R.id.track_list_aggregated_stats)).perform(click());
|
onView(withId(R.id.track_list_aggregated_stats)).perform(click());
|
||||||
|
|
||||||
// open FilterDialogFragment through toolbar's menu item
|
// open FilterDialogFragment through toolbar's menu item
|
||||||
onView(withId(R.id.toolbar)).check(matches(isDisplayed()));
|
onView(withId(R.id.bottom_app_bar)).check(matches(isDisplayed()));
|
||||||
onView(withId(R.id.toolbar)).check(matches(hasDescendant(withId(R.id.aggregated_statistics_filter))));
|
onView(withId(R.id.bottom_app_bar)).check(matches(hasDescendant(withId(R.id.aggregated_statistics_filter))));
|
||||||
onView(withId(R.id.aggregated_statistics_filter)).perform(click());
|
onView(withId(R.id.aggregated_statistics_filter)).perform(click());
|
||||||
|
|
||||||
// check there's a checkbox with CATEGORY text
|
// check there's a checkbox with CATEGORY text
|
||||||
@@ -73,7 +74,7 @@ public class EspressoAggregatedFilterTest {
|
|||||||
withParent(allOf(withId(R.id.filter_items),
|
withParent(allOf(withId(R.id.filter_items),
|
||||||
withParent(IsInstanceOf.<View>instanceOf(android.view.ViewGroup.class)))),
|
withParent(IsInstanceOf.<View>instanceOf(android.view.ViewGroup.class)))),
|
||||||
isDisplayed()));
|
isDisplayed()));
|
||||||
checkBox.check(matches(isDisplayed()));
|
checkBox.perform(waitFor(2000));
|
||||||
|
|
||||||
// check there's an edit text for "from date"
|
// check there's an edit text for "from date"
|
||||||
onView(withId(R.id.filter_date_edit_text_from)).check(matches(isDisplayed()));
|
onView(withId(R.id.filter_date_edit_text_from)).check(matches(isDisplayed()));
|
||||||
@@ -81,4 +82,5 @@ public class EspressoAggregatedFilterTest {
|
|||||||
// check there's an edit text for "to date"
|
// check there's an edit text for "to date"
|
||||||
onView(withId(R.id.filter_date_edit_text_to)).check(matches(isDisplayed()));
|
onView(withId(R.id.filter_date_edit_text_to)).check(matches(isDisplayed()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,32 +3,23 @@ package de.dennisguse.opentracks.ui.customRecordingLayout;
|
|||||||
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
import static androidx.test.espresso.Espresso.onView;
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
import static androidx.test.espresso.action.ViewActions.click;
|
||||||
|
import static androidx.test.espresso.action.ViewActions.longClick;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.assertThat;
|
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 androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.ViewParent;
|
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.test.espresso.NoMatchingViewException;
|
import androidx.test.espresso.NoMatchingViewException;
|
||||||
import androidx.test.espresso.UiController;
|
|
||||||
import androidx.test.espresso.ViewAction;
|
|
||||||
import androidx.test.espresso.ViewAssertion;
|
import androidx.test.espresso.ViewAssertion;
|
||||||
import androidx.test.espresso.ViewInteraction;
|
import androidx.test.espresso.ViewInteraction;
|
||||||
import androidx.test.espresso.action.MotionEvents;
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers;
|
import androidx.test.espresso.matcher.ViewMatchers;
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.LargeTest;
|
import androidx.test.filters.LargeTest;
|
||||||
import androidx.test.rule.GrantPermissionRule;
|
import androidx.test.rule.GrantPermissionRule;
|
||||||
|
|
||||||
import org.hamcrest.Description;
|
|
||||||
import org.hamcrest.Matcher;
|
|
||||||
import org.hamcrest.TypeSafeMatcher;
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@@ -50,7 +41,7 @@ public class EspressoCustomLayoutTest {
|
|||||||
@Test
|
@Test
|
||||||
public void customLayoutTest() {
|
public void customLayoutTest() {
|
||||||
// TrackListActivity: start recording
|
// TrackListActivity: start recording
|
||||||
ViewInteraction trackControllerRecordButton = onView(ViewMatchers.withId(R.id.controller_record));
|
ViewInteraction trackControllerRecordButton = onView(ViewMatchers.withId(R.id.track_list_fab_action));
|
||||||
trackControllerRecordButton.perform(click());
|
trackControllerRecordButton.perform(click());
|
||||||
|
|
||||||
// Get custom layout preferences and check all data fields are showed.
|
// Get custom layout preferences and check all data fields are showed.
|
||||||
@@ -59,8 +50,8 @@ public class EspressoCustomLayoutTest {
|
|||||||
onView(withId(R.id.stats_recycler_view)).check(new RecyclerViewItemCountAssertion((int) layout.getFields().stream().filter(DataField::isVisible).count()));
|
onView(withId(R.id.stats_recycler_view)).check(new RecyclerViewItemCountAssertion((int) layout.getFields().stream().filter(DataField::isVisible).count()));
|
||||||
|
|
||||||
// stop recording
|
// stop recording
|
||||||
ViewInteraction trackControllerStopButton = onView(withId(R.id.controller_stop));
|
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
|
||||||
trackControllerStopButton.perform(veryLongTouch(1600));
|
trackControllerStopButton.perform(longClick());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class RecyclerViewItemCountAssertion implements ViewAssertion {
|
private static class RecyclerViewItemCountAssertion implements ViewAssertion {
|
||||||
@@ -82,51 +73,4 @@ public class EspressoCustomLayoutTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package de.dennisguse.opentracks.util;
|
||||||
|
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewParent;
|
||||||
|
import android.widget.ListView;
|
||||||
|
|
||||||
|
import androidx.test.espresso.UiController;
|
||||||
|
import androidx.test.espresso.ViewAction;
|
||||||
|
|
||||||
|
import com.google.android.material.tabs.TabLayout;
|
||||||
|
|
||||||
|
import org.hamcrest.Description;
|
||||||
|
import org.hamcrest.Matcher;
|
||||||
|
import org.hamcrest.TypeSafeMatcher;
|
||||||
|
|
||||||
|
public class EspressoUtils {
|
||||||
|
|
||||||
|
public 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 Matcher<View> withListSize (final int 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");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ViewAction selectTabAtIndex(final int index) {
|
||||||
|
return new ViewAction() {
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Selecting tab.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Matcher<View> getConstraints() {
|
||||||
|
return isDisplayed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void perform(UiController uiController, View view) {
|
||||||
|
TabLayout tabLayout = (TabLayout) view;
|
||||||
|
tabLayout.getTabAt(index).select();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.util;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class ExportUtilsTest extends TestCase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIsExportFileExists_exists() {
|
|
||||||
// given
|
|
||||||
UUID uuid = UUID.randomUUID();
|
|
||||||
String format = "kmz";
|
|
||||||
List<String> fileNames = new ArrayList<>();
|
|
||||||
fileNames.add(uuid.toString().substring(0, 8) + "_name.gpx");
|
|
||||||
fileNames.add(uuid.toString().substring(0, 8) + "_other name name.kmz");
|
|
||||||
|
|
||||||
// when
|
|
||||||
boolean exists = ExportUtils.isExportFileExists(uuid, format, fileNames);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertTrue(exists);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIsExportFileExists_not_exists1() {
|
|
||||||
// given
|
|
||||||
UUID uuid = UUID.randomUUID();
|
|
||||||
String format = "gpx";
|
|
||||||
List<String> fileNames = new ArrayList<>();
|
|
||||||
fileNames.add(UUID.randomUUID().toString().substring(0, 8) + "_name.gpx");
|
|
||||||
fileNames.add(UUID.randomUUID().toString().substring(0, 8) + "_other name name.gpx");
|
|
||||||
|
|
||||||
// when
|
|
||||||
boolean exists = ExportUtils.isExportFileExists(uuid, format, fileNames);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertFalse(exists);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIsExportFileExists_not_exists2() {
|
|
||||||
// given
|
|
||||||
UUID uuid = UUID.randomUUID();
|
|
||||||
String format = "kmz";
|
|
||||||
List<String> fileNames = new ArrayList<>();
|
|
||||||
fileNames.add(uuid.toString().substring(0, 8) + "_name.gpx");
|
|
||||||
fileNames.add(UUID.randomUUID().toString().substring(0, 8) + "_other name name.gpx");
|
|
||||||
|
|
||||||
// when
|
|
||||||
boolean exists = ExportUtils.isExportFileExists(uuid, format, fileNames);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertFalse(exists);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,6 +24,7 @@ import android.content.Context;
|
|||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
@@ -45,8 +46,7 @@ import java.util.Locale;
|
|||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.LocaleRule;
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link StringUtils}.
|
* Tests for {@link StringUtils}.
|
||||||
@@ -56,6 +56,9 @@ import de.dennisguse.opentracks.data.models.Speed;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class StringUtilsTest {
|
public class StringUtilsTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final LocaleRule mLocaleRule = new LocaleRule(Locale.ENGLISH);
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,21 +89,6 @@ public class StringUtilsTest {
|
|||||||
assertEquals("100:00:00", StringUtils.formatElapsedTimeWithHour(Duration.ofMillis(360000000)));
|
assertEquals("100:00:00", StringUtils.formatElapsedTimeWithHour(Duration.ofMillis(360000000)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link StringUtils#formatDistance(android.content.Context, Distance, boolean)}.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testFormatDistance() {
|
|
||||||
// A large number in metric
|
|
||||||
assertEquals("5.00 km", StringUtils.formatDistance(context, Distance.of(5000), true));
|
|
||||||
// A large number in imperial
|
|
||||||
assertEquals("3.11 mi", StringUtils.formatDistance(context, Distance.of(5000), false));
|
|
||||||
// A small number in metric
|
|
||||||
assertEquals("100.00 m", StringUtils.formatDistance(context, Distance.of(100), true));
|
|
||||||
// A small number in imperial
|
|
||||||
assertEquals("328.08 ft", StringUtils.formatDistance(context, Distance.of(100), false));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link StringUtils#formatCData(String)}.
|
* Tests {@link StringUtils#formatCData(String)}.
|
||||||
*/
|
*/
|
||||||
@@ -178,27 +166,6 @@ public class StringUtilsTest {
|
|||||||
assertEquals("1.0", StringUtils.formatDecimal(0.99, 1));
|
assertEquals("1.0", StringUtils.formatDecimal(0.99, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetSpeedParts() {
|
|
||||||
assertEquals("4:59", StringUtils.getSpeedParts(context, Speed.of(3.34), true, false).first);
|
|
||||||
assertEquals("5:00", StringUtils.getSpeedParts(context, Speed.of(3.33), true, false).first);
|
|
||||||
|
|
||||||
assertEquals("11.9", StringUtils.getSpeedParts(context, Speed.of(3.31), true, true).first);
|
|
||||||
assertEquals("7.5", StringUtils.getSpeedParts(context, Speed.of(3.34), false, true).first);
|
|
||||||
|
|
||||||
assertEquals("min/km", StringUtils.getSpeedParts(context, Speed.zero(), true, false).second);
|
|
||||||
assertEquals("min/mi", StringUtils.getSpeedParts(context, Speed.zero(), false, false).second);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFormatSpeed() {
|
|
||||||
assertEquals("4:59 min/km", StringUtils.formatSpeed(context, Speed.of(3.34), true, false));
|
|
||||||
assertEquals("8:02 min/mi", StringUtils.formatSpeed(context, Speed.of(3.34), false, false));
|
|
||||||
|
|
||||||
assertEquals("12.0 km/h", StringUtils.formatSpeed(context, Speed.of(3.34), true, true));
|
|
||||||
assertEquals("7.5 mph", StringUtils.formatSpeed(context, Speed.of(3.34), false, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFormatDateTodayRelative() {
|
public void testFormatDateTodayRelative() {
|
||||||
// given
|
// given
|
||||||
|
|||||||
@@ -23,11 +23,14 @@ import android.content.Context;
|
|||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.LocaleRule;
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
@@ -40,6 +43,9 @@ import de.dennisguse.opentracks.settings.PreferencesUtils;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class TrackNameUtilsTest {
|
public class TrackNameUtilsTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final LocaleRule mLocaleRule = new LocaleRule(Locale.ENGLISH);
|
||||||
|
|
||||||
private static final Track.Id TRACK_ID = new Track.Id(1L);
|
private static final Track.Id TRACK_ID = new Track.Id(1L);
|
||||||
private static final OffsetDateTime START_TIME = OffsetDateTime.parse("2022-01-02T10:15:30.1234+01:00");
|
private static final OffsetDateTime START_TIME = OffsetDateTime.parse("2022-01-02T10:15:30.1234+01:00");
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import de.dennisguse.opentracks.data.models.HeartRate;
|
|||||||
import de.dennisguse.opentracks.data.models.Power;
|
import de.dennisguse.opentracks.data.models.Power;
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
import de.dennisguse.opentracks.services.RecordingData;
|
import de.dennisguse.opentracks.services.RecordingData;
|
||||||
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
||||||
@@ -61,7 +62,7 @@ public class StatisticDataBuilderTest extends TestCase {
|
|||||||
layout.addField(context.getString(R.string.stats_custom_layout_max_speed_key), context.getString(R.string.stats_max_speed), false, true, false);
|
layout.addField(context.getString(R.string.stats_custom_layout_max_speed_key), context.getString(R.string.stats_max_speed), false, true, false);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(context, recordingDataMock, layout, true);
|
List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(context, recordingDataMock, layout, UnitSystem.METRIC);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(statisticDataList.size(), 3);
|
assertEquals(statisticDataList.size(), 3);
|
||||||
@@ -100,7 +101,7 @@ public class StatisticDataBuilderTest extends TestCase {
|
|||||||
layout.addField(context.getString(R.string.stats_custom_layout_power_key), context.getString(R.string.stats_sensors_power), true, true, false);
|
layout.addField(context.getString(R.string.stats_custom_layout_power_key), context.getString(R.string.stats_sensors_power), true, true, false);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(context, recordingDataMock, layout, true);
|
List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(context, recordingDataMock, layout, UnitSystem.METRIC);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(statisticDataList.size(), 18);
|
assertEquals(statisticDataList.size(), 18);
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#time,trackpoint_type,latitude,longitude,altitude,accuracy_horizontal,accuracy_vertical,speed,altitude_gain,altitude_loss,sensor_distance,heartrate,cadence,power
|
||||||
|
"2020-02-02T03:02:02+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,,
|
||||||
|
"2020-02-02T03:02:03+01:00","TRACKPOINT",3,3,10,10,,54,1,1,,,,
|
||||||
|
"2020-02-02T03:02:04+01:00","SENSORPOINT",,,,,,54,1,1,10,66,3,50
|
||||||
|
"2020-02-02T03:02:05+01:00","TRACKPOINT",3,3,10,10,,18,0,0,2,66,3,50
|
||||||
|
"2020-02-02T03:02:06+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
|
||||||
|
"2020-02-02T03:02:20+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,,
|
||||||
|
"2020-02-02T03:02:21+01:00","TRACKPOINT",3,3,10,10,,54,0,0,,,,
|
||||||
|
"2020-02-02T03:02:22+01:00","SEGMENT_START_AUTOMATIC",3,3,10,10,,54,0,0,,,,
|
||||||
|
"2020-02-02T03:02:23+01:00","TRACKPOINT",3,3,10,10,,54,0,0,,,,
|
||||||
|
"2020-02-02T03:02:24+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
|
||||||
|