mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-01 01:02:21 +02:00
Compare commits
250 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c691f6faba | |||
| 58c4b38c0b | |||
| 0225497d88 | |||
| 1834f943e4 | |||
| 7505e6d693 | |||
| d5e83cf057 | |||
| f8279c7095 | |||
| 465e3e9be8 | |||
| f38999a970 | |||
| 4dfd2485bf | |||
| 10273cd772 | |||
| fbfe6f3ab8 | |||
| 86726c5e0e | |||
| b711c4fdc5 | |||
| cefdb1280a | |||
| 91b379c328 | |||
| e28a84fc88 | |||
| c2d8d06204 | |||
| 7ca871c699 | |||
| 337c50eb91 | |||
| 2ff8d21897 | |||
| 2d4e987812 | |||
| fb928ccc45 | |||
| ab8791a620 | |||
| 7c03085761 | |||
| 12a8686896 | |||
| e12bb967e6 | |||
| d56b018676 | |||
| a6fd701004 | |||
| ce26e20571 | |||
| d3ff2061c6 | |||
| aa63474114 | |||
| cf545dc17b | |||
| a542f45661 | |||
| 1eeb706330 | |||
| 01ea65742b | |||
| e9456919cb | |||
| 2a8e1c99b6 | |||
| 245854581b | |||
| e372a780f0 | |||
| 25a2f978c4 | |||
| 36c3885dce | |||
| 00be0b565c | |||
| 8b1e1cf294 | |||
| ce313ba4fd | |||
| df501bde0d | |||
| 30ed8d8ba3 | |||
| ea2127846d | |||
| ac0da5e9d1 | |||
| 47fea690c9 | |||
| a1fa95094f | |||
| c4a1fb0d98 | |||
| 57cd4b3780 | |||
| 5c645712ff | |||
| e724baaafa | |||
| f55dcd7162 | |||
| 74169cb819 | |||
| 1821f47cf1 | |||
| 925a253bbb | |||
| e89ae1c2df | |||
| 75058118e0 | |||
| 96559230f8 | |||
| f16f2cfc64 | |||
| 7f353a5d13 | |||
| 2453edaa65 | |||
| 1b1568511a | |||
| 29a36c3ea7 | |||
| 71d0c7cc92 | |||
| b09d640844 | |||
| a039f1a00c | |||
| 3df5c06c20 | |||
| 2904c65a01 | |||
| 4977789004 | |||
| 95e6448cba | |||
| a66c1b9407 | |||
| 9d55ac261f | |||
| dbd3e2fc5c | |||
| 713e454e44 | |||
| f0616fc043 | |||
| 75ed3120bd | |||
| b22e41cf66 | |||
| d1f003c0ce | |||
| 6ef80ade98 | |||
| e49d9fbd83 | |||
| c7738b1cb2 | |||
| 713f5c7708 | |||
| b9a36d1b3c | |||
| 34e89ea3f9 | |||
| 50a0e37086 | |||
| 11ac6b7279 | |||
| aadb79bf7d | |||
| da6b26644e | |||
| b264655d75 | |||
| 1ea9c0b216 | |||
| 8040edac7a | |||
| 037c79a1c5 | |||
| bc46f7447e | |||
| 6e88a448b1 | |||
| 2dac8d3ea6 | |||
| 0844c48d25 | |||
| 9607fda962 | |||
| 77ed28a8e2 | |||
| 4283e6d316 | |||
| ee030f32f5 | |||
| fb25055f3e | |||
| 9cdf87b998 | |||
| e9217b5327 | |||
| b4cb606499 | |||
| d1975685bc | |||
| 8cd9dda1f5 | |||
| c072967dd1 | |||
| 94e91d2063 | |||
| 796fa216f6 | |||
| f143aabfbf | |||
| d58342233c | |||
| 91c254ba7f | |||
| 19edb47554 | |||
| 1129c96562 | |||
| 7224efa8a9 | |||
| 149f8f809e | |||
| c2f59c11bf | |||
| 8b7d4fa813 | |||
| 81081b0ee5 | |||
| def4f3a1b9 | |||
| b679f00cc5 | |||
| 40494557d4 | |||
| a94f023a17 | |||
| ee105a1610 | |||
| 3bec73e074 | |||
| 1155a56e49 | |||
| 9b7e858d1d | |||
| d1490fc343 | |||
| 3f6a4975fa | |||
| b5408eabcd | |||
| e1e34452ae | |||
| df121b40d7 | |||
| 4e737616cb | |||
| fb963781af | |||
| 96339b6a36 | |||
| d043842901 | |||
| cefb6b3abf | |||
| 3ee6d43e3f | |||
| 2edd9a1f05 | |||
| f9a4fcfa0d | |||
| 6bed9c5ddf | |||
| 4e400c60f6 | |||
| ca6f973360 | |||
| edb0e7f7bb | |||
| 876b013c2e | |||
| 7fb27cbe95 | |||
| 6d519e2fc1 | |||
| c2f3b50767 | |||
| a95c6a1857 | |||
| f45dad167b | |||
| 88c75d31a8 | |||
| d49e7749f3 | |||
| 8d3e953220 | |||
| 6566904906 | |||
| 1d0e8f59b7 | |||
| be7987f88a | |||
| a92a1e7838 | |||
| 8cba53cfe4 | |||
| fcedb9c444 | |||
| 2ec82af8f1 | |||
| 447d1bb7a3 | |||
| 2f6765e45f | |||
| 90bf2e6ab0 | |||
| d12423522e | |||
| 24f052491b | |||
| 6b4e7d7d9b | |||
| 00fcbb070a | |||
| 708cbf0b2a | |||
| 60e2fe57f9 | |||
| 14cebf3fec | |||
| b651a8ca6a | |||
| d75472ef11 | |||
| 0b849c3d55 | |||
| 1c767c41a7 | |||
| dfa80d61d7 | |||
| fa558c9ab7 | |||
| a1a363698f | |||
| b79acfb942 | |||
| 24ea718ff2 | |||
| 6d187fa66d | |||
| ea27db13fa | |||
| 29d2e7c78c | |||
| 4b7badbecc | |||
| 0dcb8010e2 | |||
| 0a9d4b207c | |||
| c6cb388ce4 | |||
| e6e009ba14 | |||
| f8aa456549 | |||
| 167d90332e | |||
| 719a052f34 | |||
| 244ca26210 | |||
| 476f3a517b | |||
| 289e93eb5d | |||
| 18ff96733e | |||
| 0f2c5adf26 | |||
| 74954df600 | |||
| 1a14fed287 | |||
| 744853ee91 | |||
| 597e1b2c7f | |||
| 8156993921 | |||
| 9890dc28bd | |||
| b65d83010d | |||
| 1a5d06f9c6 | |||
| 723c21d7ec | |||
| 8fe0c3be7d | |||
| e64792bf3f | |||
| 21bafff761 | |||
| c33c2de51d | |||
| a6b11c1c37 | |||
| 315788308b | |||
| 547c1c5c65 | |||
| 69efd6fb82 | |||
| 3a0524f96d | |||
| 05456b44ef | |||
| b449dad619 | |||
| ccefe36489 | |||
| 90e9ab6d2a | |||
| d1061fa6cd | |||
| c0c813001f | |||
| 51498bbcf9 | |||
| 4ef1fcefbc | |||
| 9a572130a7 | |||
| e931f5ab7c | |||
| 9affe28fad | |||
| 4905a37951 | |||
| 439671d0a4 | |||
| 825e8dda6e | |||
| 661c839c78 | |||
| 8eb42a7ecb | |||
| 660a55ff5a | |||
| 958697bcb3 | |||
| 6029eb0317 | |||
| 8b56d53a6a | |||
| e9deebea6c | |||
| 54844b9a9a | |||
| 738201c1c2 | |||
| 48b6be0bd1 | |||
| 6b924cd936 | |||
| 5f083de530 | |||
| 278f6323f1 | |||
| 5baa8fa21e | |||
| 707f4add71 | |||
| b583f8f23d | |||
| c847f622e4 | |||
| 70dd58f484 | |||
| 015352c989 |
@@ -13,7 +13,7 @@ jobs:
|
||||
image: 'ubuntu:android-24.04'
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
image: 'ubuntu:android-24.04'
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* Polar H10
|
||||
* Polar OH1
|
||||
Has updatable firmware that requires a user account at polar.com
|
||||
* Polar Loop Gen 2
|
||||
* Wahoo Tickr (Model: WFBTHR02)
|
||||
* Mi Band 3, Amazfit Band 5, Amazfit Bip. These devices are manufactured by Huami, and other Huami devices will very likely work. Theoretically, this sensor must be enabled, but testing reveals that it works even without enabling. For details on how to enable see [Gadgetbridge wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Huami-Heartrate-measurement#bluetooth-heart-rate-sensor)
|
||||
* Amazfit Neo. But first you have to enable "Discoverable" and "Activity heart rate sharing" in the official Zepp app. (after pairing with OpenTracks you can disable them again)
|
||||
@@ -44,7 +45,10 @@ However, often only one value is provided.
|
||||
We only support _Instantaneous Power_.
|
||||
|
||||
For cycling (incl. cadence):
|
||||
* Assioma Duo Powermeter
|
||||
|
||||
* Assioma
|
||||
* Duo Powermeter
|
||||
* Pro MX-2
|
||||
* Elite drivo II
|
||||
* QUARQ Red DZero Powermeter
|
||||
* Tacx Satori Smart
|
||||
@@ -109,3 +113,16 @@ Here is a list of the smartphones tested:
|
||||
| Google | Nexus 4 | LineageOS 18.1 | Ok |
|
||||
| Samsung | S4 | Android 11 | Ok |
|
||||
| Sony | Xperia XZ1 Compact | LineageOS 16.1 | Failure |
|
||||
|
||||
## Bosch Smart System eBike
|
||||
|
||||
OpenTracks also supports Bosch Smart System eBikes via Bosch's custom BLE service `00000010-eaa2-11e9-81b4-2a2ae2dbcce4`.
|
||||
|
||||
Tested with:
|
||||
|
||||
* Bosch Perfomance Line CX (SW-Version 16.9.0) / Kiox 300 (SW-Version 16.4.0)
|
||||
|
||||
Currently supported measurements:
|
||||
|
||||
* Human power
|
||||
* Cadence
|
||||
|
||||
+2
-5
@@ -11,9 +11,6 @@ CHANGELOG_FILE="$CHANGELOG_DIR/$VERSIONID.txt"
|
||||
|
||||
git diff --exit-code || echo -e "\nWARNING WARNING WARNING WARNING WARNING: this branch contains uncommit changes."
|
||||
|
||||
#TODO Execute all tests: we are destroying data, running on a real device.
|
||||
# ./gradlew connectedCheck
|
||||
|
||||
# Changelog
|
||||
echo -e "\nPrevious changelog\n------------"
|
||||
PREVIOUS_CHANGELOG=$(find "$CHANGELOG_DIR" -iname "[[:digit:]]**.txt" | sort -n | tail -1)
|
||||
@@ -34,8 +31,8 @@ gedit -w "$CHANGELOG_FILE"
|
||||
git add "$CHANGELOG_FILE"
|
||||
|
||||
echo "Updating build.gradle"
|
||||
sed -i s/versionCode\ .*/versionCode\ "$VERSIONID"/g build.gradle
|
||||
sed -i s/versionName\ .*/versionName\ "\"$VERSIONNAME\""/g build.gradle
|
||||
sed -i -e "s/versionCode\ [0-9]\{1,\}/versionCode\ $VERSIONID/g" build.gradle
|
||||
sed -i -e "s/versionName\ \"v[0-9\.]\{1,\}\"/versionName\ \"$VERSIONNAME\"/g" build.gradle
|
||||
|
||||
git diff
|
||||
|
||||
|
||||
+7
-2
@@ -6,8 +6,13 @@ export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64/
|
||||
|
||||
JAVA_VERSION=`java --version`
|
||||
if [[ $JAVA_VERSION != "openjdk 21."* ]]; then
|
||||
echo "OpenJDK version should be 21.X"
|
||||
exit -1;
|
||||
echo -e "OpenJDK version should be 21.X"
|
||||
fi
|
||||
|
||||
git diff --exit-code || echo -e "\nWARNING WARNING WARNING WARNING WARNING: this branch contains uncommit changes."
|
||||
|
||||
if [[ $(git log "$(git describe --tags --abbrev=0)..HEAD" --no-merges --oneline | wc -l) -gt 0 ]]; then
|
||||
echo -e "No version tag for this commit; please use RELEASE.sh";
|
||||
fi
|
||||
|
||||
RELEASE_STORE_PASSWORD=
|
||||
|
||||
+33
-32
@@ -6,7 +6,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.13.2'
|
||||
classpath 'com.android.tools.build:gradle:9.4.0'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@ allprojects {
|
||||
}
|
||||
|
||||
def getVersionName = { ->
|
||||
return 'git describe --tags'.execute().text.trim()
|
||||
return 'git describe --tags'.execute(null, project.rootDir).text.trim()
|
||||
}
|
||||
|
||||
def getVersionCode = { ->
|
||||
return Integer.valueOf('git rev-list HEAD --count'.execute().text.trim())
|
||||
def getVersionCodeNightly = { ->
|
||||
return Integer.valueOf('git rev-list HEAD --count'.execute(null, project.rootDir).text.trim())
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -33,11 +33,12 @@ android {
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
compileSdk = 36
|
||||
compileSdk = 37
|
||||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
buildConfig = true
|
||||
resValues = true
|
||||
}
|
||||
|
||||
androidResources {
|
||||
@@ -46,13 +47,13 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'de.dennisguse.opentracks'
|
||||
versionCode 6516
|
||||
versionName "v4.24.2"
|
||||
versionCode 6766
|
||||
versionName "v4.29.0"
|
||||
|
||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||
|
||||
minSdk = 26
|
||||
targetSdk = 36
|
||||
targetSdk = 37
|
||||
|
||||
testInstrumentationRunner 'de.dennisguse.opentracks.TestRunner'
|
||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||
@@ -106,6 +107,11 @@ android {
|
||||
nightly {
|
||||
dimension 'version'
|
||||
applicationId 'de.dennisguse.opentracks.nightly'
|
||||
|
||||
versionCode getVersionCodeNightly()
|
||||
versionName getVersionName()
|
||||
base.archivesName = "de.dennisguse.opentracks.nightly_${getVersionCodeNightly()}"
|
||||
|
||||
signingConfig = signingConfigs.nightly
|
||||
}
|
||||
irreproducible {
|
||||
@@ -120,27 +126,13 @@ android {
|
||||
// PlayStore: https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore
|
||||
dimension 'version'
|
||||
applicationId 'de.dennisguse.opentracks.playstore'
|
||||
|
||||
base.archivesName = "de.dennisguse.opentracks.playstore_${getVersionName()}_${android.defaultConfig.versionCode}"
|
||||
|
||||
signingConfig = signingConfigs.release
|
||||
}
|
||||
}
|
||||
|
||||
applicationVariants.configureEach { variant ->
|
||||
variant.resValue 'string', 'applicationId', variant.applicationId
|
||||
|
||||
variant.outputs.configureEach {
|
||||
if (variant.flavorName == 'nightly') {
|
||||
setVersionCodeOverride(getVersionCode())
|
||||
setVersionNameOverride(getVersionName())
|
||||
}
|
||||
|
||||
if (variant.flavorName == 'reproducible') {
|
||||
outputFileName = "${applicationId}_${variant.versionName}.apk"
|
||||
} else {
|
||||
outputFileName = "${applicationId}_${variant.versionCode}.apk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependenciesInfo {
|
||||
// Disables dependency metadata when building APKs.
|
||||
includeInApk = false
|
||||
@@ -153,24 +145,33 @@ android {
|
||||
dependencies {
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.7.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.8.0'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
implementation 'androidx.documentfile:documentfile:1.1.0'
|
||||
implementation 'androidx.gridlayout:gridlayout:1.1.0'
|
||||
implementation 'com.google.android.material:material:1.13.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
|
||||
implementation 'androidx.core:core:1.17.0'
|
||||
implementation 'com.google.android.material:material:1.14.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.2.2'
|
||||
implementation 'androidx.core:core:1.19.0'
|
||||
implementation 'androidx.core:core-splashscreen:1.2.0'
|
||||
implementation 'androidx.mediarouter:mediarouter:1.8.1'
|
||||
implementation 'androidx.core:core-location-altitude:1.0.0-beta01'
|
||||
implementation 'androidx.work:work-runtime:2.11.0'
|
||||
implementation 'androidx.core:core-location-altitude:1.0.0'
|
||||
implementation 'androidx.work:work-runtime:2.11.2'
|
||||
implementation 'androidx.health.connect:connect-client:1.1.0'
|
||||
|
||||
androidTestImplementation 'androidx.test:core:1.7.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
|
||||
androidTestImplementation 'androidx.test:rules:1.7.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.7.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.21.0'
|
||||
|
||||
androidTestUtil 'androidx.test:orchestrator:1.6.1'
|
||||
}
|
||||
|
||||
androidComponents {
|
||||
onVariants(selector().all()) { variant ->
|
||||
variant.resValues.put(
|
||||
new com.android.build.api.variant.impl.ResValueKeyImpl("string", "applicationId"),
|
||||
new com.android.build.api.variant.ResValue(variant.applicationId.get(), null)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+788
@@ -0,0 +1,788 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xsd:schema
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns="http://www.topografix.com/GPX/1/1"
|
||||
targetNamespace="http://www.topografix.com/GPX/1/1"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
GPX schema version 1.1 - For more information on GPX and this schema, visit http://www.topografix.com/gpx.asp
|
||||
|
||||
GPX uses the following conventions: all coordinates are relative to the WGS84 datum. All measurements are in metric units.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:element name="gpx" type="gpxType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
GPX is the root element in the XML file.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="gpxType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
GPX documents contain a metadata header, followed by waypoints, routes, and tracks. You can add your own elements
|
||||
to the extensions section of the GPX document.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="metadata" type="metadataType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Metadata about the file.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="wpt" type="wptType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
A list of waypoints.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="rte" type="rteType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
A list of routes.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="trk" type="trkType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
A list of tracks.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="extensions" type="extensionsType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
You can add extend GPX by adding your own elements from another schema here.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
|
||||
<xsd:attribute name="version" type="xsd:string" use="required" fixed="1.1">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
You must include the version number in your GPX document.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="creator" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
You must include the name or URL of the software that created your GPX document. This allows others to
|
||||
inform the creator of a GPX instance document that fails to validate.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="metadataType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Information about the GPX file, author, and copyright restrictions goes in the metadata section. Providing rich,
|
||||
meaningful information about your GPX files allows others to search for and use your GPS data.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence> <!-- elements must appear in this order -->
|
||||
<xsd:element name="name" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The name of the GPX file.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="desc" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
A description of the contents of the GPX file.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="author" type="personType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The person or organization who created the GPX file.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="copyright" type="copyrightType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Copyright and license information governing use of the file.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="link" type="linkType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
URLs associated with the location described in the file.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="time" type="xsd:dateTime" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The creation date of the file.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="keywords" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Keywords associated with the file. Search engines or databases can use this information to classify the data.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="bounds" type="boundsType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Minimum and maximum coordinates which describe the extent of the coordinates in the file.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="extensions" type="extensionsType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
You can add extend GPX by adding your own elements from another schema here.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="wptType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
wpt represents a waypoint, point of interest, or named feature on a map.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence> <!-- elements must appear in this order -->
|
||||
<!-- Position info -->
|
||||
<xsd:element name="ele" type="xsd:decimal" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Elevation (in meters) of the point.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="time" type="xsd:dateTime" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Creation/modification timestamp for element. Date and time in are in Univeral Coordinated Time (UTC), not local time! Conforms to ISO 8601 specification for date/time representation. Fractional seconds are allowed for millisecond timing in tracklogs.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="magvar" type="degreesType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Magnetic variation (in degrees) at the point
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="geoidheight" type="xsd:decimal" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Height (in meters) of geoid (mean sea level) above WGS84 earth ellipsoid. As defined in NMEA GGA message.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<!-- Description info -->
|
||||
<xsd:element name="name" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The GPS name of the waypoint. This field will be transferred to and from the GPS. GPX does not place restrictions on the length of this field or the characters contained in it. It is up to the receiving application to validate the field before sending it to the GPS.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="cmt" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
GPS waypoint comment. Sent to GPS as comment.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="desc" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
A text description of the element. Holds additional information about the element intended for the user, not the GPS.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="src" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Source of data. Included to give user some idea of reliability and accuracy of data. "Garmin eTrex", "USGS quad Boston North", e.g.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="link" type="linkType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Link to additional information about the waypoint.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="sym" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Text of GPS symbol name. For interchange with other programs, use the exact spelling of the symbol as displayed on the GPS. If the GPS abbreviates words, spell them out.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="type" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Type (classification) of the waypoint.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<!-- Accuracy info -->
|
||||
<xsd:element name="fix" type="fixType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Type of GPX fix.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="sat" type="xsd:nonNegativeInteger" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Number of satellites used to calculate the GPX fix.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="hdop" type="xsd:decimal" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Horizontal dilution of precision.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="vdop" type="xsd:decimal" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Vertical dilution of precision.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="pdop" type="xsd:decimal" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Position dilution of precision.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="ageofdgpsdata" type="xsd:decimal" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Number of seconds since last DGPS update.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="dgpsid" type="dgpsStationType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
ID of DGPS station used in differential correction.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="extensions" type="extensionsType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
You can add extend GPX by adding your own elements from another schema here.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
|
||||
<xsd:attribute name="lat" type="latitudeType" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The latitude of the point. This is always in decimal degrees, and always in WGS84 datum.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="lon" type="longitudeType" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The longitude of the point. This is always in decimal degrees, and always in WGS84 datum.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="rteType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
rte represents route - an ordered list of waypoints representing a series of turn points leading to a destination.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="name" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
GPS name of route.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="cmt" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
GPS comment for route.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="desc" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Text description of route for user. Not sent to GPS.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="src" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Source of data. Included to give user some idea of reliability and accuracy of data.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="link" type="linkType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Links to external information about the route.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="number" type="xsd:nonNegativeInteger" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
GPS route number.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="type" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Type (classification) of route.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="extensions" type="extensionsType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
You can add extend GPX by adding your own elements from another schema here.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="rtept" type="wptType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
A list of route points.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="trkType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
trk represents a track - an ordered list of points describing a path.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="name" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
GPS name of track.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="cmt" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
GPS comment for track.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="desc" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
User description of track.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="src" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Source of data. Included to give user some idea of reliability and accuracy of data.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="link" type="linkType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Links to external information about track.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="number" type="xsd:nonNegativeInteger" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
GPS track number.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="type" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Type (classification) of track.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="extensions" type="extensionsType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
You can add extend GPX by adding your own elements from another schema here.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="trkseg" type="trksegType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
A Track Segment holds a list of Track Points which are logically connected in order. To represent a single GPS track where GPS reception was lost, or the GPS receiver was turned off, start a new Track Segment for each continuous span of track data.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="extensionsType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
You can add extend GPX by adding your own elements from another schema here.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence>
|
||||
<xsd:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
You can add extend GPX by adding your own elements from another schema here.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:any>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="trksegType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
A Track Segment holds a list of Track Points which are logically connected in order. To represent a single GPS track where GPS reception was lost, or the GPS receiver was turned off, start a new Track Segment for each continuous span of track data.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence> <!-- elements must appear in this order -->
|
||||
<xsd:element name="trkpt" type="wptType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
A Track Point holds the coordinates, elevation, timestamp, and metadata for a single point in a track.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="extensions" type="extensionsType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
You can add extend GPX by adding your own elements from another schema here.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="copyrightType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Information about the copyright holder and any license governing use of this file. By linking to an appropriate license,
|
||||
you may place your data into the public domain or grant additional usage rights.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence> <!-- elements must appear in this order -->
|
||||
<xsd:element name="year" type="xsd:gYear" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Year of copyright.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="license" type="xsd:anyURI" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Link to external file containing license text.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="author" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Copyright holder (TopoSoft, Inc.)
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="linkType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
A link to an external resource (Web page, digital photo, video clip, etc) with additional information.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence> <!-- elements must appear in this order -->
|
||||
<xsd:element name="text" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Text of hyperlink.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="type" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Mime type of content (image/jpeg)
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="href" type="xsd:anyURI" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
URL of hyperlink.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="emailType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
An email address. Broken into two parts (id and domain) to help prevent email harvesting.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="id" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
id half of email address (billgates2004)
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="domain" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
domain half of email address (hotmail.com)
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="personType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
A person or organization.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence> <!-- elements must appear in this order -->
|
||||
<xsd:element name="name" type="xsd:string" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Name of person or organization.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="email" type="emailType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Email address.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="link" type="linkType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Link to Web site or other external information about person.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="ptType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
A geographic point with optional elevation and time. Available for use by other schemas.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence> <!-- elements must appear in this order -->
|
||||
<xsd:element name="ele" type="xsd:decimal" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The elevation (in meters) of the point.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="time" type="xsd:dateTime" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The time that the point was recorded.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="lat" type="latitudeType" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The latitude of the point. Decimal degrees, WGS84 datum.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="lon" type="longitudeType" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The latitude of the point. Decimal degrees, WGS84 datum.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="ptsegType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
An ordered sequence of points. (for polygons or polylines, e.g.)
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence> <!-- elements must appear in this order -->
|
||||
<xsd:element name="pt" type="ptType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Ordered list of geographic points.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="boundsType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Two lat/lon pairs defining the extent of an element.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="minlat" type="latitudeType" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The minimum latitude.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="minlon" type="longitudeType" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The minimum longitude.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="maxlat" type="latitudeType" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The maximum latitude.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="maxlon" type="longitudeType" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The maximum longitude.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
|
||||
<xsd:simpleType name="latitudeType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The latitude of the point. Decimal degrees, WGS84 datum.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:restriction base="xsd:decimal">
|
||||
<xsd:minInclusive value="-90.0"/>
|
||||
<xsd:maxInclusive value="90.0"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:simpleType name="longitudeType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The longitude of the point. Decimal degrees, WGS84 datum.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:restriction base="xsd:decimal">
|
||||
<xsd:minInclusive value="-180.0"/>
|
||||
<xsd:maxExclusive value="180.0"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:simpleType name="degreesType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Used for bearing, heading, course. Units are decimal degrees, true (not magnetic).
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:restriction base="xsd:decimal">
|
||||
<xsd:minInclusive value="0.0"/>
|
||||
<xsd:maxExclusive value="360.0"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:simpleType name="fixType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Type of GPS fix. none means GPS had no fix. To signify "the fix info is unknown, leave out fixType entirely. pps = military signal used
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="none"/>
|
||||
<xsd:enumeration value="2d"/>
|
||||
<xsd:enumeration value="3d"/>
|
||||
<xsd:enumeration value="dgps"/>
|
||||
<xsd:enumeration value="pps"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:simpleType name="dgpsStationType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Represents a differential GPS station.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:restriction base="xsd:integer">
|
||||
<xsd:minInclusive value="0"/>
|
||||
<xsd:maxInclusive value="1023"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
</xsd:schema>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,10 @@
|
||||
(versionName): OpenTracks
|
||||
|
||||
Ændringer:
|
||||
-
|
||||
|
||||
Fejlrettelser:
|
||||
-
|
||||
|
||||
Udvikler:
|
||||
-
|
||||
@@ -0,0 +1,13 @@
|
||||
v4.25.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Record button is centered
|
||||
- Support for BLE thermometer (Environmental Sensing)
|
||||
|
||||
Bugfix:
|
||||
- On import: get type from filename (not URI)
|
||||
|
||||
Developer:
|
||||
- TrackPoint is a record
|
||||
- Update to Android Gradle Plugin 9.0
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.25.1: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Since v4.24.0, the API for transferring data to OSM Dashboard was broken.
|
||||
Sadly, the changes in OpenTracks could not be reverted.
|
||||
Thus, the protocol version was bumped to 3.
|
||||
This requires updating OSM Dashboard to a version AFTER v6.5.7.
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.25.2: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Restored compatibility with OSMDashboard (Dashboard API protocol version 2)
|
||||
|
||||
Bugfix:
|
||||
- Update UI immediately after resuming a track
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
v4.26.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- GPS is optional: configurable in the settings
|
||||
- BLE sensor address/name is only shown after successful connection
|
||||
|
||||
Bugfix:
|
||||
- Altitude is now shown again in EGM2008 (introduced in v4.25.0)
|
||||
- Altitude and speed were shown as 0.0 briefly when data was stored
|
||||
|
||||
Developer:
|
||||
- Refactored sensor subsystem
|
||||
@@ -0,0 +1,6 @@
|
||||
v4.26.1: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Crash when deleting a UI layout
|
||||
- Crash when heartrate sensor data could not be decoded
|
||||
- Crash when exporting zero tracks
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.26.2: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- GPS via Bluetooth is not (yet) supported
|
||||
- Altitude type (EGM2008) wasn't shown
|
||||
@@ -0,0 +1,14 @@
|
||||
v4.27.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Sensor: power from Bosch eBike
|
||||
- Sensors: include already bonded Bluetooth devices (like Bosch eBike)
|
||||
- Barometer: enforce sampling frequency
|
||||
|
||||
Bugfix:
|
||||
- Dashboard API: did not provide update
|
||||
- GPS could not be enabled with Bluetooth disabled
|
||||
|
||||
Developer:
|
||||
- Removed Mockito
|
||||
- UI tests: less flacky
|
||||
@@ -0,0 +1,11 @@
|
||||
v4.27.1: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Support for Bosch Smart System eBikes (cadence, human power)
|
||||
- Add activity "gravel biking"
|
||||
|
||||
Bugfix:
|
||||
- While recording: TrackRecordingService would not restart correctly if killed by Android
|
||||
|
||||
Developer:
|
||||
- TargetSDK 37
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.27.2: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Not all valid timezone offsets could be set
|
||||
- Fixed race condition for IDLE trackpoints
|
||||
@@ -0,0 +1,10 @@
|
||||
v4.28.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Support for exporting to Health Connect
|
||||
|
||||
Bugfix:
|
||||
- Dashboard API: when sharing multiple tracks, the currently recorded one wasn't updated
|
||||
- Configuration setting for announcement of lap power wasn't stored correctly (only some languages affected)
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
v4.28.1: OpenTracks
|
||||
|
||||
Changes:
|
||||
- HealthConnect: sending total distance, speed and elevation gain
|
||||
|
||||
Bugfix:
|
||||
- Gravel activity was not selectable
|
||||
|
||||
Developer:
|
||||
- Cleanup ContentProvider URLs
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.28.2: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Import: ignore GPX waypoints without time
|
||||
|
||||
Bugfix:
|
||||
- Only start GPS once
|
||||
@@ -0,0 +1,10 @@
|
||||
v4.28.3: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Settings: allow to disable segment creation (max recording distance)
|
||||
- Importer: match activity type case-insensitive
|
||||
- GPX: export moving speed in summary
|
||||
|
||||
Bugfix:
|
||||
- GPX: importer must respect namespace prefixes
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
v4.29.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Removed "Export all (one file for all tracks)" functionality
|
||||
For KML/KMZ, the markers are exported in such a way that on import OpenTracks cannot determine to which track a marker belonged.
|
||||
Recommendation for backups: use KMZ with one track per file.
|
||||
@@ -1,15 +1,15 @@
|
||||
v3.0.0: OpenTracks
|
||||
|
||||
Primer lanzamiento de OpenTracks (es decir, una bifurcación de MyTracks)
|
||||
Primera versión de OpenTracks (es decir, una bifurcación dura de MyTracks)
|
||||
|
||||
Cambios:
|
||||
- Cambio de nombre de MyTracks a OpenTracks
|
||||
- Se agregó un sensor de frecuencia cardíaca Bluetooth LE
|
||||
- Se eliminaron todas las funciones que requieren acceso a Internet.
|
||||
- Se eliminó la integración de Google Maps
|
||||
- Se eliminó el uso de los servicios de Google Play.
|
||||
- Se eliminaron los sensores ANT+
|
||||
- Se eliminaron los sensores clásicos de Bluetooth.
|
||||
- Se eliminó Protobuf
|
||||
- Se eliminaron la estimación de calorías y la estimación de actividad.
|
||||
- Se eliminó la API del servicio MyTracks (es decir, no hay integración con otras aplicaciones)
|
||||
- Se ha añadido el sensor de frecuencia cardíaca Bluetooth LE
|
||||
- Se han eliminado todas las funciones que requerían acceso a Internet
|
||||
- Se ha eliminado la integración con Google Maps
|
||||
- Se ha eliminado el uso de los servicios de Google Play
|
||||
- Se han eliminado los sensores ANT+
|
||||
- Se han eliminado los sensores Bluetooth clásicos
|
||||
- Se ha eliminado Protobuf
|
||||
- Se han eliminado la estimación de calorías y la estimación de actividad
|
||||
- Se ha eliminado la API del servicio MyTracks (es decir, no hay integración con otras aplicaciones)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
v3.0.1: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Compartir marcadores
|
||||
- Configuración reestructurada
|
||||
- Se ha eliminado el botón «Atrás» de la barra de acciones
|
||||
- Exportación: se ha eliminado la opción de configuración del tamaño de las fotos
|
||||
- Exportación: ahora se puede seleccionar el formato KML
|
||||
|
||||
Correcciones de errores:
|
||||
- El directorio de exportación es ahora /OpenTracks/
|
||||
- El modo horizontal ya no provoca fallos
|
||||
- Se ha corregido la exportación a KML/KMZ
|
||||
|
||||
Desarrollador:
|
||||
- Las pruebas se pueden ejecutar (aunque muchas fallan)
|
||||
- Se ha habilitado la integración continua con TravisCI (sin pruebas)
|
||||
@@ -0,0 +1,14 @@
|
||||
v3.1.0: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Se ha añadido la opción de mostrar el recorrido en aplicaciones de mapas (probado con OsmAnd y Maps.ME)
|
||||
- Se ha añadido la opción de mostrar las estadísticas del recorrido sin desbloquear el dispositivo mientras se realiza el seguimiento
|
||||
- Se ha eliminado el modo horizontal para la vista detallada del recorrido (presentaba errores)
|
||||
- Se ha eliminado el historial de sugerencias de búsqueda
|
||||
|
||||
Corrección de errores:
|
||||
- Se solicita permiso de almacenamiento para «Importar todo»/«Exportar todo»
|
||||
|
||||
Desarrollador:
|
||||
- Actualización a Gradle 5.6
|
||||
- No se minimizan las compilaciones de lanzamiento
|
||||
@@ -0,0 +1,16 @@
|
||||
v3.2.0: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Se ha eliminado el widget
|
||||
- Se han incorporado iconos de Material Design
|
||||
- Se han eliminado el snowboard y el esquí (se necesitan nuevos iconos)
|
||||
|
||||
Corrección de errores:
|
||||
- Se ha corregido la funcionalidad de búsqueda
|
||||
|
||||
Desarrollador:
|
||||
- Se han corregido (algunos) casos de prueba
|
||||
- Se ha eliminado la funcionalidad «parcelable» en desuso
|
||||
- Limpieza y reformateo a fondo
|
||||
- No se utiliza la difusión para la comunicación entre la aplicación y el widget
|
||||
- El archivo stats_content.xml no incluye otros archivos
|
||||
@@ -0,0 +1,7 @@
|
||||
v3.2.1: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Los ajustes ahora están en una sola página
|
||||
|
||||
Desarrollo:
|
||||
-Los ajustes se migraron a Androidx compat (usa fragments)
|
||||
@@ -0,0 +1,13 @@
|
||||
v3.2.2: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Se eliminaron las estadísticas agregadas
|
||||
- Migrado a Material Design
|
||||
|
||||
Corrección de errores:
|
||||
- Se corrigió la exportación de datos de sensores
|
||||
- Se corrigió la importación de datos de sensores incompletos
|
||||
|
||||
Desarrollador:
|
||||
- Se reemplazó la barra de acción (actionbar) por la barra de herramientas (toolbar)
|
||||
- Migrado a androidx appcompat (incl. Material Design)
|
||||
@@ -0,0 +1,14 @@
|
||||
v3.2.3: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Compartir: usar Android Sharesheet
|
||||
- Compartir: compartir múltiples pistas como archivos individuales
|
||||
- Usar Android ShareSheet
|
||||
- Sensor de frecuencia cardíaca (Bluetooth LE): reconexión automática
|
||||
|
||||
Corrección de errores:
|
||||
- Sensor de frecuencia cardíaca (Bluetooth LE): desconexión correcta
|
||||
- La frecuencia cardíaca no siempre se mostraba durante la grabación
|
||||
|
||||
Desarrollador:
|
||||
- Se habilitaron las pruebas de instrumentación en TravisCI
|
||||
@@ -0,0 +1,5 @@
|
||||
v3.2.4: OpenTracks
|
||||
|
||||
Corrección de errores:
|
||||
- Caída/cierre inesperado (crash) en la actividad de ajustes
|
||||
Se eliminó la posibilidad de cambiar la frecuencia de voz/fracción desde el menú contextual
|
||||
@@ -0,0 +1,15 @@
|
||||
v3.2.5: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Deshabilitar el cambio de actividad
|
||||
- Añadir permiso para Android 10: grabación de datos de ubicación
|
||||
|
||||
Corrección de errores:
|
||||
- Corregir el restablecimiento de los ajustes
|
||||
|
||||
Desarrollador:
|
||||
- Preferencias
|
||||
- si no se pueden cargar los Ajustes, restablecer todas las preferencias
|
||||
- se movió el acceso a las preferencias a PreferencesUtil
|
||||
- se movieron los valores por defecto de las preferencias del código a XML
|
||||
- usar SummaryProvider
|
||||
@@ -0,0 +1,8 @@
|
||||
v3.2.6: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Monitor de frecuencia cardíaca: escanear dispositivos disponibles
|
||||
- Eliminar acceso directo a los ajustes de Bluetooth
|
||||
|
||||
Desarrollador:
|
||||
- Preferencias: se movieron los valores por defecto de las preferencias del código a XML
|
||||
@@ -0,0 +1,10 @@
|
||||
v3.2.7: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- La notificación muestra la precisión del GPS
|
||||
- Mostrar notificación emergente (heads-up) si la precisión del GPS cae por debajo del umbral
|
||||
- Mostrar siempre el gráfico tanto por tiempo como por distancia
|
||||
- Se añadió ayuda (en Ajustes)
|
||||
|
||||
Corrección de errores:
|
||||
- TrackDetailActivity: se deshabilitó el modo horizontal que no funcionaba (temporal)
|
||||
@@ -0,0 +1,10 @@
|
||||
v3.2.8: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Gráfico: zoom mediante gestos
|
||||
|
||||
Corrección de errores:
|
||||
- La exportación a través de KMZ no exportaba correctamente los nombres de archivo de las imágenes
|
||||
- Compartir múltiples pistas no incluía los archivos KMZ
|
||||
- No se mostraba la interfaz de usuario de MarkerDetailActivity
|
||||
- Los ajustes se cerraban inesperadamente si la preferencia de frecuencia cardíaca no estaba inicializada
|
||||
@@ -0,0 +1,10 @@
|
||||
v3.2.9: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Opción de mantener la pantalla encendida durante la grabación
|
||||
- Marcadores: se eliminaron por completo los marcadores de división (split markers)
|
||||
- Los anuncios de voz ahora funcionan sin el permiso READ_PHONE_STATE
|
||||
|
||||
Corrección de errores:
|
||||
- Estadísticas: no se mostraba la frecuencia cardíaca
|
||||
- Estadísticas: el tiempo se restablecía brevemente a 0s
|
||||
@@ -0,0 +1,11 @@
|
||||
v3.2.10: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- ChartView ya no es configurable
|
||||
- Después de reiniciar: se eliminó el reinicio automático de una grabación incompleta
|
||||
|
||||
Corrección de errores:
|
||||
- Los anuncios de voz usan MAY_DUCK (de nuevo)
|
||||
- ChartView muestra marcadores (de nuevo)
|
||||
- Se corrigió el diseño de grabación en horizontal (landscape recordingLayout) de TrackListActivity
|
||||
- Se solicita el comportamiento heredado de la API de archivos (necesario para Android 10)
|
||||
@@ -0,0 +1,12 @@
|
||||
v3.2.11: OpenTracks
|
||||
|
||||
Error:
|
||||
- La velocidad máxima es idéntica a la velocidad promedio
|
||||
|
||||
Corrección de errores:
|
||||
- Cierre inesperado (crash) al seleccionar el sensor de frecuencia cardíaca
|
||||
|
||||
Desarrollador:
|
||||
- Se reemplazó TabHost por TabsLayout
|
||||
- Se eliminó el primer punto de ruta (waypoint) especial
|
||||
- Se eliminaron las traducciones duplicadas
|
||||
@@ -0,0 +1,13 @@
|
||||
v3.3.0: OpenTracks
|
||||
|
||||
Cambios importantes:
|
||||
- Fotos en almacenamiento privado (ocultas para otras apps)
|
||||
NOTA: No se verán fotos de versiones anteriores
|
||||
- Español (Latinoamérica) requiere Android 7.0 o superior
|
||||
|
||||
Cambios:
|
||||
- Importar/exportar vía Storage Access Framework (sin permisos de almacenamiento)
|
||||
|
||||
Desarrollador:
|
||||
- Limpieza de estructuras de datos internas
|
||||
- Icono/nombre distintos para versión debug
|
||||
@@ -0,0 +1,11 @@
|
||||
v3.3.1: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Android 10: permite conservar datos al desinstalar
|
||||
- API Dashboard: icono de mapa para abrir la pista en app externa (ej. OSMDashboard)
|
||||
|
||||
Corrección de errores:
|
||||
- Se corrigió el nombre de la app (roto en v3.3.0)
|
||||
|
||||
Desarrollador:
|
||||
- Documentación de la API Dashboard en README_DEVELOPER.md
|
||||
@@ -0,0 +1,11 @@
|
||||
v3.3.2: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Exportación: ahora sobrescribe los archivos
|
||||
|
||||
Corrección de errores:
|
||||
- Se elimina siempre la notificación de grabación
|
||||
|
||||
Desarrollador:
|
||||
- En API 26+ se usa java.time para analizar y escribir fechas
|
||||
- Refactorización de TrackRecordingService
|
||||
@@ -0,0 +1,12 @@
|
||||
v3.4.0: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- API Dashboard: múltiples pistas
|
||||
- Ayuda actualizada
|
||||
|
||||
Corrección de errores:
|
||||
- Se corrigió el cierre inesperado al recrear fragmentos
|
||||
- En pausa: evita que la app se detenga
|
||||
|
||||
Desarrollador:
|
||||
- Pruebas habilitadas vía CI (Github Actions y TravisCI)
|
||||
@@ -0,0 +1,14 @@
|
||||
v3.5.0: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Marcador: seleccionar imágenes de la galería
|
||||
- Marcador: compartir imagen
|
||||
- Utilizar el nombre de la ruta como nombre de archivo al compartir
|
||||
- Se han eliminado los valores mínimos y máximos de altitud (eran imprecisos)
|
||||
|
||||
Corrección de errores:
|
||||
- Los gráficos utilizan las unidades y la escala preferidas
|
||||
|
||||
Desarrollador:
|
||||
- Base de datos: eliminar columnas en desuso
|
||||
- Base de datos: añadir índices
|
||||
@@ -0,0 +1,10 @@
|
||||
v3.6.0: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Ahora se pueden reanudar las rutas que se habían detenido
|
||||
|
||||
Corrección de errores:
|
||||
- La importación de archivos KMZ no procesaba correctamente la URL de las fotos
|
||||
|
||||
Desarrollador:
|
||||
- Se ha simplificado el procesamiento de datos
|
||||
@@ -0,0 +1,4 @@
|
||||
v3.6.1: OpenTracks
|
||||
|
||||
Corrección de errores:
|
||||
- Importación de KMZ: guardar la ruta correcta de la imagen
|
||||
@@ -0,0 +1,4 @@
|
||||
v3.6.2: OpenTracks
|
||||
|
||||
Corrección de errores:
|
||||
- Los gráficos no se mostraban correctamente (problema introducido en la v3.6.0)
|
||||
@@ -0,0 +1,13 @@
|
||||
v3.7.0: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Informe de velocidad/ritmo por tipo de actividad
|
||||
- Se ha añadido una solución provisional para el cambio de semana del GPS
|
||||
- (Experimental) Compatibilidad con sensores de ciclismo Bluetooth LE (cadencia y velocidad)
|
||||
- Se ha mejorado la importación de KMZ/KML
|
||||
|
||||
Corrección de errores:
|
||||
- Importación de KMZ: solo se importan las imágenes utilizadas
|
||||
|
||||
Desarrollador:
|
||||
- Se ha refactorizado TrackRecordingService
|
||||
@@ -0,0 +1,7 @@
|
||||
v3.7.1: OpenTracks
|
||||
|
||||
Solución provisional:
|
||||
- Ocultar el botón de inicio del GPS
|
||||
|
||||
Corrección de errores:
|
||||
- Se ha corregido un puntero nulo
|
||||
@@ -0,0 +1,6 @@
|
||||
v3.7.2: OpenTracks
|
||||
|
||||
Corrección de errores:
|
||||
- Se ha vuelto a añadir el botón «Iniciar GPS»
|
||||
- El nombre de la ruta ya no se sobrescribe al final de una grabación
|
||||
- Marcadores: al eliminar una foto, también se borra el archivo
|
||||
@@ -0,0 +1,7 @@
|
||||
v3.7.3: OpenTracks
|
||||
|
||||
Corrección de errores:
|
||||
- Se ha corregido el cálculo de la cadencia y la velocidad (mediante el sensor Bluetooth LE)
|
||||
|
||||
Desarrollador:
|
||||
- Se han eliminado las traducciones que no se utilizaban
|
||||
@@ -0,0 +1,12 @@
|
||||
v3.7.4: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Formato de compartir/exportar configurable
|
||||
|
||||
Corrección de errores:
|
||||
- El diámetro de la rueda no se almacenaba correctamente (para el sensor de velocidad)
|
||||
- Ayuda: los enlaces son clicables
|
||||
|
||||
Desarrollador:
|
||||
- Se ha dividido TrackDetailActivity
|
||||
- El sucesor de StatsFragment utiliza ConstraintLayout
|
||||
@@ -0,0 +1,11 @@
|
||||
v3.7.5: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Se ha añadido un resumen de estadísticas agregadas (desde el menú de la pantalla principal)
|
||||
- API del panel de control: se pasan los ajustes «mostrar en la pantalla de bloqueo» y «mantener la pantalla encendida»
|
||||
- Se han actualizado las traducciones
|
||||
- Importación: se ha flexibilizado el formato de fecha ISO 8601
|
||||
- Mostrar ruta en el mapa: se puede seleccionar el formato de archivo y la apertura del panel de control funciona en Android R (API 30+)
|
||||
|
||||
Corrección de errores:
|
||||
- El icono de la aplicación ahora se muestra correctamente
|
||||
@@ -0,0 +1,11 @@
|
||||
v3.7.6: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Modo horizontal
|
||||
- La interfaz de usuario para borrar rutas ahora es solo un indicador giratorio
|
||||
- La velocidad se exporta e importa (GPX/KML/KMZ)
|
||||
- El formato de exportación predeterminado es KMZ, incluyendo todos los datos y las imágenes
|
||||
|
||||
Desarrollador:
|
||||
- Se han sustituido las AsyncTasks por hilos
|
||||
- Sincronización: se genera un UUID por cada ruta (aún no se exporta)
|
||||
@@ -0,0 +1,16 @@
|
||||
v3.8.0: OpenTracks
|
||||
|
||||
Cambio importante:
|
||||
- Importación/exportación de GPX de velocidad, cadencia y frecuencia cardíaca a través de TrackPointExtension/v2
|
||||
|
||||
Cambios:
|
||||
- API del panel de control: permite acceder a los marcadores (incluida una llamada de retorno para mostrarlos en OpenTracks)
|
||||
- Nuevo icono de marcador (chincheta naranja, SVG)
|
||||
- Los archivos que se van a importar se pueden seleccionar a través del gestor de archivos
|
||||
|
||||
Correcciones de errores:
|
||||
- La interfaz de usuario de estadísticas agregadas respeta la frecuencia preferida
|
||||
- Fallaba la edición de un marcador al cambiar la orientación
|
||||
|
||||
Desarrollador:
|
||||
- Se ha refactorizado TrackRecordingService
|
||||
@@ -0,0 +1,8 @@
|
||||
v3.8.1: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Durante la grabación: se ha añadido la opción de pantalla completa
|
||||
- El panel de control se inicia en la misma tarea
|
||||
|
||||
Correcciones de errores:
|
||||
- Los filtros de intención para importar archivos eran demasiado optimistas
|
||||
@@ -0,0 +1,14 @@
|
||||
v3.9.0: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Exportación de rutas con su UUID
|
||||
- Se evita la importación de rutas ya existentes por su UUID (configurable; por defecto, activado)
|
||||
- Ahora hay que mantener pulsado el botón de pausa/parada
|
||||
- El preinicio del GPS muestra el estado de la señal
|
||||
- Se han añadido nuevos iconos de actividad
|
||||
- Sensor de velocidad: ahora se puede configurar el tamaño de la rueda en milímetros
|
||||
|
||||
Correcciones de errores:
|
||||
- Se han corregido algunos fallos poco frecuentes (NPE)
|
||||
- Se ha corregido el formato del ritmo
|
||||
- Se ha cambiado la posición del icono de la aplicación en el lanzador
|
||||
@@ -1,7 +1,7 @@
|
||||
v4.14.1: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Detección de inactividad se puede desactivar
|
||||
- Se puede desactivar la detección de inactividad
|
||||
|
||||
Correcciones de errores:
|
||||
- UI para añadir imágenes a los marcadores estaba oculto
|
||||
Corrección de errores:
|
||||
- La interfaz de usuario para añadir imágenes a los marcadores estaba oculta
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
v4.27.0: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Sensor: potencia de la bicicleta eléctrica Bosch
|
||||
- Sensores: se incluyen los dispositivos Bluetooth ya emparejados (como la bicicleta eléctrica Bosch)
|
||||
- Barómetro: se aplica la frecuencia de muestreo
|
||||
|
||||
Corrección de errores:
|
||||
- API del panel de control: no se proporcionaba la actualización
|
||||
- No se podía activar el GPS con el Bluetooth desactivado
|
||||
|
||||
Desarrollador:
|
||||
- Se ha eliminado Mockito
|
||||
- Pruebas de la interfaz de usuario: menos inestables
|
||||
@@ -1,8 +1,22 @@
|
||||
OpenTracks es un tracker deportivo que respeta tu privacidad.
|
||||
Un compañero de seguimiento deportivo que respeta tu privacidad.
|
||||
|
||||
Funciones:
|
||||
* Tracker deportivo: haz un seguimiento de tus actividades al aire libre
|
||||
* Marca lugares de interés, incluso agrega fotos a los marcadores
|
||||
* Pulsómetro (compatible con dispositivos Bluetooth LE)
|
||||
* Exporta tus rutas como KML o GPX
|
||||
* Completamente offline, no necesitas internet
|
||||
El deporte y las actividades al aire libre aportan felicidad, destreza y confianza.
|
||||
|
||||
Cuida tu salud llevando un registro de tu entrenamiento.
|
||||
Registra tus sesiones de carrera o caminata, y te ofrece un ciclocomputador con una pantalla más grande para el ciclismo.
|
||||
Marca lugares interesantes a lo largo de tu recorrido con fotos.
|
||||
Guarda estadísticas detalladas para su análisis.
|
||||
Comparte solo los datos que quieras que vean los demás.
|
||||
|
||||
* Avisos de voz.
|
||||
* Compatible con sensores Bluetooth LE: frecuencia cardíaca, velocidad y distancia (ciclismo), cadencia (ciclismo) y medidor de potencia (ciclismo).
|
||||
* Desnivel positivo y negativo: mediante sensor barométrico.
|
||||
* Altitud mostrada en EGM2008 (por encima del nivel medio del mar); exportada como WGS84.
|
||||
* Exporta los datos como rutas en formato KMZ (incluidas fotos), KML o GPX.
|
||||
* No requiere acceso a Internet ni permisos adicionales.
|
||||
* Temas oscuro y claro, respetando la configuración del sistema.
|
||||
* Sin anuncios.
|
||||
|
||||
Software libre / Software gratuito / Código abierto,
|
||||
lo que significa que puedes utilizar, estudiar, modificar y compartir el código fuente.
|
||||
Licencia Apache 2.0
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.26.2: OpenTracks
|
||||
|
||||
Arranxos:
|
||||
- (aínda) non hai compatibilidade co GPS vía Bluetooth
|
||||
- non se mostraba a altitude tipo (EGM2008)
|
||||
@@ -0,0 +1,15 @@
|
||||
v3.0.0: OpenTracks
|
||||
|
||||
Rilisan pertama OpenTracks (fork keras dari MyTracks)
|
||||
|
||||
Perubahan:
|
||||
- Perubahan merek MyTracks ke OpenTracks
|
||||
- Menambahkan sensor detak jantung Bluetooth LE
|
||||
- Menghapus semua fitur yang memerlukan koneksi internet
|
||||
- Menghapus integrasi Google Maps
|
||||
- Menghapus penggunaan layanan Google Play
|
||||
- Menghapus sensor ANT+
|
||||
- Menghapus sensor klasik Bluetooth
|
||||
- Menghapus Protobuf
|
||||
- Menghapus estimasi kalori dan aktivitas
|
||||
- Menghapus API MyTracks Service (dengan kata lain, tidak ada integrasi dengan aplikasi lain)
|
||||
@@ -0,0 +1,17 @@
|
||||
v3.0.1: OpenTracks
|
||||
|
||||
Perubahan:
|
||||
- Bagikan penanda
|
||||
- Perubahan struktur pengaturan
|
||||
- Menghapus tombol kembali di bilah aksi
|
||||
- Ekspor: menghapus pilihan pengaturan ukuran foto
|
||||
- Ekspor: KML sebagai format sekarang dapat dipilih
|
||||
|
||||
Perbaikan Bug:
|
||||
- direktori ekspor sekarang di /OpenTracks/
|
||||
- mode lanskap tidak akan mogok
|
||||
- memperbaiki ekspor KML/KMZ
|
||||
|
||||
Pengembang:
|
||||
- Tes dapat dieksekusi (namun banyak yang gagal)
|
||||
- Mengaktifkan integrasi terus menerus dengan TravisCI (tanpa tes)
|
||||
@@ -0,0 +1,14 @@
|
||||
v3.1.0: OpenTracks
|
||||
|
||||
Perubahan:
|
||||
- Menambahkan opsi tampilan jalur salju di peta aplikasi (telah diuji dengan OsmAnd dan Maps.ME)
|
||||
- Menambahkan ketika melacak, akan menampilkan statistik jalur tanpa perlu membuka kunci perangkat
|
||||
- Menghapus mode landkap untuk tampilan detail jalur (tidak stabil)
|
||||
- Menghapus riwayat saran penelusuran
|
||||
|
||||
Perbaikan Bug:
|
||||
- Permintaan izin penyimpanan untuk impor/ekspor semua
|
||||
|
||||
Pengembang:
|
||||
- Pembaruan ke Gradle 5.6
|
||||
- Jangan mengecilkan (minify) rilisan build
|
||||
@@ -1,4 +1,4 @@
|
||||
Teman pelacakan olahraga yang menghormati privasi Anda.
|
||||
Sebuah teman pelacak olahraga yang benar-benar menghormati privasi Anda.
|
||||
|
||||
Olahraga dan aktivitas luar ruangan memberikan kebahagiaan, keterampilan, dan kepercayaan diri.
|
||||
|
||||
@@ -11,12 +11,12 @@ Hanya membagikan data yang Anda ingin orang lain memilikinya.
|
||||
* Notifikasi suara.
|
||||
* Mendukung sensor Bluetooth LE: detak jantung, kecepatan dan jarak (bersepeda), irama (bersepeda), dan pengukur daya (bersepeda).
|
||||
* Kenaikan dan penurunan ketinggian: melalui sensor barometrik.
|
||||
* Ketinggian yang ditampilkan dalam EGM2008 (di atas permukaan laut); diekspor sebagai WGS84.
|
||||
* Ketinggian ditampilkan dalam EGM2008 (di atas permukaan laut); diekspor sebagai WGS84.
|
||||
* Mengekspor data sebagai lintasan, baik sebagai KMZ (termasuk foto), KML, atau GPX.
|
||||
* Tidak ada akses Internet atau izin tambahan.
|
||||
* Tema gelap dan terang, dengan memperhatikan pengaturan sistem.
|
||||
* Tanpa iklan.
|
||||
|
||||
Perangkat lunak bebas / Perangkat Lunak Bebas / Sumber Terbuka
|
||||
Perangkat Lunak Bebas / Sumber Terbuka
|
||||
yang berarti Anda dapat menggunakan, mempelajari, mengubah, dan berbagi kode sumber.
|
||||
Berlisensi Apache 2.0
|
||||
|
||||
@@ -1 +1 @@
|
||||
Sebuah pelacak olahraga yang benar-benar menghormati privasi Anda.
|
||||
Sebuah teman pelacak olahraga yang benar-benar menghormati privasi Anda.
|
||||
|
||||
@@ -4,7 +4,7 @@ Modifiche:
|
||||
- Velocità degli annunci vocali configurabile
|
||||
- API <26: L'icona del lanciatore omette lo sfondo
|
||||
|
||||
Bugfix:
|
||||
Correzioni:
|
||||
- UI di registrazione: mostra i valori corretti
|
||||
- Registrazione UI: mostra (di nuovo) l'altitudine in EGM2008
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
v4.22.1: OpenTracks
|
||||
|
||||
Modiciche:
|
||||
Modifiche:
|
||||
- Primo rilascio da Codeberg
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
v4.22.2: OpenTracks
|
||||
|
||||
Correzione di errori:
|
||||
Correzioni:
|
||||
- L'impostazione degli annunci vocali per l'ora e l'unità di misura è stata collegata
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.26.2: OpenTracks
|
||||
|
||||
Correzioni:
|
||||
- Il GPS via Bluetooth non è (ancora) supportato
|
||||
- Il tipo di altitudine (EGM2008) non viene mostrato
|
||||
@@ -3,7 +3,7 @@
|
||||
Modifiche:
|
||||
-
|
||||
|
||||
Correzioni di errori:
|
||||
Correzioni:
|
||||
-
|
||||
|
||||
Sviluppatore:
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
Un tracker sportivo che rispetta la tua privacy.
|
||||
|
||||
Sport e attività all'aria aperta portano a felicità, allenamento e fiducia
|
||||
|
||||
Valorizza la tua salute tenendo traccia dei tuoi progressi.
|
||||
Registra quando stai correndo o passeggiando e regalati un monitor più grande quando fai ciclismo.
|
||||
Segna con fotografie i posti interessanti incrociati durante il tragitto.
|
||||
Tieni traccia delle statistiche in dettaglio per analizzarle.
|
||||
Condividi solo i dati che vuoi.
|
||||
|
||||
* Annunci vocali.
|
||||
* Supporto a sensori Bluetooth LE: battito cardiaco, velocità e distanza (ciclismo), cadenza (ciclismo), rilevamento potenza (ciclismo).
|
||||
* Altitudine positiva o negativa tramite sensore barometrico.
|
||||
* Altitudine mostrata in EGM2008 (sopra il livello del mare) ed esportata come WGS84.
|
||||
* Esporta tracce come KMZ (con foto incluse), KML o GPX.
|
||||
* Non richiede accesso a internet o altri permessi.
|
||||
* Temi chiaro e scuro, o seguendo la impostazioni di sistema.
|
||||
* Nessuna pubblicità.
|
||||
|
||||
Libre software / Free Software / Open Source
|
||||
cioè puoi usare, studiare, cambiare e condividere il codice sorgente.
|
||||
Licenza: Apache 2.0
|
||||
@@ -1 +1 @@
|
||||
Un tracker sportivo che rispetta completamente la tua privacy.
|
||||
Un tracker sportivo che rispetta la tua privacy.
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
OpenTracks é um aplicativo de rastreamento de esportes que respeita totalmente a sua privacidade.
|
||||
Um companheiro de rastreamento esportivo que respeita sua privacidade.
|
||||
|
||||
Características:
|
||||
* Rastreamento de esportes: monitore seu esporte e atividades ao ar livre
|
||||
* Fotos e marcadores: marque locais interessantes durante o rastreamento
|
||||
* Ganho e perda de altitude: via sensor barométrico
|
||||
* Altitude mostrada no EGM2008 (acima do nível médio do mar); exportado como WGS84
|
||||
* Anúncios de voz
|
||||
* Suporta sensores Bluetooth LE: frequência cardíaca, velocidade e distância (ciclismo), cadência (ciclismo) e medidor de potência (ciclismo)
|
||||
* Exportar dados: exporte trilhas como KMZ (incluindo fotos), KML, ou GPX
|
||||
* Sem acesso à Internet: a Internet não é usada
|
||||
Esportes e atividades ao ar livre garantem felicidade, habilidades e confiança.
|
||||
|
||||
Valoriza sua saúde acompanhando os seus treinos.
|
||||
Grava enquanto você corre ou caminha e te entrega um computador de bicicleta com uma tela maior para o ciclismo.
|
||||
Marca locais interessantes durante o percurso com fotos.
|
||||
Deixa estatísticas registradas em grande detalhe para analises.
|
||||
Compartilha apenas os dados que você quer que outros tenham.
|
||||
|
||||
* Anúncios por voz.
|
||||
* Suporte para sensores Bluetooth LE: frequência cardíaca, velocidade e distância (ciclismo), cadência (ciclismo) e medição de potência (ciclismo).
|
||||
* Ganhos e perdas de altitude: via sensor barométrico.
|
||||
* Altitudes exibidas em EGM2008 (acima do nível médio do mar); exportado como WGS84.
|
||||
* Exporta os dados de rastreio como KMZ (inclusive fotos), KML ou GPX.
|
||||
* Sem acesso à internet ou permissões extra.
|
||||
* Tema claro e escuro, respeitando as configurações do sistema.
|
||||
* Sem propagandas.
|
||||
|
||||
Software Livre / Código Aberto
|
||||
Significa que você pode usar, estudar, mudar e compartilhar o código.
|
||||
Licença Apache 2.0
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
v4.24.0: OpenTracks
|
||||
|
||||
மாற்றங்கள்:
|
||||
- PublicAPI: தரவு இருந்தால் ஸ்டார்ட் டிராக் மட்டுமே புலத்தை அமைக்கும்
|
||||
- மார்க்கர் புகைப்படங்களை மீண்டும் பகிரலாம்
|
||||
- புகைப்படங்கள் இல்லாமல் KMZ அகற்றப்பட்டது
|
||||
- இதயத் துடிப்பு 0.0 தவறானதாகக் கருதப்படுகிறது (சென்சார் ஸ்டாடிஸ்டிக்ஸ் தவிர)
|
||||
- சிறப்பு ஒருங்கிணைப்புகளுக்கான இறக்குமதி தீர்வு நீக்கப்பட்டது (100/200)
|
||||
(சிறப்பு டிராக் பாயிண்ட்களை ஏற்றுமதி/இறக்குமதி செய்ய MyTracks பயன்படுத்தியது)
|
||||
|
||||
டெவலப்பர்:
|
||||
- ட்ராக் மற்றும் மார்க்கர் பதிவுகள்
|
||||
- திரட்டப்பட்ட புள்ளிவிவரங்கள் தரவுத்தளத்தில் கணக்கிடப்படுகின்றன
|
||||
- TrackStatisticsUpdater மற்றும் SegmentStatisticUpdater எனப் பிரிக்கவும்
|
||||
- அறிமுகப்படுத்தப்பட்ட இட்டரேட்டர் வகுப்புகள் (கர்சர் அல்லது பட்டியலைக் கடந்து செல்வதற்குப் பதிலாக)
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.24.1: OpenTracks
|
||||
|
||||
பிழைத்திருத்தம்:
|
||||
- OSM டாஷ்போர்டு v6.4.1 மற்றும் அதற்கு முந்தையவற்றுடன் இணக்கத்தன்மையை மீட்டெடுக்கவும் (v4.24.0 இல் அறிமுகப்படுத்தப்பட்டது)
|
||||
- எண்ணின்படி இயல்புநிலை ட்ராக் பெயரைப் பயன்படுத்தும்போது செயலிழக்கிறது (v4.24.0 இல் அறிமுகப்படுத்தப்பட்டது)
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.24.2: OpenTracks
|
||||
|
||||
பிழைத்திருத்தம்:
|
||||
- அனைத்தையும் தேர்ந்தெடுக்கும்போது செயலிழக்க (v4.24.0 இல் அறிமுகப்படுத்தப்பட்டது)
|
||||
@@ -0,0 +1,12 @@
|
||||
v4.25.0: OpenTracks
|
||||
|
||||
மாற்றங்கள்:
|
||||
- பதிவுப் பொத்தான் மையமாக உள்ளது
|
||||
- BLE தெர்மோமீட்டருக்கான ஆதரவு (சுற்றுச்சூழல் உணர்தல்)
|
||||
|
||||
பிழைத்திருத்தம்:
|
||||
- இறக்குமதியில்: கோப்பு பெயரிலிருந்து வகையைப் பெறுங்கள் (URI அல்ல)
|
||||
|
||||
டெவலப்பர்:
|
||||
- TrackPoint ஒரு பதிவு
|
||||
- Android Gradle செருகுநிரல் 9.0 க்கு புதுப்பிக்கவும்
|
||||
@@ -17,6 +17,6 @@
|
||||
* 深色和浅色主题,跟随系统设置。
|
||||
* 无广告。
|
||||
|
||||
自由软件/开放源代码
|
||||
自由软件/开放源代码
|
||||
意味着您可以使用、研究、修改和分享源代码。
|
||||
采用 Apache 2.0 许可
|
||||
|
||||
@@ -1 +1 @@
|
||||
一個尊重您的隱私的運動蹤跡紀錄器。
|
||||
一款尊重您隱私的運動蹤跡紀錄器。
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>OpenTracks: Privacy Policy</h1>
|
||||
|
||||
<p>
|
||||
OpenTracks does only store data on the local device that is relevant for tracking your sport exercise.
|
||||
Stored data is not transmitted from the app itself to a third party.
|
||||
|
||||
Data is stored by OpenTracks until:
|
||||
1. the user deletes the recorded track or
|
||||
2. the user uninstalls OpenTracks
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
@@ -2,71 +2,117 @@ package de.dennisguse.opentracks;
|
||||
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.longClick;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withParentIndex;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static androidx.test.espresso.matcher.RootMatchers.isDialog;
|
||||
import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.performClick;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.performClickOnAncestor;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.performLongClickOnAncestor;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitUntilDisplayed;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitUntilRecyclerViewItemCount;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.withRecyclerViewItemCount;
|
||||
import static de.dennisguse.opentracks.util.TestEnvironmentUtils.resetTrackRecordingServiceAndDeleteTracks;
|
||||
import static de.dennisguse.opentracks.util.TrackUiTestUtils.renameStoppedTrack;
|
||||
import static de.dennisguse.opentracks.util.TrackUiTestUtils.startRecordingFromTrackList;
|
||||
import static de.dennisguse.opentracks.util.TrackUiTestUtils.stopRecordingToTrackStopped;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.childAtPosition;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class EspressoDeleteTrackTest {
|
||||
|
||||
private static final Duration UI_TIMEOUT = Duration.ofSeconds(10);
|
||||
|
||||
@Rule
|
||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||
|
||||
@Test
|
||||
public void espressoDeleteTrackTest() {
|
||||
// TrackListActivity: start recording
|
||||
onView(withId(R.id.track_list_fab_action)).perform(click());
|
||||
|
||||
// TrackRecordingActivity
|
||||
onView(withId(R.id.track_recording_fab_action))
|
||||
// wait; stay recording
|
||||
.perform(waitFor(5000))
|
||||
// stop;
|
||||
.perform(longClick());
|
||||
|
||||
// TrackStoppedActivity
|
||||
onView(withId(R.id.finish_button)).perform(click());
|
||||
|
||||
// select track
|
||||
onView(allOf(withParent(withId(R.id.track_list)), withParentIndex(0))).perform(longClick());
|
||||
|
||||
// open menu and delete selected track
|
||||
onView(
|
||||
allOf(withContentDescription("More options"),
|
||||
childAtPosition(
|
||||
childAtPosition(
|
||||
withId(androidx.appcompat.R.id.action_mode_bar),
|
||||
1),
|
||||
2),
|
||||
isDisplayed()))
|
||||
.perform(click());
|
||||
|
||||
onView(withText("Delete")).perform(click());
|
||||
|
||||
onView(withText("OK")).perform(click());
|
||||
|
||||
// tracklist is empty now
|
||||
onView(allOf(withText("Start recording your next adventure here"), isDisplayed()));
|
||||
@Before
|
||||
public void setUp() {
|
||||
resetTrackRecordingServiceAndDeleteTracks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteTrackFromTrackStoppedActivity() {
|
||||
recordTrackAndOpenTrackStoppedActivity();
|
||||
|
||||
// TrackStoppedActivity: discard the just-recorded track directly
|
||||
onView(withId(R.id.discard_button))
|
||||
.perform(performClick());
|
||||
onView(withText(android.R.string.ok))
|
||||
.inRoot(isDialog())
|
||||
.perform(performClick());
|
||||
waitUntilDisplayed(withId(R.id.track_list), UI_TIMEOUT);
|
||||
waitUntilRecyclerViewItemCount(withId(R.id.track_list), 0, UI_TIMEOUT);
|
||||
|
||||
// the recycler is empty again after discarding the only recorded track
|
||||
onView(withId(R.id.track_list))
|
||||
.check(matches(withRecyclerViewItemCount(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteTrackFromSelectedTrackInTrackList() {
|
||||
String uniqueTrackName = "DeleteFromTrackList-" + System.currentTimeMillis();
|
||||
|
||||
recordTrackAndOpenTrackStoppedActivity();
|
||||
renameStoppedTrack(uniqueTrackName, UI_TIMEOUT);
|
||||
|
||||
onView(withId(R.id.finish_button))
|
||||
.perform(performClick());
|
||||
waitUntilDisplayed(withId(R.id.track_list), UI_TIMEOUT);
|
||||
waitUntilDisplayed(allOf(withId(R.id.track_list_item_name), withText(uniqueTrackName)), UI_TIMEOUT);
|
||||
waitUntilRecyclerViewItemCount(withId(R.id.track_list), 1, UI_TIMEOUT);
|
||||
|
||||
onView(allOf(withId(R.id.track_list_item_name), withText(uniqueTrackName)))
|
||||
.perform(performLongClickOnAncestor());
|
||||
|
||||
clickDeleteSelectedTrackAction();
|
||||
onView(withText(android.R.string.ok))
|
||||
.inRoot(isDialog())
|
||||
.perform(performClick());
|
||||
waitUntilRecyclerViewItemCount(withId(R.id.track_list), 0, UI_TIMEOUT);
|
||||
|
||||
onView(withId(R.id.track_list))
|
||||
.check(matches(withRecyclerViewItemCount(0)));
|
||||
}
|
||||
|
||||
private void recordTrackAndOpenTrackStoppedActivity() {
|
||||
startRecordingFromTrackList(UI_TIMEOUT);
|
||||
stopRecordingToTrackStopped(UI_TIMEOUT, R.id.discard_button, R.id.finish_button);
|
||||
}
|
||||
|
||||
private void clickDeleteSelectedTrackAction() {
|
||||
try {
|
||||
waitUntilDisplayed(withId(R.id.list_context_menu_delete), UI_TIMEOUT);
|
||||
onView(withId(R.id.list_context_menu_delete))
|
||||
.perform(performClick());
|
||||
} catch (AssertionError ignored) {
|
||||
onView(allOf(
|
||||
withContentDescription(androidx.appcompat.R.string.abc_action_menu_overflow_description),
|
||||
isDescendantOfA(withId(androidx.appcompat.R.id.action_mode_bar))))
|
||||
.perform(performClick());
|
||||
onView(withText(R.string.menu_delete))
|
||||
.inRoot(isPlatformPopup())
|
||||
.perform(performClickOnAncestor());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,35 +2,43 @@ package de.dennisguse.opentracks;
|
||||
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.Espresso.openContextualActionModeOverflowMenu;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
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.scrollTo;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.performClick;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.performClickOnAncestor;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitUntilDisplayed;
|
||||
import static de.dennisguse.opentracks.util.TestEnvironmentUtils.resetTrackRecordingServiceAndDeleteTracks;
|
||||
import static de.dennisguse.opentracks.util.TrackUiTestUtils.startRecordingFromTrackList;
|
||||
import static de.dennisguse.opentracks.util.TrackUiTestUtils.stopRecordingToTrackStopped;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||
|
||||
import androidx.test.espresso.ViewInteraction;
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Locale;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class EspressoEditTrackRecordingTest {
|
||||
|
||||
private static final Duration UI_TIMEOUT = Duration.ofSeconds(10);
|
||||
|
||||
@Rule
|
||||
public final LocaleRule mLocaleRule = new LocaleRule(Locale.ENGLISH);
|
||||
|
||||
@@ -40,46 +48,48 @@ public class EspressoEditTrackRecordingTest {
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
resetTrackRecordingServiceAndDeleteTracks();
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void espressoEditTrackRecordingTest() {
|
||||
{
|
||||
// TrackListActivity: start recording
|
||||
ViewInteraction fabRecordButton = onView(withId(R.id.track_list_fab_action));
|
||||
fabRecordButton.perform(click());
|
||||
}
|
||||
startRecordingFromTrackList(UI_TIMEOUT);
|
||||
{
|
||||
// TrackRecordingActivity
|
||||
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
|
||||
|
||||
// wait; stay recording
|
||||
trackControllerStopButton.perform(waitFor(4000));
|
||||
|
||||
// open menu
|
||||
openContextualActionModeOverflowMenu();
|
||||
|
||||
// Click the item.
|
||||
onView(withText(R.string.menu_edit)).perform(click());
|
||||
onView(allOf(
|
||||
withContentDescription(androidx.appcompat.R.string.abc_action_menu_overflow_description),
|
||||
isDescendantOfA(withId(R.id.bottom_app_bar))))
|
||||
.perform(performClick());
|
||||
onView(withText(R.string.menu_edit))
|
||||
.inRoot(isPlatformPopup())
|
||||
.perform(performClickOnAncestor());
|
||||
waitUntilDisplayed(withId(R.id.track_edit_name), UI_TIMEOUT);
|
||||
|
||||
// change name for "New Name"
|
||||
ViewInteraction textInputEditText = onView(withId(R.id.track_edit_name));
|
||||
textInputEditText.perform(scrollTo(), replaceText("New Name"));
|
||||
onView(withId(R.id.track_edit_name))
|
||||
.perform(scrollTo(), replaceText("New Name"));
|
||||
|
||||
ViewInteraction textInputEditText2 = onView(allOf(withId(R.id.track_edit_name), withText("New Name"), isDisplayed()));
|
||||
textInputEditText2.perform(closeSoftKeyboard());
|
||||
onView(allOf(withId(R.id.track_edit_name), withText("New Name"), isDisplayed()))
|
||||
.perform(closeSoftKeyboard());
|
||||
|
||||
// save edition
|
||||
ViewInteraction appCompatButton = onView(withId(R.id.track_edit_save));
|
||||
appCompatButton.perform(click());
|
||||
onView(withId(R.id.track_edit_save))
|
||||
.perform(performClick());
|
||||
waitUntilDisplayed(withId(R.id.track_recording_fab_action), UI_TIMEOUT);
|
||||
|
||||
// stop;
|
||||
trackControllerStopButton.perform(longClick());
|
||||
stopRecordingToTrackStopped(UI_TIMEOUT, R.id.resume_button, R.id.finish_button);
|
||||
|
||||
// it's on track stopped activity and there are two buttons
|
||||
ViewInteraction resumeButton = onView(withId(R.id.resume_button));
|
||||
resumeButton.check(matches(isDisplayed()));
|
||||
ViewInteraction finishButton = onView(withId(R.id.finish_button));
|
||||
finishButton.check(matches(isDisplayed()));
|
||||
onView(withId(R.id.resume_button))
|
||||
.check(matches(isDisplayed()));
|
||||
onView(withId(R.id.finish_button))
|
||||
.check(matches(isDisplayed()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,100 +1,141 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.longClick;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withParentIndex;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.performLongClickOnAncestor;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.selectTabAtIndex;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitForResumedViewCondition;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitUntilDisplayed;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.withSelectedTab;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.withViewPagerCurrentItem;
|
||||
import static de.dennisguse.opentracks.util.TestEnvironmentUtils.resetTrackRecordingServiceAndDeleteTracks;
|
||||
import static de.dennisguse.opentracks.util.TrackUiTestUtils.finishStoppedTrack;
|
||||
import static de.dennisguse.opentracks.util.TrackUiTestUtils.resumeStoppedTrack;
|
||||
import static de.dennisguse.opentracks.util.TrackUiTestUtils.startRecordingFromTrackList;
|
||||
import static de.dennisguse.opentracks.util.TrackUiTestUtils.stopRecordingToTrackStopped;
|
||||
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.espresso.ViewInteraction;
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.RuleChain;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackBuilder;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class EspressoUITest {
|
||||
|
||||
@Rule
|
||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||
private static final Duration UI_TIMEOUT = Duration.ofSeconds(10);
|
||||
|
||||
private final ActivityScenarioRule<TrackListActivity> activityRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||
private final GrantPermissionRule grantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||
public RuleChain rules = RuleChain.outerRule(grantPermissionRule)
|
||||
.around(activityRule);
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
resetTrackRecordingServiceAndDeleteTracks();
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void record_stop_resume_stop_finish() {
|
||||
startRecordingFromTrackList(UI_TIMEOUT);
|
||||
{
|
||||
// TrackListActivity: start recording
|
||||
ViewInteraction trackControllerRecordButton = onView(withId(R.id.track_list_fab_action));
|
||||
trackControllerRecordButton.perform(click());
|
||||
}
|
||||
{
|
||||
// TrackRecordingActivity: wait to record some time and then stop
|
||||
onView(withId(R.id.track_recording_fab_action))
|
||||
.perform(waitFor(5000))
|
||||
.perform(longClick());
|
||||
stopRecordingToTrackStopped(UI_TIMEOUT, R.id.resume_button);
|
||||
resumeStoppedTrack(UI_TIMEOUT);
|
||||
stopRecordingToTrackStopped(UI_TIMEOUT, R.id.finish_button);
|
||||
finishStoppedTrack(UI_TIMEOUT);
|
||||
|
||||
// TrackStoppedActivity: resume
|
||||
onView(allOf(withId(R.id.resume_button), isClickable()))
|
||||
.perform(click());
|
||||
|
||||
// TrackRecordingActivity: wait and then stop
|
||||
onView(withId(R.id.track_recording_fab_action))
|
||||
.perform(waitFor(5000))
|
||||
.perform(longClick());
|
||||
|
||||
// TrackStoppedActivity
|
||||
onView(withId(R.id.finish_button))
|
||||
.perform(click());
|
||||
onView(withId(R.id.track_list))
|
||||
.check(matches(isDisplayed()));
|
||||
}
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void record_move_through_tabs() {
|
||||
{
|
||||
// TrackListActivity: start recording
|
||||
ViewInteraction trackControllerRecordButton = onView(withId(R.id.track_list_fab_action));
|
||||
trackControllerRecordButton.perform(click());
|
||||
}
|
||||
startRecordingFromTrackList(UI_TIMEOUT);
|
||||
waitUntilDisplayed(withId(R.id.track_detail_activity_tablayout), UI_TIMEOUT);
|
||||
waitUntilDisplayed(withId(R.id.track_detail_activity_view_pager), UI_TIMEOUT);
|
||||
{
|
||||
// TrackRecordingActivity
|
||||
ViewInteraction tabLayout = onView(withId(R.id.track_detail_activity_tablayout));
|
||||
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
|
||||
|
||||
tabLayout.perform(selectTabAtIndex(1));
|
||||
tabLayout.perform(waitFor(1000));
|
||||
waitUntilTabSelectionSettlesAt(1);
|
||||
|
||||
tabLayout.perform(selectTabAtIndex(2));
|
||||
tabLayout.perform(waitFor(1000));
|
||||
waitUntilTabSelectionSettlesAt(2);
|
||||
|
||||
tabLayout.perform(selectTabAtIndex(3));
|
||||
tabLayout.perform(waitFor(1000));
|
||||
waitUntilTabSelectionSettlesAt(3);
|
||||
|
||||
tabLayout.perform(selectTabAtIndex(0));
|
||||
tabLayout.perform(waitFor(1000));
|
||||
waitUntilTabSelectionSettlesAt(0);
|
||||
|
||||
// stop
|
||||
trackControllerStopButton.perform(longClick());
|
||||
stopRecordingToTrackStopped(UI_TIMEOUT, R.id.resume_button, R.id.finish_button);
|
||||
|
||||
onView(withId(R.id.resume_button))
|
||||
.check(matches(isDisplayed()));
|
||||
onView(withId(R.id.finish_button))
|
||||
.check(matches(isDisplayed()));
|
||||
}
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void selectAndDeleteTrack() {
|
||||
Pair<Track, List<TrackPoint>> pair = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 5);
|
||||
String uniqueTrackName = "SelectAndDeleteTrack-" + System.currentTimeMillis();
|
||||
TrackBuilder trackBuilder = new TrackBuilder(pair.first);
|
||||
trackBuilder.setName(uniqueTrackName);
|
||||
TestDataUtil.insertTrackWithLocations(new ContentProviderUtils(ApplicationProvider.getApplicationContext()), trackBuilder.getTrack(), pair.second);
|
||||
activityRule.getScenario().recreate();
|
||||
waitUntilDisplayed(allOf(withId(R.id.track_list_item_name), withText(uniqueTrackName)), UI_TIMEOUT);
|
||||
|
||||
onView(withId(R.id.track_list)).check(matches(isDisplayed()));
|
||||
onView(allOf(withParent(withId(R.id.track_list)), withParentIndex(0))).perform(longClick());
|
||||
onView(allOf(withId(R.id.track_list_item_name), withText(uniqueTrackName)))
|
||||
.perform(performLongClickOnAncestor());
|
||||
|
||||
onView(withId(androidx.appcompat.R.id.action_mode_bar))
|
||||
.check(matches(isDisplayed()));
|
||||
}
|
||||
|
||||
private void waitUntilTabSelectionSettlesAt(int index) {
|
||||
waitForResumedViewCondition(
|
||||
withId(R.id.track_detail_activity_tablayout),
|
||||
view -> withSelectedTab(index).matches(view),
|
||||
"selected tab to be " + index,
|
||||
UI_TIMEOUT);
|
||||
waitForResumedViewCondition(
|
||||
withId(R.id.track_detail_activity_view_pager),
|
||||
view -> withViewPagerCurrentItem(index).matches(view),
|
||||
"ViewPager current item to be " + index,
|
||||
UI_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,13 @@ import de.dennisguse.opentracks.data.MarkerIterator;
|
||||
import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Marker;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
@@ -92,9 +96,9 @@ public class TestDataUtil {
|
||||
);
|
||||
|
||||
List<Marker> markers = List.of(
|
||||
new Marker(null, trackId, "Marker 1", "Marker description 1", "Marker typeLocalized 3", trackPoints.get(1).getPosition(), null),
|
||||
new Marker(null, trackId, "Marker 2", "Marker description 2", "Marker typeLocalized 3", trackPoints.get(4).getPosition(), null),
|
||||
new Marker(null, trackId, "Marker 3", "Marker description 3", "Marker typeLocalized 3", trackPoints.get(5).getPosition(), null)
|
||||
new Marker(null, trackId, "Marker 1", "Marker description 1", "Marker typeLocalized 3", trackPoints.get(1).position(), null),
|
||||
new Marker(null, trackId, "Marker 2", "Marker description 2", "Marker typeLocalized 3", trackPoints.get(4).position(), null),
|
||||
new Marker(null, trackId, "Marker 3", "Marker description 3", "Marker typeLocalized 3", trackPoints.get(5).position(), null)
|
||||
);
|
||||
|
||||
return new TrackData(track, trackPoints, markers);
|
||||
@@ -112,8 +116,10 @@ public class TestDataUtil {
|
||||
return pair.first;
|
||||
}
|
||||
|
||||
public static TrackPoint createTrackPoint(int i) {
|
||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
public static TrackPoint createTrackPoint(int i, TrackPoint.Type type, AltitudeGainLoss altitudeGainLoss) {
|
||||
return new TrackPoint(
|
||||
null,
|
||||
type,
|
||||
new Position(
|
||||
Instant.ofEpochSecond(i),
|
||||
INITIAL_LATITUDE + (double) i / 10000.0,
|
||||
@@ -123,21 +129,25 @@ public class TestDataUtil {
|
||||
null,
|
||||
null,
|
||||
Speed.of(5f + (i / 10f))
|
||||
));
|
||||
|
||||
trackPoint.setHeartRate(100f + i % 80);
|
||||
trackPoint.setCadence(300f + i);
|
||||
trackPoint.setPower(400f + i);
|
||||
trackPoint.setAltitudeGainLoss(ALTITUDE_GAIN, ALTITUDE_LOSS);
|
||||
return trackPoint;
|
||||
),
|
||||
null,
|
||||
HeartRate.of(100f + i % 80),
|
||||
null,
|
||||
Cadence.of(300f + i),
|
||||
Power.of(400f + i),
|
||||
altitudeGainLoss
|
||||
);
|
||||
}
|
||||
|
||||
public static TrackPoint createTrackPoint(int i, TrackPoint.Type type) {
|
||||
TrackPoint trackPoint = createTrackPoint(i);
|
||||
trackPoint.setType(type);
|
||||
return trackPoint;
|
||||
return createTrackPoint(i, TrackPoint.Type.TRACKPOINT, new AltitudeGainLoss(ALTITUDE_GAIN, ALTITUDE_LOSS));
|
||||
}
|
||||
|
||||
public static TrackPoint createTrackPoint(int i) {
|
||||
return createTrackPoint(i, TrackPoint.Type.TRACKPOINT);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inserts a track with locations into the database.
|
||||
*
|
||||
@@ -164,7 +174,7 @@ public class TestDataUtil {
|
||||
"Marker name",
|
||||
description,
|
||||
"Marker typeLocalized",
|
||||
trackPoint.getPosition(),
|
||||
trackPoint.position(),
|
||||
photoUri);
|
||||
}
|
||||
|
||||
|
||||
+15
-12
@@ -6,8 +6,12 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
|
||||
@@ -20,7 +24,9 @@ public class TestSensorDataUtil {
|
||||
sensorDataList.add(new TestSensorDataUtil.SensorData(time, hr, cadence, power, type));
|
||||
int i = trackPointList.size() + 1;
|
||||
|
||||
TrackPoint tp = new TrackPoint(type,
|
||||
TrackPoint tp = new TrackPoint(
|
||||
null,
|
||||
type,
|
||||
new Position(
|
||||
time,
|
||||
TestDataUtil.INITIAL_LATITUDE + (double) i / 10000.0,
|
||||
@@ -30,18 +36,15 @@ public class TestSensorDataUtil {
|
||||
null,
|
||||
null,
|
||||
Speed.of(5f + (i / 10f))
|
||||
)
|
||||
),
|
||||
null,
|
||||
HeartRate.ofOrNull(hr),
|
||||
null,
|
||||
cadence != null ? Cadence.of(cadence) : null,
|
||||
power != null ? Power.of(power) : null,
|
||||
new AltitudeGainLoss(3, 3)
|
||||
);
|
||||
if (hr != null) {
|
||||
tp.setHeartRate(hr);
|
||||
}
|
||||
if (cadence != null) {
|
||||
tp.setCadence(cadence);
|
||||
}
|
||||
if (power != null) {
|
||||
tp.setPower(power);
|
||||
}
|
||||
tp.setAltitudeGainLoss(3, 3);
|
||||
|
||||
trackPointList.add(tp);
|
||||
}
|
||||
|
||||
|
||||
+110
-77
@@ -20,22 +20,19 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -54,19 +51,22 @@ import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeExtremities;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Marker;
|
||||
import de.dennisguse.opentracks.data.models.MarkerBuilder;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackBuilder;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.data.statistics.SensorStatistics;
|
||||
import de.dennisguse.opentracks.data.tables.MarkerColumns;
|
||||
import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
|
||||
import de.dennisguse.opentracks.data.tables.TracksColumns;
|
||||
import de.dennisguse.opentracks.data.statistics.SensorStatistics;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
|
||||
/**
|
||||
@@ -75,7 +75,6 @@ import de.dennisguse.opentracks.util.FileUtils;
|
||||
* @author Bartlomiej Niechwiej
|
||||
* @author Youtao Liu
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CustomContentProviderUtilsTest {
|
||||
|
||||
private static final String NAME_PREFIX = "test name";
|
||||
@@ -87,12 +86,6 @@ public class CustomContentProviderUtilsTest {
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
@Mock
|
||||
private Cursor cursorMock;
|
||||
|
||||
@Mock
|
||||
private ContentResolver contentResolverMock;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
@@ -101,21 +94,21 @@ public class CustomContentProviderUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_noPoints() {
|
||||
testIterator(new Track.Id(1), 0);
|
||||
assertEquals(0, testIterator(new Track.Id(1), 0).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_noAscending() {
|
||||
testIterator(new Track.Id(1), 50);
|
||||
testIterator(new Track.Id(2), 50);
|
||||
assertEquals(50, testIterator(new Track.Id(1), 50).size());
|
||||
assertEquals(50, testIterator(new Track.Id(2), 50).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_largeTrack() {
|
||||
testIterator(new Track.Id(1), 20000 / 2);
|
||||
assertEquals(20000 / 2, testIterator(new Track.Id(1), 20000 / 2).size());
|
||||
}
|
||||
|
||||
private void testIterator(Track.Id trackId, int numPoints) {
|
||||
private List<TrackPoint> testIterator(Track.Id trackId, int numPoints) {
|
||||
TrackPoint.Id lastPointId = initializeTrack(trackId, numPoints);
|
||||
List<TrackPoint> locations = new ArrayList<>(numPoints);
|
||||
try (TrackPointIterator it = contentProviderUtils.getTrackPointIterator(trackId, null)) {
|
||||
@@ -124,10 +117,11 @@ public class CustomContentProviderUtilsTest {
|
||||
assertNotNull(trackPoint);
|
||||
locations.add(trackPoint);
|
||||
// Make sure the IDs are returned in the right order.
|
||||
assertEquals(lastPointId.id() - numPoints + locations.size(), trackPoint.getId().id());
|
||||
assertEquals(lastPointId.id() - numPoints + locations.size(), trackPoint.id().id());
|
||||
}
|
||||
assertEquals(numPoints, locations.size());
|
||||
}
|
||||
return locations;
|
||||
}
|
||||
|
||||
private TrackPoint.Id initializeTrack(Track.Id id, int numPoints) {
|
||||
@@ -167,7 +161,7 @@ public class CustomContentProviderUtilsTest {
|
||||
try (TrackPointIterator it = contentProviderUtils.getTrackPointIterator(id, null)) {
|
||||
while (it.hasNext()) {
|
||||
TrackPoint trackPoint = it.next();
|
||||
lastPointId = trackPoint.getId();
|
||||
lastPointId = trackPoint.id();
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
@@ -184,40 +178,62 @@ public class CustomContentProviderUtilsTest {
|
||||
@Test
|
||||
public void testCreateTrack() {
|
||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||
|
||||
int columnIndex = 1;
|
||||
// Id
|
||||
when(cursorMock.getColumnIndexOrThrow(TracksColumns._ID)).thenReturn(columnIndex);
|
||||
when(cursorMock.isNull(columnIndex)).thenReturn(false);
|
||||
when(cursorMock.getLong(columnIndex)).thenReturn(trackId.id());
|
||||
|
||||
//Uuid
|
||||
columnIndex++;
|
||||
when(cursorMock.getColumnIndexOrThrow(TracksColumns.UUID)).thenReturn(columnIndex);
|
||||
when(cursorMock.isNull(columnIndex)).thenReturn(false);
|
||||
when(cursorMock.getBlob(columnIndex)).thenReturn(UUIDUtils.toBytes(UUID.randomUUID()));
|
||||
|
||||
// Name
|
||||
columnIndex++;
|
||||
String name = NAME_PREFIX + trackId.id();
|
||||
when(cursorMock.getColumnIndexOrThrow(TracksColumns.NAME)).thenReturn(columnIndex);
|
||||
when(cursorMock.isNull(columnIndex)).thenReturn(false);
|
||||
when(cursorMock.getString(columnIndex)).thenReturn(name);
|
||||
|
||||
Track track = ContentProviderUtils.createTrack(cursorMock);
|
||||
MatrixCursor cursor = new MatrixCursor(new String[]{
|
||||
TracksColumns._ID,
|
||||
TracksColumns.UUID,
|
||||
TracksColumns.NAME,
|
||||
TracksColumns.DESCRIPTION,
|
||||
TracksColumns.ACTIVITY_TYPE,
|
||||
TracksColumns.ACTIVITY_TYPE_LOCALIZED,
|
||||
TracksColumns.STARTTIME,
|
||||
TracksColumns.STARTTIME_OFFSET,
|
||||
TracksColumns.STOPTIME,
|
||||
TracksColumns.TOTALDISTANCE,
|
||||
TracksColumns.TOTALTIME,
|
||||
TracksColumns.MOVINGTIME,
|
||||
TracksColumns.MAXSPEED,
|
||||
TracksColumns.MIN_ALTITUDE,
|
||||
TracksColumns.MAX_ALTITUDE,
|
||||
TracksColumns.ALTITUDE_GAIN,
|
||||
TracksColumns.ALTITUDE_LOSS,
|
||||
});
|
||||
cursor.addRow(new Object[]{
|
||||
trackId.id(),
|
||||
UUIDUtils.toBytes(UUID.randomUUID()),
|
||||
name,
|
||||
"",
|
||||
ActivityType.UNKNOWN.getId(),
|
||||
"",
|
||||
1_000L,
|
||||
ZoneOffset.UTC.getTotalSeconds(),
|
||||
2_000L,
|
||||
123.45f,
|
||||
1_000L,
|
||||
500L,
|
||||
12.34f,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
});
|
||||
assertTrue(cursor.moveToFirst());
|
||||
|
||||
Track track = ContentProviderUtils.createTrack(cursor);
|
||||
assertEquals(trackId, track.id());
|
||||
assertEquals(name, track.name());
|
||||
}
|
||||
|
||||
private void assertCount(int trackCount, int trackPointCount, int markerCount) {
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
try (Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID)) {
|
||||
try (Cursor tracksCursor = contentResolver.query(CustomContentProvider.UrlType.TRACKS.getUri(), null, null, null, TracksColumns._ID)) {
|
||||
assertEquals(trackCount, tracksCursor.getCount());
|
||||
}
|
||||
try (Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID)) {
|
||||
try (Cursor tracksPointsCursor = contentResolver.query(CustomContentProvider.UrlType.TRACKPOINTS_BY_ID.getUri(), null, null, null, TrackPointsColumns._ID)) {
|
||||
assertEquals(trackPointCount, tracksPointsCursor.getCount());
|
||||
}
|
||||
try (Cursor markerCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID)) {
|
||||
try (Cursor markerCursor = contentResolver.query(CustomContentProvider.UrlType.MARKERS.getUri(), null, null, null, MarkerColumns._ID)) {
|
||||
assertEquals(markerCount, markerCursor.getCount());
|
||||
}
|
||||
}
|
||||
@@ -237,7 +253,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
getLastValidTrackPoint(trackId).getPosition(),
|
||||
getLastValidTrackPoint(trackId).position(),
|
||||
null
|
||||
));
|
||||
|
||||
@@ -300,7 +316,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
getLastValidTrackPoint(trackId1).getPosition(),
|
||||
getLastValidTrackPoint(trackId1).position(),
|
||||
null
|
||||
));
|
||||
|
||||
@@ -446,8 +462,6 @@ public class CustomContentProviderUtilsTest {
|
||||
marker.setDescription(TEST_DESC);
|
||||
contentProviderUtils.insertMarker(marker.getMarker());
|
||||
|
||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(contentResolverMock);
|
||||
|
||||
Marker.Id markerId = new Marker.Id(System.currentTimeMillis());
|
||||
marker.setId(markerId);
|
||||
ContentValues contentValues = contentProviderUtils.createContentValues(marker.getMarker(), trackId);
|
||||
@@ -473,7 +487,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
TEST_DESC,
|
||||
"",
|
||||
getLastValidTrackPoint(trackId).getPosition(),
|
||||
getLastValidTrackPoint(trackId).position(),
|
||||
null
|
||||
));
|
||||
|
||||
@@ -553,7 +567,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
MOCK_DESC,
|
||||
"",
|
||||
getLastValidTrackPoint(trackId).getPosition(),
|
||||
getLastValidTrackPoint(trackId).position(),
|
||||
null
|
||||
));
|
||||
Marker.Id marker2Id = contentProviderUtils.insertMarker(new Marker(
|
||||
@@ -562,7 +576,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
MOCK_DESC,
|
||||
"",
|
||||
getLastValidTrackPoint(trackId).getPosition(),
|
||||
getLastValidTrackPoint(trackId).position(),
|
||||
null
|
||||
));
|
||||
|
||||
@@ -589,7 +603,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
MOCK_DESC,
|
||||
"",
|
||||
getLastValidTrackPoint(trackId).getPosition(),
|
||||
getLastValidTrackPoint(trackId).position(),
|
||||
null
|
||||
));
|
||||
}
|
||||
@@ -613,7 +627,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
TEST_DESC,
|
||||
"",
|
||||
getLastValidTrackPoint(trackId).getPosition(),
|
||||
getLastValidTrackPoint(trackId).position(),
|
||||
null
|
||||
));
|
||||
|
||||
@@ -636,7 +650,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
TEST_DESC,
|
||||
"",
|
||||
getLastValidTrackPoint(trackId).getPosition(),
|
||||
getLastValidTrackPoint(trackId).position(),
|
||||
null
|
||||
));
|
||||
|
||||
@@ -647,7 +661,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
TEST_DESC_NEW,
|
||||
"",
|
||||
getLastValidTrackPoint(trackId).getPosition(),
|
||||
getLastValidTrackPoint(trackId).position(),
|
||||
null
|
||||
));
|
||||
|
||||
@@ -803,20 +817,27 @@ public class CustomContentProviderUtilsTest {
|
||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||
|
||||
TrackPoint trackPoint = TestDataUtil.createTrackPoint(5);
|
||||
trackPoint.setHeartRate(1F);
|
||||
trackPoint.setCadence(2F);
|
||||
trackPoint.setPower(3F);
|
||||
TrackPoint trackPoint = new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
TestDataUtil.createTrackPoint(5).position(),
|
||||
null,
|
||||
HeartRate.of(1),
|
||||
null,
|
||||
Cadence.of(2),
|
||||
Power.of(3),
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
contentProviderUtils.insertTrackPoint(trackPoint, trackId);
|
||||
|
||||
// then
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
assertTrue(trackPoints.get(10).hasHeartRate());
|
||||
assertEquals(trackPoint.getHeartRate(), trackPoints.get(10).getHeartRate());
|
||||
assertEquals(trackPoint.getCadence(), trackPoints.get(10).getCadence());
|
||||
assertEquals(trackPoint.getPower(), trackPoints.get(10).getPower());
|
||||
assertNotNull(trackPoints.get(10).heartRate());
|
||||
assertEquals(trackPoint.heartRate(), trackPoints.get(10).heartRate());
|
||||
assertEquals(trackPoint.cadence(), trackPoints.get(10).cadence());
|
||||
assertEquals(trackPoint.power(), trackPoints.get(10).power());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -857,9 +878,9 @@ public class CustomContentProviderUtilsTest {
|
||||
for (int i = 0; i < trackpointIds.size(); i++) {
|
||||
assertTrue(trackPointIterator.hasNext());
|
||||
TrackPoint trackPoint = trackPointIterator.next();
|
||||
assertEquals(startTrackPointId.id() + i, trackPoint.getId().id());
|
||||
assertEquals(startTrackPointId.id() + i, trackPoint.id().id());
|
||||
|
||||
Position position = trackPoint.getPosition();
|
||||
Position position = trackPoint.position();
|
||||
assertEquals(TestDataUtil.INITIAL_LATITUDE + i / 10000.0, position.latitude(), 0.01);
|
||||
assertEquals(TestDataUtil.INITIAL_LONGITUDE - i / 10000.0, position.longitude(), 0.01);
|
||||
assertEquals(i / 100.0, position.horizontalAccuracy().distance_m(), 0.01);
|
||||
@@ -879,11 +900,18 @@ public class CustomContentProviderUtilsTest {
|
||||
public void testGetSensorStats_noSensorData() {
|
||||
// given
|
||||
List<TrackPoint> trackPointList = new ArrayList<>();
|
||||
TrackPoint trackPoint = TestDataUtil.createTrackPoint(1);
|
||||
trackPoint.setType(TrackPoint.Type.TRACKPOINT);
|
||||
trackPoint.setPower(null);
|
||||
trackPoint.setCadence(null);
|
||||
trackPoint.setHeartRate(null);
|
||||
TrackPoint trackPoint = new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
TestDataUtil.createTrackPoint(1).position(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
trackPointList.add(trackPoint);
|
||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||
Track track = TestDataUtil.createTrack(trackId);
|
||||
@@ -1167,7 +1195,7 @@ public class CustomContentProviderUtilsTest {
|
||||
assertEquals(sensorStatistics.avgPower().getW(), stats.avgPower, 0f);
|
||||
}
|
||||
|
||||
private void testGetSensorStats_randomData(int totalPoints, boolean withStartSegments) {
|
||||
private Pair<SensorStatistics, TestSensorDataUtil.SensorDataStats> testGetSensorStats_randomData(int totalPoints, boolean withStartSegments) {
|
||||
// given
|
||||
Instant start = Instant.now();
|
||||
TestSensorDataUtil sensorDataUtil = new TestSensorDataUtil();
|
||||
@@ -1190,22 +1218,27 @@ public class CustomContentProviderUtilsTest {
|
||||
SensorStatistics sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
||||
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
||||
|
||||
// then
|
||||
assertEquals(sensorStatistics.avgHeartRate().getBPM(), stats.avgHr, 0.01f);
|
||||
assertEquals(sensorStatistics.maxHeartRate().getBPM(), stats.maxHr, 0.01f);
|
||||
assertEquals(sensorStatistics.avgCadence().getRPM(), stats.avgCadence, 0.01f);
|
||||
assertEquals(sensorStatistics.maxCadence().getRPM(), stats.maxCadence, 0.01f);
|
||||
assertEquals(sensorStatistics.avgPower().getW(), stats.avgPower, 0.01f);
|
||||
return Pair.create(sensorStatistics, stats);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSensorStats_veryLongActivity12h() {
|
||||
testGetSensorStats_randomData(43200 / 6, false);
|
||||
Pair<SensorStatistics, TestSensorDataUtil.SensorDataStats> result = testGetSensorStats_randomData(43200 / 6, false);
|
||||
assertEquals(result.first.avgHeartRate().getBPM(), result.second.avgHr, 0.01f);
|
||||
assertEquals(result.first.maxHeartRate().getBPM(), result.second.maxHr, 0.01f);
|
||||
assertEquals(result.first.avgCadence().getRPM(), result.second.avgCadence, 0.01f);
|
||||
assertEquals(result.first.maxCadence().getRPM(), result.second.maxCadence, 0.01f);
|
||||
assertEquals(result.first.avgPower().getW(), result.second.avgPower, 0.01f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSensorStats_withSeveralRandomStartSegments() {
|
||||
testGetSensorStats_randomData(5000, true);
|
||||
Pair<SensorStatistics, TestSensorDataUtil.SensorDataStats> result = testGetSensorStats_randomData(5000, true);
|
||||
assertEquals(result.first.avgHeartRate().getBPM(), result.second.avgHr, 0.01f);
|
||||
assertEquals(result.first.maxHeartRate().getBPM(), result.second.maxHr, 0.01f);
|
||||
assertEquals(result.first.avgCadence().getRPM(), result.second.avgCadence, 0.01f);
|
||||
assertEquals(result.first.maxCadence().getRPM(), result.second.maxCadence, 0.01f);
|
||||
assertEquals(result.first.avgPower().getW(), result.second.avgPower, 0.01f);
|
||||
}
|
||||
|
||||
private TrackPoint getLastValidTrackPoint(Track.Id trackId) {
|
||||
@@ -1217,7 +1250,7 @@ public class CustomContentProviderUtilsTest {
|
||||
|
||||
return trackpoints.reversed()
|
||||
.stream()
|
||||
.filter(it -> List.of(TrackPoint.Type.TRACKPOINT, TrackPoint.Type.SEGMENT_START_AUTOMATIC).contains(it.getType()))
|
||||
.filter(it -> List.of(TrackPoint.Type.TRACKPOINT, TrackPoint.Type.SEGMENT_START_AUTOMATIC).contains(it.type()))
|
||||
.findFirst().orElseThrow();
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
public class TrackPointTest {
|
||||
|
||||
@Test
|
||||
public void distanceToPrevious() {
|
||||
public void distanceToPrevious_gps() {
|
||||
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.ofEpochMilli(0),
|
||||
@@ -38,20 +38,123 @@ public class TrackPointTest {
|
||||
|
||||
// without sensor distance
|
||||
assertEquals(11.13, tp2.distanceToPrevious(tp1).toM(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void distanceToPrevious_sensor_distance_first() {
|
||||
TrackPoint tp1 = new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.ofEpochMilli(0),
|
||||
0d,
|
||||
0.0001,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null),
|
||||
Distance.of(5),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
TrackPoint tp2 = new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.ofEpochMilli(1),
|
||||
0d,
|
||||
0.0002,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null));
|
||||
|
||||
// tp1 has sensor distance
|
||||
tp1.setSensorDistance(Distance.of(5));
|
||||
tp1.setSensorDistance(null);
|
||||
assertEquals(11.13, tp2.distanceToPrevious(tp1).toM(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void distanceToPrevious_sensor_distance_second() {
|
||||
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.ofEpochMilli(0),
|
||||
0d,
|
||||
0.0001,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null));
|
||||
|
||||
TrackPoint tp2 = new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.ofEpochMilli(1),
|
||||
0d,
|
||||
0.0002,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null),
|
||||
Distance.of(5),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
|
||||
// tp2 has sensor distance
|
||||
tp1.setSensorDistance(null);
|
||||
tp2.setSensorDistance(Distance.of(5));
|
||||
assertEquals(5, tp2.distanceToPrevious(tp1).toM(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void distanceToPrevious_sensor_distance_both() {
|
||||
TrackPoint tp1 = new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.ofEpochMilli(0),
|
||||
0d,
|
||||
0.0001,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null),
|
||||
Distance.of(10),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
TrackPoint tp2 = new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.ofEpochMilli(1),
|
||||
0d,
|
||||
0.0002,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null),
|
||||
Distance.of(5),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
|
||||
// tp1 and tp2 have sensor distance
|
||||
tp1.setSensorDistance(Distance.of(10));
|
||||
tp2.setSensorDistance(Distance.of(5));
|
||||
assertEquals(5, tp2.distanceToPrevious(tp1).toM(), 0.01);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ActivityTypeTest {
|
||||
|
||||
@Test
|
||||
public void findBy_id() {
|
||||
assertEquals(ActivityType.TRAIL_RUNNING, ActivityType.findBy("trail running"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findBy_idOfEveryActivityType() {
|
||||
for (ActivityType activityType : ActivityType.values()) {
|
||||
assertEquals(activityType, ActivityType.findBy(activityType.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findBy_synonymsOfEveryActivityType() {
|
||||
for (ActivityType activityType : ActivityType.values()) {
|
||||
for (String synonym : activityType.synonyms) {
|
||||
assertEquals(activityType, ActivityType.findBy(synonym));
|
||||
assertEquals(activityType, ActivityType.findBy(synonym.toUpperCase(Locale.ROOT).replace(' ', '_')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findBy_ignoresCase() {
|
||||
assertEquals(ActivityType.RUNNING, ActivityType.findBy("RUNNING"));
|
||||
assertEquals(ActivityType.ATV, ActivityType.findBy("atv"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findBy_ignoresSeparators() {
|
||||
assertEquals(ActivityType.TRAIL_RUNNING, ActivityType.findBy("TRAIL_RUNNING"));
|
||||
assertEquals(ActivityType.MOUNTAIN_BIKING, ActivityType.findBy("mountain-biking"));
|
||||
assertEquals(ActivityType.CROSS_COUNTRY_SKIING, ActivityType.findBy("cross_country_skiing"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findBy_ignoresSurroundingAndRepeatedWhitespace() {
|
||||
assertEquals(ActivityType.MOUNTAIN_BIKING, ActivityType.findBy(" Mountain Biking "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findBy_synonym() {
|
||||
assertEquals(ActivityType.RUNNING, ActivityType.findBy("treadmill"));
|
||||
assertEquals(ActivityType.TRAIL_RUNNING, ActivityType.findBy("TRAIL_RUN"));
|
||||
assertEquals(ActivityType.MOUNTAIN_BIKING, ActivityType.findBy("MOUNTAIN_BIKE"));
|
||||
assertEquals(ActivityType.SWIMMING_OPEN, ActivityType.findBy("SWIMMING_OPENWATER"));
|
||||
assertEquals(ActivityType.BIKING, ActivityType.findBy("bike"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findBy_unknown() {
|
||||
assertEquals(ActivityType.UNKNOWN, ActivityType.findBy("hovercraft"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findBy_null() {
|
||||
assertEquals(ActivityType.UNKNOWN, ActivityType.findBy(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findBy_blank() {
|
||||
assertEquals(ActivityType.UNKNOWN, ActivityType.findBy(" "));
|
||||
}
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
package de.dennisguse.opentracks.io.file.exporter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.TestUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Marker;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.io.file.importer.ExportImportTest;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public class KMLTrackExporterTest {
|
||||
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||
|
||||
private static final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@BeforeClass
|
||||
public static void preSetUp() {
|
||||
// Prepare looper for Android's message queue
|
||||
if (Looper.myLooper() == null) Looper.prepare();
|
||||
|
||||
PreferencesUtils.resetPreferences(context, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoTrack_with_oneMarker() throws TimeoutException, IOException {
|
||||
// given
|
||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)))
|
||||
.getService();
|
||||
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
|
||||
final Track.Id trackIdOne;
|
||||
{ // first track
|
||||
trackPointCreator.setClock("2020-02-02T02:02:02Z");
|
||||
trackIdOne = service.startNewTrack();
|
||||
|
||||
Position position = ExportImportTest.sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3.1234567, 14.0014567, 10, 13, 15, 1020.25, 1f);
|
||||
contentProviderUtils.insertMarker(
|
||||
new Marker(
|
||||
null,
|
||||
trackIdOne,
|
||||
"Marker 1",
|
||||
"Marker 1 desc",
|
||||
"Marker 1 typeLocalized",
|
||||
position,
|
||||
null
|
||||
));
|
||||
service.endCurrentTrack();
|
||||
}
|
||||
|
||||
final Track.Id trackIdTwo;
|
||||
{ // second track
|
||||
trackPointCreator.setClock("2022-02-02T02:02:02Z");
|
||||
trackIdTwo = service.startNewTrack();
|
||||
|
||||
Position position = ExportImportTest.sendLocation(trackPointCreator, "2022-02-02T02:02:03Z", 3.1234567, 14.0014567, 10, 13, 15, 1020.25, 1f);
|
||||
contentProviderUtils.insertMarker(
|
||||
new Marker(
|
||||
null,
|
||||
trackIdTwo,
|
||||
"Marker 2",
|
||||
"Marker 2 desc",
|
||||
"Marker 2 typeLocalized",
|
||||
position,
|
||||
null
|
||||
));
|
||||
service.endCurrentTrack();
|
||||
}
|
||||
|
||||
// when
|
||||
TrackExporter exporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, new ContentProviderUtils(context));
|
||||
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
exporter.writeTrack(
|
||||
List.of(contentProviderUtils.getTrack(trackIdOne), contentProviderUtils.getTrack(trackIdTwo)),
|
||||
outputStream
|
||||
);
|
||||
|
||||
// then
|
||||
try (InputStream inputStreamExpected = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.kml23_twotracks)) {
|
||||
String expectation = new String(inputStreamExpected.readAllBytes(), StandardCharsets.UTF_8);
|
||||
String expectationAdjusted = expectation
|
||||
.replace("ece162fb-d614-4431-9d33-eb20f8ee8e1d", contentProviderUtils.getTrack(trackIdOne).uuid().toString())
|
||||
.replace("6712f4ca-3456-4739-841a-9e62760cc447", contentProviderUtils.getTrack(trackIdTwo).uuid().toString());
|
||||
assertEquals(expectationAdjusted, outputStream.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
+288
-142
@@ -26,7 +26,6 @@ import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
@@ -61,22 +60,24 @@ import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Temperature;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackBuilder;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
||||
import de.dennisguse.opentracks.sensors.BluetoothHandlerManagerCyclingPower;
|
||||
import de.dennisguse.opentracks.sensors.SensorManager;
|
||||
import de.dennisguse.opentracks.sensors.driver.CyclingPowerBluetooth;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.Aggregator;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingCadence;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingDistanceSpeed;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingPower;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.AggregatorHeartRate;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.Raw;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.AggregatorTemperature;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* Export a track to {@link TrackFileFormat} and verify that the import is identical.
|
||||
@@ -93,17 +94,19 @@ public class ExportImportTest {
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||
|
||||
//For csv_export_only() as we the timezone is hardcoded in the expectation.
|
||||
//For csv_export_only() as the timezone is hardcoded in the expectation.
|
||||
@Rule
|
||||
public TimezoneRule timezoneRule = new TimezoneRule(TimeZone.getTimeZone("Europe/Berlin"));
|
||||
|
||||
private static final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@BeforeClass
|
||||
public static void preSetUp() {
|
||||
// Prepare looper for Android's message queue
|
||||
if (Looper.myLooper() == null) Looper.prepare();
|
||||
}
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
PreferencesUtils.resetPreferences(context, true);
|
||||
}
|
||||
|
||||
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
@@ -123,11 +126,14 @@ public class ExportImportTest {
|
||||
private TrackImporter trackImporter;
|
||||
|
||||
@Before
|
||||
public void fileSetup() throws IOException {
|
||||
public void fileSetup() throws IOException, TimeoutException {
|
||||
//TODO Replace with a ByteArrayOutputStream instead of storing a file?
|
||||
tmpFile = File.createTempFile("test", "test", context.getFilesDir());
|
||||
tmpFileUri = Uri.fromFile(tmpFile);
|
||||
|
||||
trackImporter = new TrackImporter(context, contentProviderUtils, Distance.of(200), true);
|
||||
|
||||
setUp();
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -150,7 +156,7 @@ public class ExportImportTest {
|
||||
|
||||
Distance sensorDistance = Distance.of(10); // recording distance interval
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3.1234567, 14.0014567, 10, 13, 15, 1020.25, 1f);
|
||||
Position position = sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3.1234567, 14.0014567, 10, 13, 15, 1020.25, 1f);
|
||||
contentProviderUtils.insertMarker(
|
||||
new Marker(
|
||||
null,
|
||||
@@ -158,22 +164,22 @@ public class ExportImportTest {
|
||||
"Marker 1",
|
||||
"Marker 1 desc",
|
||||
"Marker 1 typeLocalized",
|
||||
service.getLastStoredTrackPointWithLocation().getPosition(),
|
||||
position,
|
||||
null
|
||||
));
|
||||
|
||||
// A sensor-only TrackPoint
|
||||
trackPointCreator.setClock("2020-02-02T02:02:04Z");
|
||||
mockSensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f, 1f);
|
||||
mockSensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f, 1f, 27);
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:14Z");
|
||||
mockSensorData(trackPointCreator, 15f, null, 67f, 3f, 50f, null);
|
||||
trackPointCreator.setClock("2020-02-02T02:02:14Z"); //ignored
|
||||
mockSensorData(trackPointCreator, 15f, null, 67f, 3f, 50f, null, 28);
|
||||
trackPointCreator.setClock("2020-02-02T02:02:15Z");
|
||||
mockSensorData(trackPointCreator, null, null, 68f, 3f, 50f, null);
|
||||
mockSensorData(trackPointCreator, null, null, 68f, 3f, 50f, null, 29);
|
||||
trackPointCreator.setClock("2020-02-02T02:02:16Z");
|
||||
mockSensorData(trackPointCreator, 5f, Distance.of(2), 69f, 3f, 50f, null); // Distance will be added to next TrackPoint
|
||||
mockSensorData(trackPointCreator, 5f, Distance.of(2), 69f, 3f, 50f, null, 30); //Distance will be added to next TrackPoint
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3.1234567, 14.0014567, 10, 13, 15, 1020.25, 0f);
|
||||
position = sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3.1234567, 14.0014567, 10, 13, 15, 1020.25, 0f);
|
||||
contentProviderUtils.insertMarker(
|
||||
new Marker(
|
||||
null,
|
||||
@@ -181,12 +187,11 @@ public class ExportImportTest {
|
||||
"Marker 2",
|
||||
"Marker 2 desc",
|
||||
"Marker 2 typeLocalized",
|
||||
service.getLastStoredTrackPointWithLocation().getPosition(),
|
||||
position,
|
||||
null
|
||||
));
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:18Z");
|
||||
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet(trackPointCreator);
|
||||
service.endCurrentTrack();
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:03:20Z");
|
||||
@@ -201,7 +206,6 @@ public class ExportImportTest {
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3.1234567, 16.001, 10, 27, 15, 999.123, 0f);
|
||||
|
||||
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet(trackPointCreator);
|
||||
trackPointCreator.setClock("2020-02-02T02:04:00Z");
|
||||
service.endCurrentTrack();
|
||||
|
||||
@@ -218,13 +222,158 @@ public class ExportImportTest {
|
||||
@LargeTest
|
||||
@Test
|
||||
public void track() throws TimeoutException {
|
||||
setUp();
|
||||
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
Statistics trackStatistics = track.statistics();
|
||||
|
||||
assertEquals(ZoneOffset.of("+01:00"), track.zoneOffset());
|
||||
|
||||
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse("2020-02-02T02:02:02Z")),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:03Z"),
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.25), null,
|
||||
null,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(1, 1)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:04Z"),
|
||||
null, null, null,
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)),
|
||||
Distance.of(10),
|
||||
HeartRate.of(66),
|
||||
Temperature.of(27),
|
||||
Cadence.of(3),
|
||||
Power.of(50),
|
||||
new AltitudeGainLoss(1, 1)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
Position.of(Instant.parse("2020-02-02T02:02:15Z")),
|
||||
null,
|
||||
HeartRate.of(68),
|
||||
Temperature.of(29),
|
||||
Cadence.of(3),
|
||||
Power.of(50),
|
||||
null
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:17Z"),
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.25), null,
|
||||
null,
|
||||
Speed.of(5)),
|
||||
Distance.of(2),
|
||||
HeartRate.of(69),
|
||||
Temperature.of(30),
|
||||
Cadence.of(3),
|
||||
Power.of(50),
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:18Z"),
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.25), null,
|
||||
null,
|
||||
Speed.of(5)),
|
||||
Distance.of(0),
|
||||
HeartRate.of(69),
|
||||
null,
|
||||
Cadence.of(3),
|
||||
Power.of(50),
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse("2020-02-02T02:03:20Z")),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:21Z"),
|
||||
3.123456, 14.002456, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
null,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(Instant.parse("2020-02-02T02:03:22Z"),
|
||||
3.123456, 16d, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
null,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.IDLE,
|
||||
Position.of(Instant.parse("2020-02-02T02:03:30Z")),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:50Z"),
|
||||
3.123456, 16.001, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
null, Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
Position.of(Instant.parse("2020-02-02T02:04:00Z")),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0))
|
||||
), actual);
|
||||
|
||||
assertEquals(new Statistics(
|
||||
Instant.parse("2020-02-02T02:02:02Z"),
|
||||
Instant.parse("2020-02-02T02:04:00Z"),
|
||||
@@ -238,77 +387,12 @@ public class ExportImportTest {
|
||||
null
|
||||
),
|
||||
trackStatistics);
|
||||
|
||||
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse("2020-02-02T02:02:02Z")),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:03Z"),
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.25), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(1, 1),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
Instant.parse("2020-02-02T02:02:04Z"))
|
||||
.setSensorDistance(Distance.of(10))
|
||||
.setSpeed(Speed.of(15))
|
||||
.setHeartRate(HeartRate.of(66))
|
||||
.setCadence(3)
|
||||
.setPower(50).setAltitudeGainLoss(1, 1),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:15Z"))
|
||||
.setHeartRate(HeartRate.of(68))
|
||||
.setCadence(3)
|
||||
.setPower(50),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:17Z"),
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.25), null,
|
||||
null,
|
||||
Speed.of(5)))
|
||||
.setSensorDistance(Distance.of(2))
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
.setHeartRate(HeartRate.of(69))
|
||||
.setCadence(3)
|
||||
.setPower(50),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:02:18Z")),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse("2020-02-02T02:03:20Z")),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:21Z"),
|
||||
3.123456, 14.002456, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(Instant.parse("2020-02-02T02:03:22Z"),
|
||||
3.123456, 16d, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.IDLE, Instant.parse("2020-02-02T02:03:30Z"))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:50Z"),
|
||||
3.123456, 16.001, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
null, Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:04:00Z"))
|
||||
), actual);
|
||||
}
|
||||
|
||||
//TODO Does not test marker images
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kmz_with_trackdetail_and_sensordata() throws TimeoutException, IOException {
|
||||
setUp();
|
||||
|
||||
public void kmz_with_trackdetail_and_sensordata() throws IOException {
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
@@ -360,9 +444,7 @@ public class ExportImportTest {
|
||||
|
||||
@LargeTest
|
||||
@Test(expected = ImportAlreadyExistsException.class)
|
||||
public void kml_with_trackdetail_and_sensordata_duplicate_trackUUID() throws TimeoutException, IOException {
|
||||
setUp();
|
||||
|
||||
public void kml_with_trackdetail_and_sensordata_duplicate_trackUUID() throws IOException {
|
||||
// given
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||
editor.putBoolean(context.getString(R.string.import_prevent_reimport_key), true);
|
||||
@@ -387,8 +469,6 @@ public class ExportImportTest {
|
||||
@LargeTest
|
||||
@Test
|
||||
public void gpx() throws TimeoutException, IOException {
|
||||
setUp();
|
||||
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
@@ -421,51 +501,86 @@ public class ExportImportTest {
|
||||
.setDelta(0.05); // speed is not fully
|
||||
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:03Z"),
|
||||
3.123456, 14.001456d, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.2), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(1, 1),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(1, 1)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:17Z"),
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.2), null,
|
||||
null,
|
||||
Speed.of(5)))
|
||||
.setAltitudeGainLoss(1, 1)
|
||||
.setSensorDistance(Distance.of(12))
|
||||
.setHeartRate(69)
|
||||
.setPower(50f)
|
||||
.setCadence(3f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
Speed.of(5)),
|
||||
Distance.of(12),
|
||||
HeartRate.of(69),
|
||||
Temperature.of(30),
|
||||
Cadence.of(3),
|
||||
Power.of(50),
|
||||
new AltitudeGainLoss(1, 1)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:21Z"),
|
||||
3.123456, 14.002456, Distance.of(10),
|
||||
Altitude.WGS84.of(999.0999755859375), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:22Z"),
|
||||
3.123456, 16d, Distance.of(10),
|
||||
Altitude.WGS84.of(999.0999755859375), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:50Z"),
|
||||
3.123456, 16.001, Distance.of(10),
|
||||
Altitude.WGS84.of(999.0999755859375), null,
|
||||
null,
|
||||
Speed.of(10)))
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
.setSpeed(Speed.of(15))
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
)
|
||||
), actual);
|
||||
|
||||
// 3. trackstatistics
|
||||
@@ -492,9 +607,7 @@ public class ExportImportTest {
|
||||
|
||||
@LargeTest
|
||||
@Test(expected = ImportAlreadyExistsException.class)
|
||||
public void gpx_duplicate_trackUUID() throws TimeoutException, IOException {
|
||||
setUp();
|
||||
|
||||
public void gpx_duplicate_trackUUID() throws IOException {
|
||||
// given
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||
editor.putBoolean(context.getString(R.string.import_prevent_reimport_key), true);
|
||||
@@ -520,9 +633,7 @@ public class ExportImportTest {
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void csv_export_only() throws TimeoutException, IOException {
|
||||
setUp();
|
||||
|
||||
public void csv_export_only() throws IOException {
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
@@ -566,17 +677,17 @@ public class ExportImportTest {
|
||||
}
|
||||
}
|
||||
|
||||
private void mockSensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power, Float altitudeGain) {
|
||||
SensorDataSet sensorDataSet = trackPointCreator.getSensorManager().sensorDataSet;
|
||||
private static void mockSensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power, Float altitudeGain, float temperature) {
|
||||
SensorManager sensorManager = trackPointCreator.getSensorManager();
|
||||
|
||||
AggregatorCyclingPower cyclingPower = new AggregatorCyclingPower("", "");
|
||||
cyclingPower.add(new Raw<>(trackPointCreator.createNow(), new BluetoothHandlerManagerCyclingPower.Data(Power.of(power), null)));
|
||||
sensorDataSet.add(cyclingPower);
|
||||
cyclingPower.add(trackPointCreator.getNow(), new CyclingPowerBluetooth.Data(Power.of(power), null));
|
||||
sensorManager.setAggregator(cyclingPower);
|
||||
|
||||
|
||||
AggregatorHeartRate avgHeartRate = new AggregatorHeartRate("", "");
|
||||
avgHeartRate.add(new Raw<>(trackPointCreator.createNow(), HeartRate.of(heartRate)));
|
||||
sensorDataSet.add(avgHeartRate);
|
||||
avgHeartRate.add(trackPointCreator.getNow(), HeartRate.of(heartRate));
|
||||
sensorManager.setAggregator(avgHeartRate);
|
||||
|
||||
AggregatorCyclingCadence cyclingCadence = new AggregatorCyclingCadence("", "") {
|
||||
@NonNull
|
||||
@@ -590,36 +701,69 @@ public class ExportImportTest {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
sensorDataSet.add(cyclingCadence);
|
||||
sensorManager.setAggregator(cyclingCadence);
|
||||
|
||||
if (distance != null && speed != null) {
|
||||
AggregatorCyclingDistanceSpeed distanceSpeed = Mockito.mock(AggregatorCyclingDistanceSpeed.class);
|
||||
Mockito.when(distanceSpeed.hasReceivedData()).thenReturn(true);
|
||||
Mockito.when(distanceSpeed.getAggregatedValue(Mockito.any())).thenReturn(new AggregatorCyclingDistanceSpeed.Data(null, distance, Speed.of(speed)));
|
||||
sensorDataSet.add(distanceSpeed);
|
||||
AggregatorCyclingDistanceSpeed aggregatorCyclingDistanceSpeed = new AggregatorCyclingDistanceSpeed("", "") {
|
||||
@NonNull
|
||||
@Override
|
||||
public Data getAggregatedValue(Instant now) {
|
||||
return new AggregatorCyclingDistanceSpeed.Data(null, distance, Speed.of(speed));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReceivedData() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
sensorManager.setAggregator(aggregatorCyclingDistanceSpeed);
|
||||
} else {
|
||||
sensorDataSet.add(new AggregatorCyclingDistanceSpeed("", ""));
|
||||
sensorManager.setAggregator(new AggregatorCyclingDistanceSpeed("", ""));
|
||||
}
|
||||
|
||||
mockAltitudeChange(trackPointCreator, altitudeGain);
|
||||
|
||||
trackPointCreator.onChange(sensorDataSet);
|
||||
sensorManager.setAggregator(new AggregatorTemperature("", "'") {
|
||||
@NonNull
|
||||
@Override
|
||||
public Temperature getAggregatedValue(Instant now) {
|
||||
return Temperature.of(temperature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReceivedData() {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
trackPointCreator.onChange();
|
||||
}
|
||||
|
||||
private void mockAltitudeChange(TrackPointCreator trackPointCreator, Float altitudeGain) {
|
||||
SensorDataSet sensorDataSet = trackPointCreator.getSensorManager().sensorDataSet;
|
||||
private static void mockAltitudeChange(TrackPointCreator trackPointCreator, Float altitudeGain) {
|
||||
SensorManager sensorManager = trackPointCreator.getSensorManager();
|
||||
|
||||
if (altitudeGain != null) {
|
||||
AggregatorBarometer barometer = Mockito.mock(AggregatorBarometer.class);
|
||||
Mockito.when(barometer.hasReceivedData()).thenReturn(true);
|
||||
Mockito.when(barometer.getAggregatedValue(Mockito.any())).thenReturn(new AltitudeGainLoss(altitudeGain, altitudeGain));
|
||||
sensorDataSet.add(barometer);
|
||||
} else {
|
||||
sensorDataSet.add(new AggregatorBarometer("test", null));
|
||||
if (altitudeGain == null) {
|
||||
sensorManager.getAltitudeChangeHandler().setAggregator(new AggregatorBarometer("test", null));
|
||||
return;
|
||||
}
|
||||
|
||||
AggregatorBarometer aggregatorBarometer = new AggregatorBarometer("", "") {
|
||||
@NonNull
|
||||
@Override
|
||||
public AltitudeGainLoss getAggregatedValue(Instant now) {
|
||||
return new AltitudeGainLoss(altitudeGain, altitudeGain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReceivedData() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
sensorManager.getAltitudeChangeHandler().setAggregator(aggregatorBarometer);
|
||||
}
|
||||
|
||||
private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, double altitude, Float altitudeGain) {
|
||||
//TODO Use return value (for markers)
|
||||
public static Position sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, double altitude, Float altitudeGain) {
|
||||
Location location = new Location("mock");
|
||||
location.setLatitude(latitude);
|
||||
location.setLongitude(longitude);
|
||||
@@ -631,6 +775,8 @@ public class ExportImportTest {
|
||||
mockAltitudeChange(trackPointCreator, altitudeGain);
|
||||
|
||||
trackPointCreator.setClock(time);
|
||||
trackPointCreator.getSensorManager().getGpsManager().onLocationChanged(location);
|
||||
trackPointCreator.getSensorManager().getGpsHandler().onDataReceived(location);
|
||||
|
||||
return Position.of(location, Instant.parse(time));
|
||||
}
|
||||
}
|
||||
+239
-22
@@ -27,8 +27,12 @@ import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
@@ -116,6 +120,184 @@ public class GPXTrackImporterTest {
|
||||
), importedTrackPoints);
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void gpx_missing_time() throws IOException {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new GPXTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx11_missing_time);
|
||||
|
||||
// when
|
||||
// 1. import
|
||||
importTrackId = importer.importFile(inputStream).get(0);
|
||||
|
||||
// then
|
||||
// 2. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("", importedTrack.activityTypeLocalized());
|
||||
assertEquals("", importedTrack.description());
|
||||
assertEquals("20210907_213924.gpx", importedTrack.name());
|
||||
assertEquals(ActivityType.UNKNOWN, importedTrack.activityType());
|
||||
|
||||
// 3. trackstatistics
|
||||
Statistics trackStatistics = importedTrack.statistics();
|
||||
assertEquals(0, trackStatistics.maxSpeed().toMPS(), 0.01);
|
||||
assertEquals(Duration.ofSeconds(0), trackStatistics.movingDuration());
|
||||
|
||||
// 4. trackpoints
|
||||
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
assertEquals(1, importedTrackPoints.size());
|
||||
|
||||
TrackPointAssert a = new TrackPointAssert();
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2021-09-07T22:12:00Z"),
|
||||
30.14185982, -40.3863038, null,
|
||||
Altitude.WGS84.of(-5), null,
|
||||
null,
|
||||
null))
|
||||
), importedTrackPoints);
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void gpx_with_power_pwd() throws IOException {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new GPXTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx_with_power_pwr);
|
||||
|
||||
// when
|
||||
// 1. import
|
||||
importTrackId = importer.importFile(inputStream).get(0);
|
||||
|
||||
// then
|
||||
// 2. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("", importedTrack.activityTypeLocalized());
|
||||
assertEquals("", importedTrack.description());
|
||||
assertEquals("gpx with power", importedTrack.name());
|
||||
assertEquals(ActivityType.UNKNOWN, importedTrack.activityType());
|
||||
|
||||
// 3. trackpoints
|
||||
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
assertEquals(3, importedTrackPoints.size());
|
||||
|
||||
TrackPointAssert a = new TrackPointAssert();
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(null, TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2021-09-07T22:10:19Z"),
|
||||
30.14185982, -40.3863038, null,
|
||||
Altitude.WGS84.of(-5), null,
|
||||
null,
|
||||
null),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new Power(100f),
|
||||
null
|
||||
),
|
||||
new TrackPoint(null, TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2021-09-07T22:11:07Z"),
|
||||
30.14184657, -40.38670089, null,
|
||||
Altitude.WGS84.of(-5), null,
|
||||
null,
|
||||
Speed.of(0.7976524233818054)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new Power(110f),
|
||||
null),
|
||||
new TrackPoint(null, TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2021-09-07T22:12:00Z"),
|
||||
30.14185982, -40.3863038, null,
|
||||
Altitude.WGS84.of(-5), null,
|
||||
null,
|
||||
Speed.of(0.7224021553993225)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new Power(150f),
|
||||
null)
|
||||
), importedTrackPoints);
|
||||
}
|
||||
@LargeTest
|
||||
@Test
|
||||
public void gpx_with_power_gpxpx() throws IOException {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new GPXTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx_with_power_gpxpx);
|
||||
|
||||
// when
|
||||
// 1. import
|
||||
importTrackId = importer.importFile(inputStream).get(0);
|
||||
|
||||
// then
|
||||
// 2. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("", importedTrack.activityTypeLocalized());
|
||||
assertEquals("", importedTrack.description());
|
||||
assertEquals("gpx with power", importedTrack.name());
|
||||
assertEquals(ActivityType.UNKNOWN, importedTrack.activityType());
|
||||
|
||||
// 3. trackpoints
|
||||
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
assertEquals(3, importedTrackPoints.size());
|
||||
|
||||
TrackPointAssert a = new TrackPointAssert();
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(null, TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2021-09-07T22:10:19Z"),
|
||||
30.14185982, -40.3863038, null,
|
||||
Altitude.WGS84.of(-5), null,
|
||||
null,
|
||||
null),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new Power(100f),
|
||||
null
|
||||
),
|
||||
new TrackPoint(null, TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2021-09-07T22:11:07Z"),
|
||||
30.14184657, -40.38670089, null,
|
||||
Altitude.WGS84.of(-5), null,
|
||||
null,
|
||||
Speed.of(0.7976524233818054)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new Power(110f),
|
||||
null),
|
||||
new TrackPoint(null, TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2021-09-07T22:12:00Z"),
|
||||
30.14185982, -40.3863038, null,
|
||||
Altitude.WGS84.of(-5), null,
|
||||
null,
|
||||
Speed.of(0.7224021553993225)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new Power(150f),
|
||||
null)
|
||||
), importedTrackPoints);
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void gpx_speed_no_namespace() throws IOException {
|
||||
@@ -193,51 +375,86 @@ public class GPXTrackImporterTest {
|
||||
TrackPointAssert a = new TrackPointAssert()
|
||||
.setDelta(0.05); // speed is not fully
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:03Z"),
|
||||
3d, 14d, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(1, 1),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(1, 1)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:17Z"),
|
||||
3d, 14.001, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(5)))
|
||||
.setAltitudeGainLoss(1, 1)
|
||||
.setSensorDistance(Distance.of(12))
|
||||
.setHeartRate(69)
|
||||
.setPower(50f)
|
||||
.setCadence(3f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
Speed.of(5)),
|
||||
Distance.of(12),
|
||||
HeartRate.of(69),
|
||||
null,
|
||||
Cadence.of(3),
|
||||
Power.of(50),
|
||||
new AltitudeGainLoss(1, 1)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:21Z"),
|
||||
3d, 14.002, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:22Z"),
|
||||
3d, 16d, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:50Z"),
|
||||
3d, 16.001, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(10)))
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
.setSpeed(Speed.of(15))
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
)
|
||||
), actual);
|
||||
}
|
||||
|
||||
@@ -246,8 +463,8 @@ public class GPXTrackImporterTest {
|
||||
public void importExportTest_timezone() throws IOException {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new GPXTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx_timezone);
|
||||
try(InputStream inputStreamExpected = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx_timezone)) {
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx11_timezone);
|
||||
try (InputStream inputStreamExpected = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx11_timezone)) {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
// when
|
||||
|
||||
+28
-37
@@ -16,57 +16,48 @@ public class TrackPointAssert {
|
||||
public void assertEquals(TrackPoint expected, TrackPoint actual) {
|
||||
Assert.assertEquals("time", expected.getTime(), actual.getTime());
|
||||
|
||||
Assert.assertEquals("type", expected.getType(), actual.getType());
|
||||
Assert.assertEquals("type", expected.type(), actual.type());
|
||||
|
||||
Assert.assertEquals("has location,", expected.hasLocation(), actual.hasLocation());
|
||||
if (expected.hasLocation()) {
|
||||
Assert.assertEquals("latitude", expected.getPosition().latitude(), actual.getPosition().latitude(), 0.001);
|
||||
Assert.assertEquals("longitude", expected.getPosition().longitude(), actual.getPosition().longitude(), 0.001);
|
||||
Assert.assertEquals("has location,", expected.position().hasLocation(), actual.position().hasLocation());
|
||||
if (expected.position().hasLocation()) {
|
||||
Assert.assertEquals("latitude", expected.position().latitude(), actual.position().latitude(), 0.001);
|
||||
Assert.assertEquals("longitude", expected.position().longitude(), actual.position().longitude(), 0.001);
|
||||
}
|
||||
|
||||
Assert.assertEquals("has altitude", expected.hasAltitude(), actual.hasAltitude());
|
||||
if (expected.hasAltitude()) {
|
||||
Assert.assertEquals("altitude", expected.getAltitude().toM(), actual.getAltitude().toM(), delta);
|
||||
Assert.assertEquals("has altitude", expected.position().hasAltitude(), actual.position().hasAltitude());
|
||||
if (expected.position().hasAltitude()) {
|
||||
Assert.assertEquals("altitude", expected.position().altitude().getClass(), actual.position().altitude().getClass());
|
||||
Assert.assertEquals("altitude", expected.position().altitude().toM(), actual.position().altitude().toM(), delta);
|
||||
}
|
||||
|
||||
Assert.assertEquals("has altitudeGainLoss", expected.hasAltitudeGainLoss(), actual.hasAltitudeGainLoss());
|
||||
if (expected.hasAltitudeGainLoss()) {
|
||||
Assert.assertEquals("altitudeGainLoss", expected.getAltitudeGainLoss(), actual.getAltitudeGainLoss());
|
||||
Assert.assertEquals("altitudeGainLoss", expected.altitudeGainLoss(), actual.altitudeGainLoss());
|
||||
|
||||
Assert.assertEquals("has speed", expected.position().hasSpeed(), actual.position().hasSpeed());
|
||||
if (expected.position().hasSpeed()) {
|
||||
Assert.assertEquals("speed", expected.position().speed().toMPS(), actual.position().speed().toMPS(), delta);
|
||||
}
|
||||
|
||||
Assert.assertEquals("has speed", expected.hasSpeed(), actual.hasSpeed());
|
||||
if (expected.hasSpeed()) {
|
||||
Assert.assertEquals("speed", expected.getSpeed().toMPS(), actual.getSpeed().toMPS(), delta);
|
||||
Assert.assertEquals("has horizontalAccuracy", expected.position().hasHorizontalAccuracy(), actual.position().hasHorizontalAccuracy());
|
||||
if (expected.position().hasHorizontalAccuracy()) {
|
||||
Assert.assertEquals("horizontalAccuracy", expected.position().horizontalAccuracy().toM(), actual.position().horizontalAccuracy().toM(), delta);
|
||||
}
|
||||
Assert.assertEquals("has verticalAccuracy", expected.position().hasVerticalAccuracy(), actual.position().hasVerticalAccuracy());
|
||||
if (expected.position().hasVerticalAccuracy()) {
|
||||
Assert.assertEquals("verticalAccuracy", expected.position().verticalAccuracy().toM(), actual.position().verticalAccuracy().toM(), delta);
|
||||
}
|
||||
|
||||
Assert.assertEquals("has horizontalAccuracy", expected.hasHorizontalAccuracy(), actual.hasHorizontalAccuracy());
|
||||
if (expected.hasHorizontalAccuracy()) {
|
||||
Assert.assertEquals("horizontalAccuracy", expected.getHorizontalAccuracy().toM(), actual.getHorizontalAccuracy().toM(), delta);
|
||||
}
|
||||
Assert.assertEquals("has verticalAccuracy", expected.hasVerticalAccuracy(), actual.hasVerticalAccuracy());
|
||||
if (expected.hasVerticalAccuracy()) {
|
||||
Assert.assertEquals("verticalAccuracy", expected.getVerticalAccuracy().toM(), actual.getVerticalAccuracy().toM(), delta);
|
||||
Assert.assertEquals("has sensorDistance", expected.sensorDistance() != null, actual.sensorDistance() != null);
|
||||
if (expected.sensorDistance() != null) {
|
||||
Assert.assertEquals("sensorDistance", expected.sensorDistance().toM(), actual.sensorDistance().toM(), delta);
|
||||
}
|
||||
|
||||
Assert.assertEquals("has sensorDistance", expected.hasSensorDistance(), actual.hasSensorDistance());
|
||||
if (expected.hasSensorDistance()) {
|
||||
Assert.assertEquals("sensorDistance", expected.getSensorDistance().toM(), actual.getSensorDistance().toM(), delta);
|
||||
}
|
||||
Assert.assertEquals("heartRate", expected.heartRate(), actual.heartRate());
|
||||
|
||||
Assert.assertEquals("has heartrate", expected.hasHeartRate(), actual.hasHeartRate());
|
||||
if (expected.hasHeartRate()) {
|
||||
Assert.assertEquals("heartrate", expected.getHeartRate(), actual.getHeartRate());
|
||||
}
|
||||
Assert.assertEquals("temperature", expected.temperature(), actual.temperature());
|
||||
|
||||
Assert.assertEquals("has power", expected.hasPower(), actual.hasPower());
|
||||
if (expected.hasPower()) {
|
||||
Assert.assertEquals("power", expected.getPower(), actual.getPower());
|
||||
}
|
||||
Assert.assertEquals("power", expected.power(), actual.power());
|
||||
|
||||
Assert.assertEquals("has cadence", expected.hasCadence(), actual.hasCadence());
|
||||
if (expected.hasCadence()) {
|
||||
Assert.assertEquals("cadence", expected.getCadence(), actual.getCadence());
|
||||
}
|
||||
Assert.assertEquals("cadence", expected.cadence(), actual.cadence());
|
||||
}
|
||||
|
||||
public void assertEquals(List<TrackPoint> expected, List<TrackPoint> actual) {
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
package de.dennisguse.opentracks.publicapi;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.data.CustomContentProvider;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DataProviderTest {
|
||||
|
||||
private final ContentResolver resolver = ApplicationProvider.getApplicationContext().getContentResolver();
|
||||
|
||||
@Test
|
||||
public void supportsVersion2_tracks() {
|
||||
// given
|
||||
Uri uri = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/dashboard/tracks/1");
|
||||
String[] expectedTracksColumns = new String[]{
|
||||
"_id",
|
||||
"name",
|
||||
"description",
|
||||
"category",
|
||||
"activity_type_localized",
|
||||
"starttime",
|
||||
"stoptime",
|
||||
"totaltime",
|
||||
"movingtime",
|
||||
"totaldistance",
|
||||
"maxspeed",
|
||||
"avgspeed",
|
||||
"avgmovingspeed",
|
||||
"minelevation",
|
||||
"maxelevation",
|
||||
"elevationgain",
|
||||
"elevationloss"
|
||||
};
|
||||
|
||||
// when
|
||||
try (Cursor cursor = resolver.query(
|
||||
uri,
|
||||
expectedTracksColumns,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)) {
|
||||
// then
|
||||
assertEquals("vnd.android.cursor.dir/vnd.de.dennisguse.track", resolver.getType(uri));
|
||||
|
||||
assertNotNull(cursor);
|
||||
assertArrayEquals(expectedTracksColumns, cursor.getColumnNames());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsVersion2_trackpoints() {
|
||||
// given
|
||||
Uri uri = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/dashboard/trackpoints/1");
|
||||
String[] expectedTrackpointsColumns = {
|
||||
"_id",
|
||||
"trackid",
|
||||
"latitude",
|
||||
"longitude",
|
||||
"time",
|
||||
"type",
|
||||
"speed"
|
||||
};
|
||||
|
||||
// when
|
||||
try (Cursor cursor = resolver.query(
|
||||
uri,
|
||||
expectedTrackpointsColumns,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)) {
|
||||
// then
|
||||
assertEquals("vnd.android.cursor.dir/vnd.de.dennisguse.trackpoint", resolver.getType(uri));
|
||||
|
||||
assertNotNull(cursor);
|
||||
assertArrayEquals(expectedTrackpointsColumns, cursor.getColumnNames());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsVersion2_markers() {
|
||||
// given
|
||||
Uri uri = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/dashboard/markers/1");
|
||||
String[] expectedMarkersColumns = {
|
||||
"_id",
|
||||
"name",
|
||||
"description",
|
||||
"category",
|
||||
"trackid",
|
||||
"longitude",
|
||||
"latitude",
|
||||
"photoUrl",
|
||||
"icon"
|
||||
};
|
||||
|
||||
// when
|
||||
try (Cursor cursor = resolver.query(
|
||||
uri,
|
||||
expectedMarkersColumns,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)) {
|
||||
// then
|
||||
assertEquals("vnd.android.cursor.dir/vnd.de.dennisguse.waypoint", resolver.getType(uri));
|
||||
|
||||
assertNotNull(cursor);
|
||||
assertArrayEquals(expectedMarkersColumns, cursor.getColumnNames());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsVersion3_tracks() {
|
||||
// given
|
||||
Uri uri = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/dashboard/tracks/1");
|
||||
String[] expectedTracksColumns = {
|
||||
"_id",
|
||||
"name",
|
||||
"description",
|
||||
"activity_type",
|
||||
"activity_type_localized",
|
||||
"time_start",
|
||||
"time_stop",
|
||||
"duration_total",
|
||||
"duration_moving",
|
||||
"distance",
|
||||
"speed_max",
|
||||
"altitude_min",
|
||||
"altitude_max",
|
||||
"altitude_gain",
|
||||
"altitude_loss"
|
||||
};
|
||||
|
||||
// when
|
||||
try (Cursor cursor = resolver.query(
|
||||
uri,
|
||||
expectedTracksColumns,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)) {
|
||||
// then
|
||||
assertEquals("vnd.android.cursor.dir/vnd.de.dennisguse.track", resolver.getType(uri));
|
||||
|
||||
assertNotNull(cursor);
|
||||
assertArrayEquals(expectedTracksColumns, cursor.getColumnNames());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsVersion3_trackpoints() {
|
||||
//given
|
||||
Uri uri = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/dashboard/trackpoints/1");
|
||||
String[] expectedTrackpointsColumns = {
|
||||
"_id",
|
||||
"trackid",
|
||||
"latitude",
|
||||
"longitude",
|
||||
"time",
|
||||
"type",
|
||||
"speed"
|
||||
};
|
||||
|
||||
// when
|
||||
try (Cursor cursor = resolver.query(
|
||||
uri,
|
||||
expectedTrackpointsColumns,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)) {
|
||||
//then
|
||||
assertEquals("vnd.android.cursor.dir/vnd.de.dennisguse.trackpoint", resolver.getType(uri));
|
||||
|
||||
assertNotNull(cursor);
|
||||
assertArrayEquals(expectedTrackpointsColumns, cursor.getColumnNames());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsVersion3_markers() {
|
||||
// given
|
||||
Uri uri = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/dashboard/markers/1");
|
||||
String[] expectedMarkersColumns = {
|
||||
"_id",
|
||||
"name",
|
||||
"description",
|
||||
"marker_type_localized",
|
||||
"trackid",
|
||||
"latitude",
|
||||
"longitude",
|
||||
"photoUrl"
|
||||
};
|
||||
|
||||
// when
|
||||
try (Cursor cursor = resolver.query(
|
||||
uri,
|
||||
expectedMarkersColumns,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)) {
|
||||
// then
|
||||
assertEquals("vnd.android.cursor.dir/vnd.de.dennisguse.waypoint", resolver.getType(uri));
|
||||
|
||||
assertNotNull(cursor);
|
||||
assertArrayEquals(expectedMarkersColumns, cursor.getColumnNames());
|
||||
}
|
||||
}
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
package de.dennisguse.opentracks.sensors;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class BluetoothHandlerCyclingPowerTest {
|
||||
|
||||
@Test
|
||||
public void parseCyclingPower_power() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerManagerCyclingPower.CYCLING_POWER.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0, 0, 40, 0});
|
||||
|
||||
// when
|
||||
BluetoothHandlerManagerCyclingPower.Data powerCadence = BluetoothHandlerManagerCyclingPower.parseCyclingPower(characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(40, powerCadence.power().getW(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCyclingPower_power_with_cadence() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerManagerCyclingPower.CYCLING_POWER.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x2C, 0x00, 0x00, 0x00, (byte) 0x9F, 0x00, 0x0C, 0x00, (byte) 0xE5, 0x42});
|
||||
|
||||
// when
|
||||
BluetoothHandlerManagerCyclingPower.Data powerCadence = BluetoothHandlerManagerCyclingPower.parseCyclingPower(characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(0, powerCadence.power().getW(), 0.01);
|
||||
|
||||
assertEquals(12, powerCadence.crank().crankRevolutionsCount());
|
||||
assertEquals(17125, powerCadence.crank().crankRevolutionsTime());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,7 +28,6 @@ import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class GpsStatusTest {
|
||||
@@ -82,16 +81,16 @@ public class GpsStatusTest {
|
||||
|
||||
// when / then
|
||||
subject.start();
|
||||
subject.onNewTrackPoint(new TrackPoint(TrackPoint.Type.TRACKPOINT, Position.of(badFix, Instant.now())));
|
||||
subject.onNewTrackPoint(Position.of(badFix, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_BAD), statusList);
|
||||
|
||||
subject.onNewTrackPoint(new TrackPoint(TrackPoint.Type.TRACKPOINT, Position.of(ok, Instant.now())));
|
||||
subject.onNewTrackPoint(Position.of(ok, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_BAD, GPS_SIGNAL_FIX), statusList);
|
||||
|
||||
subject.onNewTrackPoint(new TrackPoint(TrackPoint.Type.TRACKPOINT, Position.of(ok, Instant.now())));
|
||||
subject.onNewTrackPoint(Position.of(ok, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_BAD, GPS_SIGNAL_FIX), statusList);
|
||||
|
||||
subject.onNewTrackPoint(new TrackPoint(TrackPoint.Type.TRACKPOINT, Position.of(badFix, Instant.now())));
|
||||
subject.onNewTrackPoint(Position.of(badFix, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_BAD, GPS_SIGNAL_FIX, GPS_SIGNAL_BAD), statusList);
|
||||
}
|
||||
|
||||
@@ -106,13 +105,13 @@ public class GpsStatusTest {
|
||||
|
||||
// when / then
|
||||
subject.start();
|
||||
subject.onNewTrackPoint(new TrackPoint(TrackPoint.Type.TRACKPOINT, Position.of(ok, Instant.now().minusMillis(1000))));
|
||||
subject.onNewTrackPoint(Position.of(ok, Instant.now().minusMillis(1000)));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX), statusList);
|
||||
|
||||
subject.determineGpsStatusByTime(Instant.now());
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX, GPS_SIGNAL_LOST), statusList);
|
||||
|
||||
subject.onNewTrackPoint(new TrackPoint(TrackPoint.Type.TRACKPOINT, Position.of(ok, Instant.now())));
|
||||
subject.onNewTrackPoint(Position.of(ok, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX, GPS_SIGNAL_LOST, GPS_SIGNAL_FIX), statusList);
|
||||
}
|
||||
|
||||
@@ -133,13 +132,13 @@ public class GpsStatusTest {
|
||||
Thread.sleep(100);
|
||||
assertEquals(List.of(GPS_ENABLED), statusList);
|
||||
|
||||
subject.onNewTrackPoint(new TrackPoint(TrackPoint.Type.TRACKPOINT, Position.of(ok, Instant.now())));
|
||||
subject.onNewTrackPoint(Position.of(ok, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX), statusList);
|
||||
|
||||
Thread.sleep(100);
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX, GPS_SIGNAL_LOST), statusList);
|
||||
|
||||
subject.onNewTrackPoint(new TrackPoint(TrackPoint.Type.TRACKPOINT, Position.of(badFix, Instant.now())));
|
||||
subject.onNewTrackPoint(Position.of(badFix, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX, GPS_SIGNAL_LOST, GPS_SIGNAL_BAD), statusList);
|
||||
|
||||
Thread.sleep(100);
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
package de.dennisguse.opentracks.sensors;
|
||||
package de.dennisguse.opentracks.sensors.driver;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@@ -13,11 +13,11 @@ public class BluetoothHandlerBarometricPressureTest {
|
||||
@Test
|
||||
public void parseEnvironmentalSensing_Pa() {
|
||||
// given
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerBarometricPressure.BAROMETRIC_PRESSURE.serviceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BarometerBluetooth.BAROMETRIC_PRESSURE.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{(byte) 0xB2, (byte) 0x48, (byte) 0x0F, (byte) 0x00});
|
||||
|
||||
// when
|
||||
AtmosphericPressure pressure = BluetoothHandlerBarometricPressure.parseEnvironmentalSensing(characteristic);
|
||||
AtmosphericPressure pressure = BarometerBluetooth.parseEnvironmentalSensing(characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(AtmosphericPressure.ofHPA(1001.65f), pressure);
|
||||
+6
-6
@@ -1,4 +1,4 @@
|
||||
package de.dennisguse.opentracks.sensors;
|
||||
package de.dennisguse.opentracks.sensors.driver;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@@ -8,16 +8,16 @@ import org.junit.Test;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
|
||||
public class BluetoothHandlerManagerHeartRateTest {
|
||||
public class BluetoothHandlerHeartRateTest {
|
||||
|
||||
@Test
|
||||
public void parseHeartRate_uint8() {
|
||||
// given
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerManagerHeartRate.HEARTRATE.serviceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(HeartRateBluetooth.HEARTRATE.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x02, 0x3C});
|
||||
|
||||
// when
|
||||
HeartRate heartRate = BluetoothHandlerManagerHeartRate.parseHeartRate(characteristic);
|
||||
HeartRate heartRate = HeartRateBluetooth.parseHeartRate(characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(HeartRate.of(60), heartRate);
|
||||
@@ -26,11 +26,11 @@ public class BluetoothHandlerManagerHeartRateTest {
|
||||
@Test
|
||||
public void parseHeartRate_uint16() {
|
||||
// given
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerManagerHeartRate.HEARTRATE.serviceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(HeartRateBluetooth.HEARTRATE.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x01, 0x01, 0x01});
|
||||
|
||||
// when
|
||||
HeartRate heartRate = BluetoothHandlerManagerHeartRate.parseHeartRate(characteristic);
|
||||
HeartRate heartRate = HeartRateBluetooth.parseHeartRate(characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(HeartRate.of(257), heartRate);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user